如何让Flutter中Column嵌套的Navigator组件自适应包裹子内容
问题根因
- Navigator内部依赖的Overlay组件默认要求父组件提供有限的约束,放在Column的无界竖轴约束中会触发
constraints.biggest.isFinite断言崩溃 - 包裹Flexible/Expanded后会让Navigator强制占满Column剩余所有空间,不符合自适应需求
实现步骤
- 定义自定义布局委托,用来控制Navigator的外层尺寸等于子内容实际大小:
class NavigatorSizeLayoutDelegate extends SingleChildLayoutDelegate { final Size contentSize; NavigatorSizeLayoutDelegate(this.contentSize); @override Size getSize(BoxConstraints constraints) { return constraints.constrain(contentSize); } @override BoxConstraints getConstraintsForChild(BoxConstraints constraints) { return constraints.copyWith(maxHeight: double.infinity); } @override bool shouldRelayout(covariant NavigatorSizeLayoutDelegate oldDelegate) { return contentSize != oldDelegate.contentSize; } }
- 在State类中定义存储尺寸、导航相关的变量:
Size _navigatorContentSize = Size.zero; final GlobalKey _navigatorContentKey = GlobalKey(); final _navigatorKey = GlobalKey<NavigatorState>();
- 封装自适应高度的Navigator组件,监听路由变化动态测量子内容高度:
Widget _buildAdaptiveNavigator() { return CustomSingleChildLayout( delegate: NavigatorSizeLayoutDelegate(_navigatorContentSize), child: Navigator( key: _navigatorKey, initialRoute: widget.setupPageRoute, onGenerateRoute: (settings) { final originPage = _onGenerateRoute(settings); return MaterialPageRoute( builder: (context) => KeyedSubtree( key: _navigatorContentKey, child: originPage.builder(context), ), settings: settings, ); }, observers: [ NavigatorObserver() { @override void didPop(Route route, Route? previousRoute) { super.didPop(route, previousRoute); _measureNavigatorHeight(); } @override void didPush(Route route, Route? previousRoute) { super.didPush(route, previousRoute); WidgetsBinding.instance.addPostFrameCallback((_) => _measureNavigatorHeight()); } } ], ), ); } void _measureNavigatorHeight() { final renderBox = _navigatorContentKey.currentContext?.findRenderObject() as RenderBox?; if (renderBox != null && mounted) { setState(() { _navigatorContentSize = renderBox.size; }); } }
- 直接将封装好的组件放入Column即可:
Column( mainAxisSize: MainAxisSize.min, children: [ const Text("First Text"), _buildAdaptiveNavigator(), const Text("Second Text"), ], )
注意事项
- 如果路由页面内包含滚动组件,需要给滚动组件设置
shrinkWrap: true、primary: false,避免滚动组件强制撑满高度 - 页面内有动态内容变化导致高度变更的场景,可以在内容更新后手动调用
_measureNavigatorHeight同步高度
内容的提问来源于stack exchange,提问作者Bolaji
相关产品推荐
相关产品推荐

