React Navigation中NavigationContainer如何实现动态高度
直接按以下步骤实现即可,不需要在每个页面单独绑定onLayout:
- 问题核心原因:react-navigation v6 默认会对非活跃页面做视图缓存、原生端视图拆分,页面切换时不会重新触发子组件的onLayout事件,直接给页面绑定布局监听拿不到切换后的高度是正常表现。
具体实现
- 外层统一测量容器高度
不要直接把NavigationContainer作为BottomSheet的子组件,先套一层可获取ref的View作为测量锚点,监听导航索引变化主动测量高度:
import { useRef, useState, useEffect } from 'react'; import { View } from 'react-native'; import BottomSheet from '@gorhom/bottom-sheet'; import { NavigationContainer, useNavigationState } from '@react-navigation/native'; const DynamicHeightBottomSheetNav = () => { const bottomSheetRef = useRef(null); const navWrapRef = useRef(null); const [sheetHeight, setSheetHeight] = useState(320); // 初始默认高度 // 监听当前活跃页面索引 const activeIndex = useNavigationState(state => state.index); // 统一测量更新高度方法 const updateSheetHeight = () => { requestAnimationFrame(() => { navWrapRef.current?.measure((_, __, ___, height) => { if (height > 0 && height !== sheetHeight) { setSheetHeight(height); bottomSheetRef.current?.snapToPosition(height); } }); }); }; // 页面切换时触发测量 useEffect(() => { updateSheetHeight(); }, [activeIndex]); return ( <BottomSheet ref={bottomSheetRef} snapPoints={[sheetHeight]} enableDynamicSizing={false} > <View ref={navWrapRef} collapsable={false} style={{ flexShrink: 1 }} onLayout={updateSheetHeight} > <NavigationContainer independent={true}> {/* 内嵌你的Stack/Tab导航器 */} </NavigationContainer> </View> </BottomSheet> ); };
- 适配页面内动态内容场景
如果单个页面内部有展开收起、异步加载内容导致高度变化的情况,通过路由参数触发外层重新测量即可,不需要在页面单独写高度监听:
// 业务页面内,内容变化时触发 import { useNavigation } from '@react-navigation/native'; const PageA = () => { const navigation = useNavigation(); const handleLoadContent = () => { // 内容加载/展开完成后触发重测 setTimeout(() => { navigation.setParams({ remeasureTs: Date.now() }); }, 0); }; // 页面业务逻辑 }; // 外层导航组件内增加参数监听 const activeRouteParams = useNavigationState(state => state.routes[state.index]?.params); useEffect(() => { if (activeRouteParams?.remeasureTs) updateSheetHeight(); }, [activeRouteParams?.remeasureTs]);
- 必要配置避坑
- 如果使用
@react-navigation/native-stack,给导航器设置detachInactiveScreens={false},避免非活跃页面被原生端拆分导致测量值不准 - 内层导航器、页面根容器不要写
flex:1样式,会强制撑满父容器导致测量拿到固定最大值,改用flexShrink:1适配内容高度 - 安卓端记得给测量用的外层View加
collapsable={false},防止原生布局优化把节点合并导致测量失败
实测这个方案的高度更新延迟在1-2帧,用户感知不到高度跳变,不需要侵入修改每个业务页面的布局逻辑。
内容的提问来源于stack exchange,提问作者Eden Eliel
相关产品推荐
相关产品推荐

