Flutter如何实现页面导航时跨页面持久化的BottomAppBar
Flutter BottomAppBar全局持久化解决方案
你当前的代码逻辑本身切换screens列表内的5个页面时,BottomAppBar是不会消失的。如果出现跳转后导航栏消失的情况,是因为你使用了Navigator.of(context).push类的路由API,新页面会直接覆盖当前承载BottomAppBar的Scaffold层级。你可以选择以下任意一种方案实现全局持久底部导航:
方案1:嵌套Navigator(适配绝大多数业务场景)
在外层Scaffold的body中嵌入独立的Navigator路由栈,所有子页面的跳转都在这个嵌套路由内完成,不会跳出承载BottomAppBar的外层Scaffold:
- 替换原有
BottomNavBar中Scaffold的body部分代码:
// 先在State类内定义嵌套Navigator的全局key,方便后续调用路由 final GlobalKey<NavigatorState> _navKey = GlobalKey<NavigatorState>(); @override Widget build(BuildContext context) { return Scaffold( // 保留你原有的bottomNavigationBar、floatingActionButton等配置 resizeToAvoidBottomInset: false, bottomNavigationBar: /* 你的原有BottomAppBar代码 */, floatingActionButton: /* 你的原有FAB代码 */, floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, // 替换原有body为嵌套Navigator body: Navigator( key: _navKey, initialRoute: '/', onGenerateRoute: (settings) { Widget page; switch(settings.name) { case '/': // 默认返回当前选中的主页面 page = screens[currentTab]; break; // 所有需要跳转的子页面都在此处注册路由 case '/goodsDetail': page = const GoodsDetailPage(); break; case '/orderList': page = const OrderListPage(); break; default: page = screens[currentTab]; } return MaterialPageRoute(builder: (_) => page); }, ), ); }
- 子页面跳转时使用嵌套Navigator的上下文,不要调用根路由:
// 错误写法:调用根路由会直接覆盖整个外层Scaffold,导致底部导航消失 // Navigator.of(context).pushNamed('/goodsDetail'); // 正确写法:指定使用非根路由,在嵌套Navigator内跳转 Navigator.of(context, rootNavigator: false).pushNamed('/goodsDetail'); // 也可以直接用之前定义的key调用 // _navKey.currentState?.pushNamed('/goodsDetail');
方案2:壳路由嵌套(适配使用GoRouter等路由库的项目)
如果你的项目使用了GoRouter之类的路由管理库,可以直接配置ShellRoute,将所有子页面统一嵌套在承载BottomAppBar的Scaffold下:
final GoRouter router = GoRouter( routes: [ ShellRoute( builder: (context, state, child) { // 此处直接返回你的BottomNavBar组件,把子页面传入body即可 return BottomNavBar(child: child); }, routes: [ // 所有子路由都会被嵌套在ShellRoute的父组件下,底部导航持久存在 GoRoute(path: '/', builder: (context, state) => const HomePage()), GoRoute(path: '/menu', builder: (context, state) => const MenuPage()), GoRoute(path: '/goodsDetail', builder: (context, state) => const GoodsDetailPage()), ], ) ] );
同时你需要修改BottomNavBar组件,接收child参数作为body内容即可。
现有代码优化提示
你当前已经定义了screens页面列表,无需每次切换tab都重新创建页面实例,直接按索引取值即可提升性能:
// 原有的点击事件代码 // onPressed: () { // setState(() { // currentScreen = MenuPage(); // currentTab = 4; // }); // }, // 优化后写法 onPressed: () { setState(() { currentTab = 4; currentScreen = screens[currentTab]; }); },
内容的提问来源于stack exchange,提问作者Usama Ashraf
相关产品推荐
相关产品推荐

