diff --git a/example/src/App.tsx b/example/src/App.tsx index 7b25fbd4..096e0da8 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -3,6 +3,7 @@ import { DevSettings, View, Text, StatusBar } from 'react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; import AdditionalInsetsExample from './AdditionalInsetsExample'; import CornerAdaptationExample from './CornerAdaptationExample'; +import FormSheetExample from './FormSheetExample'; import HooksExample from './HooksExample'; import ListenerExample from './ListenerExample'; import SafeAreaViewExample from './SafeAreaViewExample'; @@ -20,6 +21,7 @@ type Example = | 'corner-adaptation' | 'react-navigation' | 'native-stack' + | 'form-sheet' | 'additional-insets'; export default function App() { @@ -70,6 +72,9 @@ export default function App() { DevSettings.addMenuItem('Show Native Stack Example', () => { setCurrentExample('native-stack'); }); + DevSettings.addMenuItem('Show Form Sheet Example', () => { + setCurrentExample('form-sheet'); + }); DevSettings.addMenuItem('Show Additional Insets Example', () => { setCurrentExample('additional-insets'); }); @@ -98,6 +103,9 @@ export default function App() { case 'native-stack': content = ; break; + case 'form-sheet': + content = ; + break; case 'additional-insets': content = ; break; diff --git a/example/src/FormSheetExample.tsx b/example/src/FormSheetExample.tsx new file mode 100644 index 00000000..f929983e --- /dev/null +++ b/example/src/FormSheetExample.tsx @@ -0,0 +1,161 @@ +import * as React from 'react'; +import { + Button, + ScrollView, + StyleSheet, + Switch, + Text, + View, +} from 'react-native'; +import { NavigationContainer, useNavigation } from '@react-navigation/native'; +import { + createNativeStackNavigator, + NativeStackNavigationProp, +} from '@react-navigation/native-stack'; +import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context'; + +// A SafeAreaView in a native formSheet has no SafeAreaProvider above it in the view hierarchy, so +// it is its own provider. Rotate the device with the sheet open: the red border must stay clear of +// the notch / Dynamic Island on both sides, also when "Busy JS thread" is on. The second sheet +// wraps the same content in a SafeAreaProvider, so its SafeAreaView follows the provider instead. + +type Routes = { + Home: undefined; + Sheet: undefined; + SheetWithProvider: undefined; +}; + +const Stack = createNativeStackNavigator(); + +// Blocks the JS thread for most of every second, like a busy app. With the default asynchronous +// state update the insets then lag behind a rotation by several frames. +function useBusyJsThread(enabled: boolean) { + React.useEffect(() => { + if (!enabled) { + return; + } + const id = setInterval(() => { + const end = Date.now() + 900; + while (Date.now() < end) { + // busy + } + }, 1000); + return () => clearInterval(id); + }, [enabled]); +} + +// Mounts and unmounts many SafeAreaViews, to check that the state update stays cheap while +// they mount. +function ManySafeAreaViews() { + const [count, setCount] = React.useState(0); + React.useEffect(() => { + const id = setInterval(() => setCount((c) => (c === 0 ? 200 : 0)), 500); + return () => clearInterval(id); + }, []); + return ( + + {Array.from({ length: count }, (_, i) => ( + + ))} + + ); +} + +function HomeScreen() { + const navigation = useNavigation>(); + return ( + +