react-native-bottom-sheet
react-native-bottom-sheet copied to clipboard
[v4] | [v2] Centering detached modals
Bug
Detached modals still require snap points and default to the bottom of the screen despite the common pattern not being draggable and being centered on screen.
Environment info
Library | Version |
---|---|
@gorhom/bottom-sheet | 4.4.3 |
react-native | 0.68.2 |
react-native-reanimated | 2.8.0 |
react-native-gesture-handler | 2.2.1 |
Steps To Reproduce
- Follow steps in documentation for using a detached
BottomSheetModal
- Notice that
snapPoints
are required and that the modal is not centered
Describe what you expected to happen:
-
snapPoints
would no longer be required (because modals don't need to be expandable) - The modal would automatically be centered on the screen
Reproducible sample code
N/A
There doesn't appear to be an easy way to vertically center modals via styles
Waiting for this feature! +1
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.
any update on this? :(
Still nothing?
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.
Also really wanting this feature!
Also really wanting this feature!
So do I.
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.
This issue was closed because it has been stalled for 5 days with no activity.
~Could we reopen this issue? Would love to see this supported~
Never mind, I found a workaround. Please see my comment below 🙇
For anyone still looking for a solution, I created the following hook which can be used to center a detached bottom sheet regardless of its height:
const useCenteredDynamicPositioning = () => {
const { animatedHandleHeight, animatedContentHeight, handleContentLayout } = useBottomSheetDynamicSnapPoints([
'CONTENT_HEIGHT',
]);
const insets = useSafeAreaInsets();
const windowHeight = Dimensions.get('window').height;
const animatedSnapPoints = useDerivedValue(() => {
const safeHeight = windowHeight - insets.top - insets.bottom;
const bottomSheetHeight = animatedContentHeight.value + animatedHandleHeight.value;
const verticalMargin = (safeHeight - bottomSheetHeight) / 2;
const snapPoint = windowHeight - insets.top - verticalMargin;
// Values lower than 1 aren't valid snapPoints, so always return at least 1
return [snapPoint >= 1 ? snapPoint : 1];
});
return {
animatedSnapPoints,
animatedHandleHeight,
animatedContentHeight,
handleContentLayout,
};
};
Usage example:
const SomeBottomSheet = () => {
// ...
const { animatedSnapPoints, animatedHandleHeight, animatedContentHeight, handleContentLayout } =
useCenteredDynamicPositioning();
// ...
return (
<BottomSheet
detached={true}
// Start on the first (and only) snapPoint
index={0}
snapPoints={animatedSnapPoints}
handleHeight={animatedHandleHeight}
contentHeight={animatedContentHeight}
>
{/* I would recommend adding a maxHeight to styles.contentContainer */}
{/* A normal View can also be used instead of a BottomSheetScrollView */}
<BottomSheetScrollView onLayout={handleContentLayout} style={styles.contentContainer}>
{/* ... bottom sheet contents*/}
</BottomSheetScrollView>
</BottomSheet>
);
};
since useBottomSheetDynamicSnapPoints will be removed on next release, this is my updated hook and fixed issue with device which has inset.bottom, thanks to @MiguelAraCo :
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
import { useHeaderHeight } from '@react-navigation/elements';
import { Dimensions } from 'react-native';
import { useDerivedValue, useSharedValue } from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
export const useCenteredDynamicPositioning = () => {
const tabBarHeight = useBottomTabBarHeight();
const headerHeight = useHeaderHeight();
const insets = useSafeAreaInsets();
const windowHeight = Dimensions.get('window').height;
const animatedContentHeight = useSharedValue(0);
const animatedHandleHeight = useSharedValue(-999);
const animatedSnapPoints = useDerivedValue(() => {
const safeHeight =
windowHeight -
(insets.top + headerHeight) -
(insets.bottom + tabBarHeight);
const bottomSheetHeight =
animatedContentHeight.value + animatedHandleHeight.value;
const verticalMargin = (safeHeight - bottomSheetHeight) / 2;
const snapPoint =
windowHeight - insets.top - verticalMargin - insets.bottom;
return [snapPoint >= 1 ? snapPoint : 1];
});
return {
animatedSnapPoints,
animatedHandleHeight,
animatedContentHeight,
};
};
since useBottomSheetDynamicSnapPoints will be removed on next release, this is my updated hook and fixed issue with device which has inset.bottom, thanks to @MiguelAraCo :
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs'; import { useHeaderHeight } from '@react-navigation/elements'; import { Dimensions } from 'react-native'; import { useDerivedValue, useSharedValue } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; export const useCenteredDynamicPositioning = () => { const tabBarHeight = useBottomTabBarHeight(); const headerHeight = useHeaderHeight(); const insets = useSafeAreaInsets(); const windowHeight = Dimensions.get('window').height; const animatedContentHeight = useSharedValue(0); const animatedHandleHeight = useSharedValue(-999); const animatedSnapPoints = useDerivedValue(() => { const safeHeight = windowHeight - (insets.top + headerHeight) - (insets.bottom + tabBarHeight); const bottomSheetHeight = animatedContentHeight.value + animatedHandleHeight.value; const verticalMargin = (safeHeight - bottomSheetHeight) / 2; const snapPoint = windowHeight - insets.top - verticalMargin - insets.bottom; return [snapPoint >= 1 ? snapPoint : 1]; }); return { animatedSnapPoints, animatedHandleHeight, animatedContentHeight, }; };
This is not working. The bottom sheet is always sticky to the bottom of the screen
How is the animatedContentHeight
calculated? @NhatDung
How to calculate animatedContentHeight when useCenteredDynamicPositioning is deprecated?