Skip to content

Commit 191765f

Browse files
authored
Document formSheet props and limitiations (#1402)
1 parent b827231 commit 191765f

11 files changed

+335
-2
lines changed
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

versioned_docs/version-7.x/native-stack-navigator.md

+335-2
Original file line numberDiff line numberDiff line change
@@ -864,11 +864,344 @@ Supported values:
864864
<video playsInline autoPlay muted loop>
865865
<source src="/assets/7.x/native-stack/presentation-fullScreenModal.mp4" />
866866
</video>
867-
- `formSheet`: will use "UIModalPresentationFormSheet" modal style on iOS and will fallback to "modal" on Android.
867+
868+
- `formSheet`: will use "BottomSheetBehavior" on Android and "UIModalPresentationFormSheet" modal style on iOS.
869+
<video playsInline autoPlay muted loop>
870+
<source src="/assets/7.x/native-stack/presentation-formSheet-android.mp4" />
871+
</video>
868872
<video playsInline autoPlay muted loop>
869-
<source src="/assets/7.x/native-stack/presentation-formSheet.mp4" />
873+
<source src="/assets/7.x/native-stack/presentation-formSheet-ios.mp4" />
870874
</video>
871875

876+
##### Using Form Sheet
877+
878+
To use Form Sheet for your screen, add `presentation: 'formSheet'` to the `options`.
879+
880+
<Tabs groupId="config" queryString="config">
881+
<TabItem value="static" label="Static" default>
882+
883+
```js name="Form Sheet" snack
884+
import * as React from 'react';
885+
import { Text, View } from 'react-native';
886+
import {
887+
createStaticNavigation,
888+
useNavigation,
889+
} from '@react-navigation/native';
890+
import { Button } from '@react-navigation/elements';
891+
892+
// codeblock-focus-start
893+
import { createNativeStackNavigator } from '@react-navigation/native-stack';
894+
895+
// codeblock-focus-end
896+
897+
function HomeScreen() {
898+
const navigation = useNavigation();
899+
900+
return (
901+
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
902+
<Text>Home Screen</Text>
903+
<Button onPress={() => navigation.navigate('Profile')}>
904+
Go to Profile
905+
</Button>
906+
</View>
907+
);
908+
}
909+
910+
function ProfileScreen() {
911+
const navigation = useNavigation();
912+
913+
return (
914+
<View style={{ padding: 15 }}>
915+
<Text style={{ fontSize: 30, fontWeight: 'bold' }}>Profile Screen</Text>
916+
<Text style={{ marginTop: 10 }}>
917+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam accumsan
918+
euismod enim, quis porta ligula egestas sed. Maecenas vitae consequat
919+
odio, at dignissim lorem. Ut euismod eros ac mi ultricies, vel pharetra
920+
tortor commodo. Interdum et malesuada fames ac ante ipsum primis in
921+
faucibus. Nullam at urna in metus iaculis aliquam at sed quam. In
922+
ullamcorper, ex ut facilisis commodo, urna diam posuere urna, at
923+
condimentum mi orci ac ipsum. In hac habitasse platea dictumst. Donec
924+
congue pharetra ipsum in finibus. Nulla blandit finibus turpis, non
925+
vulputate elit viverra a. Curabitur in laoreet nisl.
926+
</Text>
927+
<Button onPress={() => navigation.goBack()} style={{ marginTop: 15 }}>
928+
Go back
929+
</Button>
930+
</View>
931+
);
932+
}
933+
934+
// codeblock-focus-start
935+
const MyStack = createNativeStackNavigator({
936+
screens: {
937+
Home: {
938+
screen: HomeScreen,
939+
},
940+
Profile: {
941+
screen: ProfileScreen,
942+
options: {
943+
presentation: 'formSheet',
944+
headerShown: false,
945+
sheetAllowedDetents: 'fitToContents',
946+
},
947+
},
948+
},
949+
});
950+
// codeblock-focus-end
951+
952+
const Navigation = createStaticNavigation(MyStack);
953+
954+
export default function App() {
955+
return <Navigation />;
956+
}
957+
```
958+
959+
</TabItem>
960+
<TabItem value="dynamic" label="Dynamic">
961+
962+
```js name="Form Sheet" snack
963+
import * as React from 'react';
964+
import { Text, View } from 'react-native';
965+
import { NavigationContainer, useNavigation } from '@react-navigation/native';
966+
import { Button } from '@react-navigation/elements';
967+
// codeblock-focus-start
968+
import { createNativeStackNavigator } from '@react-navigation/native-stack';
969+
970+
const Stack = createNativeStackNavigator();
971+
972+
function MyStack() {
973+
return (
974+
<Stack.Navigator>
975+
<Stack.Screen name="Home" component={HomeScreen} />
976+
<Stack.Screen
977+
name="Profile"
978+
component={ProfileScreen}
979+
options={{
980+
presentation: 'formSheet',
981+
headerShown: false,
982+
sheetAllowedDetents: 'fitToContents',
983+
}}
984+
/>
985+
</Stack.Navigator>
986+
);
987+
}
988+
// codeblock-focus-end
989+
990+
function HomeScreen() {
991+
const navigation = useNavigation();
992+
993+
return (
994+
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
995+
<Text>Home Screen</Text>
996+
<Button onPress={() => navigation.navigate('Profile')}>
997+
Go to Profile
998+
</Button>
999+
</View>
1000+
);
1001+
}
1002+
1003+
function ProfileScreen() {
1004+
const navigation = useNavigation();
1005+
1006+
return (
1007+
<View style={{ padding: 15 }}>
1008+
<Text style={{ fontSize: 30, fontWeight: 'bold' }}>Profile Screen</Text>
1009+
<Text style={{ marginTop: 10 }}>
1010+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam accumsan
1011+
euismod enim, quis porta ligula egestas sed. Maecenas vitae consequat
1012+
odio, at dignissim lorem. Ut euismod eros ac mi ultricies, vel pharetra
1013+
tortor commodo. Interdum et malesuada fames ac ante ipsum primis in
1014+
faucibus. Nullam at urna in metus iaculis aliquam at sed quam. In
1015+
ullamcorper, ex ut facilisis commodo, urna diam posuere urna, at
1016+
condimentum mi orci ac ipsum. In hac habitasse platea dictumst. Donec
1017+
congue pharetra ipsum in finibus. Nulla blandit finibus turpis, non
1018+
vulputate elit viverra a. Curabitur in laoreet nisl.
1019+
</Text>
1020+
<Button onPress={() => navigation.goBack()} style={{ marginTop: 15 }}>
1021+
Go back
1022+
</Button>
1023+
</View>
1024+
);
1025+
}
1026+
1027+
export default function App() {
1028+
return (
1029+
<NavigationContainer>
1030+
<MyStack />
1031+
</NavigationContainer>
1032+
);
1033+
}
1034+
```
1035+
1036+
</TabItem>
1037+
</Tabs>
1038+
1039+
:::warning
1040+
1041+
Due to technical issues in platform component integration with `react-native`, `presentation: 'formSheet'` has limited support for `flex: 1`. The tradeoff is necessary to prevent ["sheet flickering" problem on iOS](https://github.com/software-mansion/react-native-screens/issues/1722). Work on the problem is [in progress](https://github.com/software-mansion/react-native-screens/pull/2748).
1042+
1043+
Currently on Android, using `flex: 1` on a top-level content container passed to a `formSheet` with `showAllowedDetents: 'fitToContents'` causes the sheet to not display at all, leaving only the dimmed background visible.
1044+
1045+
Unfortunately, even if you don't use `flex: 1` but the content's height is less than max screen height, the rest of the sheet might become translucent or use the default theme background color (you can see this happening on the screenshots in the descrption of [this PR](https://github.com/software-mansion/react-native-screens/pull/2462)). To match the sheet to the background of your content, set `backgroundColor` in the `contentStyle` prop of the given screen.
1046+
1047+
On Android, there are also some problems with getting nested ScrollViews to work properly. The solution is to set `nestedScrollEnabled` on the `ScrollView`, but this does not work if the content's height is less than the `ScrollView`'s height. Please see [this PR](https://github.com/facebook/react-native/pull/44099) for details and suggested [workaround](https://github.com/facebook/react-native/pull/44099#issuecomment-2058469661).
1048+
:::
1049+
1050+
#### `sheetAllowedDetents`
1051+
1052+
:::note
1053+
1054+
Works only when `presentation` is set to `formSheet`.
1055+
1056+
:::
1057+
1058+
<video playsInline autoPlay muted loop>
1059+
<source src="/assets/7.x/native-stack/formSheet-sheetAllowedDetents.mp4" />
1060+
</video>
1061+
1062+
Describes heights where a sheet can rest.
1063+
1064+
Supported values:
1065+
1066+
- `fitToContents` - intents to set the sheet height to the height of its contents.
1067+
- Array of fractions, e.g. `[0.25, 0.5, 0.75]`:
1068+
- Heights should be described as fraction (a number from `[0, 1]` interval) of screen height / maximum detent height.
1069+
- The array **must** be sorted in ascending order. This invariant is verified only in developement mode, where violation results in error.
1070+
- iOS accepts any number of detents, while **Android is limited to three** - any surplus values, beside first three are ignored.
1071+
1072+
Defaults to `[1.0]`.
1073+
1074+
Only supported on Android and iOS.
1075+
1076+
#### `sheetElevation`
1077+
1078+
:::note
1079+
1080+
Works only when `presentation` is set to `formSheet`.
1081+
1082+
:::
1083+
1084+
<video playsInline autoPlay muted loop>
1085+
<source src="/assets/7.x/native-stack/formSheet-sheetElevation.mp4" />
1086+
</video>
1087+
1088+
Integer value describing elevation of the sheet, impacting shadow on the top edge of the sheet.
1089+
1090+
Not dynamic - changing it after the component is rendered won't have an effect.
1091+
1092+
Defaults to `24`.
1093+
1094+
Only supported on Android.
1095+
1096+
#### `sheetExpandsWhenScrolledToEdge`
1097+
1098+
:::note
1099+
1100+
Works only when `presentation` is set to `formSheet`.
1101+
1102+
:::
1103+
1104+
<video playsInline autoPlay muted loop>
1105+
<source src="/assets/7.x/native-stack/formSheet-sheetExpandsWhenScrolledToEdge.mp4" />
1106+
</video>
1107+
1108+
Whether the sheet should expand to larger detent when scrolling.
1109+
1110+
Defaults to `true`.
1111+
1112+
Only supported on iOS.
1113+
1114+
:::warning
1115+
1116+
Please note that for this interaction to work, the ScrollView must be "first-subview-chain" descendant of the Screen component. This restriction is due to platform requirements.
1117+
1118+
:::
1119+
1120+
#### `sheetCornerRadius`
1121+
1122+
:::note
1123+
1124+
Works only when `presentation` is set to `formSheet`.
1125+
1126+
:::
1127+
1128+
<video playsInline autoPlay muted loop>
1129+
<source src="/assets/7.x/native-stack/formSheet-sheetCornerRadius.mp4" />
1130+
</video>
1131+
1132+
The corner radius that the sheet will try to render with.
1133+
1134+
If set to non-negative value it will try to render sheet with provided radius, else it will apply system default.
1135+
1136+
If left unset, system default is used.
1137+
1138+
Only supported on Android and iOS.
1139+
1140+
#### `sheetInitialDetentIndex`
1141+
1142+
:::note
1143+
1144+
Works only when `presentation` is set to `formSheet`.
1145+
1146+
:::
1147+
1148+
<video playsInline autoPlay muted loop>
1149+
<source src="/assets/7.x/native-stack/formSheet-sheetInitialDetentIndex.mp4" />
1150+
</video>
1151+
1152+
**Index** of the detent the sheet should expand to after being opened.
1153+
1154+
If the specified index is out of bounds of `sheetAllowedDetents` array, in dev environment more errors will be thrown, in production the value will be reset to default value.
1155+
1156+
Additionaly there is `last` value available, when set the sheet will expand initially to last (largest) detent.
1157+
1158+
Defaults to `0` - which represents first detent in the detents array.
1159+
1160+
Only supported on Android and iOS.
1161+
1162+
#### `sheetGrabberVisible`
1163+
1164+
:::note
1165+
1166+
Works only when `presentation` is set to `formSheet`.
1167+
1168+
:::
1169+
1170+
<video playsInline autoPlay muted loop>
1171+
<source src="/assets/7.x/native-stack/formSheet-sheetGrabberVisible.mp4" />
1172+
</video>
1173+
1174+
Boolean indicating whether the sheet shows a grabber at the top.
1175+
1176+
Defaults to `false`.
1177+
1178+
Only supported on iOS.
1179+
1180+
#### `sheetLargestUndimmedDetentIndex`
1181+
1182+
:::note
1183+
1184+
Works only when `presentation` is set to `formSheet`.
1185+
1186+
:::
1187+
1188+
<video playsInline autoPlay muted loop>
1189+
<source src="/assets/7.x/native-stack/formSheet-sheetLargestUndimmedDetentIndex.mp4" />
1190+
</video>
1191+
1192+
The largest sheet detent for which a view underneath won't be dimmed.
1193+
1194+
This prop can be set to an number, which indicates index of detent in `sheetAllowedDetents` array for which there won't be a dimming view beneath the sheet.
1195+
1196+
Additionaly there are following options available:
1197+
1198+
- `none` - there will be dimming view for all detents levels,
1199+
- `last` - there won't be a dimming view for any detent level.
1200+
1201+
Defaults to `none`, indicating that the dimming view should be always present.
1202+
1203+
Only supported on Android and iOS.
1204+
8721205
#### `orientation`
8731206

8741207
The display orientation to use for the screen.

0 commit comments

Comments
 (0)