Flutter全局持久化栏位置适配:按需置于bottomNavigationBar上方或底部
解决Flutter全局底部栏自适应页面底部导航的问题
方案一:通过NavigatorObserver监听页面状态+状态管理实现动态定位
核心思路是监听页面的入栈出栈,判断当前页面的Scaffold是否包含bottomNavigationBar,然后动态调整全局栏的bottom值。
- 定义状态管理类,保存当前页面是否有底部导航的状态:
class GlobalBarPositionNotifier extends ChangeNotifier { bool _hasBottomNav = false; bool get hasBottomNav => _hasBottomNav; set hasBottomNav(bool value) { _hasBottomNav = value; notifyListeners(); } }
- 自定义
NavigatorObserver,监听页面变化并更新状态:
class PageNavObserver extends NavigatorObserver { final GlobalBarPositionNotifier notifier; PageNavObserver(this.notifier); @override void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) { super.didPush(route, previousRoute); _checkBottomNav(route); } @override void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) { super.didPop(route, previousRoute); if (previousRoute != null) { _checkBottomNav(previousRoute); } } void _checkBottomNav(Route<dynamic> route) { if (route is MaterialPageRoute) { final widget = route.builder(navigator!.context); if (widget is Scaffold) { notifier.hasBottomNav = widget.bottomNavigationBar != null; } else { // 页面非直接返回Scaffold时,延迟查找子树中的Scaffold WidgetsBinding.instance.addPostFrameCallback((_) { final scaffoldState = Scaffold.maybeOf(navigator!.context); notifier.hasBottomNav = scaffoldState?.widget.bottomNavigationBar != null; }); } } } }
- 在
MaterialApp中配置使用:
void main() { final positionNotifier = GlobalBarPositionNotifier(); runApp( ChangeNotifierProvider.value( value: positionNotifier, child: MyApp(positionNotifier: positionNotifier), ), ); } class MyApp extends StatelessWidget { final GlobalBarPositionNotifier positionNotifier; const MyApp({super.key, required this.positionNotifier}); @override Widget build(BuildContext context) { return MaterialApp( navigatorObservers: [PageNavObserver(positionNotifier)], builder: (context, child) { return Stack( children: [ child!, Consumer<GlobalBarPositionNotifier>( builder: (context, notifier, _) { return Positioned( left: 0, right: 0, bottom: notifier.hasBottomNav ? kBottomNavigationBarHeight : 0, child: Container( height: 50, color: Colors.blue, child: const Center(child: Text('全局底部栏')), ), ); }, ), ], ); }, home: const HomePage(), // 带bottomNavigationBar的页面 ); } }
方案二:通过InheritedWidget在页面间传递状态
如果不想引入状态管理库,可通过InheritedWidget传递当前页面的底部导航状态:
- 定义
InheritedWidget:
class PageBottomNavConfig extends InheritedWidget { final bool hasBottomNav; const PageBottomNavConfig({ super.key, required this.hasBottomNav, required super.child, }); static PageBottomNavConfig? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<PageBottomNavConfig>(); } @override bool updateShouldNotify(PageBottomNavConfig oldWidget) { return hasBottomNav != oldWidget.hasBottomNav; } }
- 在每个页面的
Scaffold外层包裹该组件:
// 带底部导航的页面 class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return PageBottomNavConfig( hasBottomNav: true, child: Scaffold( bottomNavigationBar: BottomNavigationBar( items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'), BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'), ], currentIndex: 0, ), body: const Center(child: Text('首页')), ), ); } } // 不带底部导航的页面 class DetailPage extends StatelessWidget { const DetailPage({super.key}); @override Widget build(BuildContext context) { return PageBottomNavConfig( hasBottomNav: false, child: Scaffold( body: const Center(child: Text('详情页')), ), ); } }
- 在全局栏中获取配置并调整位置:
builder: (context, child) { return Stack( children: [ child!, Builder( builder: (context) { final config = PageBottomNavConfig.of(context); final bottomOffset = config?.hasBottomNav ?? false ? kBottomNavigationBarHeight : 0; return Positioned( left: 0, right: 0, bottom: bottomOffset, child: Container( height: 50, color: Colors.blue, child: const Center(child: Text('全局底部栏')), ), ); }, ), ], ); },
注意事项
- 方案一中,若页面是嵌套
Scaffold,可扩展_checkBottomNav方法递归查找子树中的Scaffold,保证判断准确。 - 方案二需要每个页面手动包裹
PageBottomNavConfig,适合页面数量较少的场景。
内容的提问来源于stack exchange,提问作者Ufder
相关产品推荐
相关产品推荐

