如何使用go_router实现底部导航栏与AppBar的持久化显示?
可以用go_router实现固定AppBar与底部导航栏的路由方案
完全可以通过go_router的嵌套路由/StatefulShellRoute实现需求,核心思路是将固定的AppBar和底部导航栏作为父容器路由的内容,子路由仅替换容器内的页面区域,跳转时父容器不会重建。
核心实现方案:StatefulShellRoute
go_router提供的StatefulShellRoute专门用于处理底部导航栏这类需要持久化状态的场景,它会将父容器(包含AppBar和底部导航)与子路由页面分离,切换子路由或底部导航tab时,父容器始终保持挂载状态。
完整代码示例
import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp.router( routerConfig: _router, title: '固定导航示例', ); } } // 定义路由 final GoRouter _router = GoRouter( routes: [ StatefulShellRoute.indexedStack( // 父容器:渲染固定的AppBar和底部导航栏 builder: (context, state, navigationShell) { return Scaffold( appBar: AppBar(title: const Text('固定导航栏')), // 子路由页面会渲染在这个navigationShell中 body: navigationShell, bottomNavigationBar: BottomNavigationBar( currentIndex: navigationShell.currentIndex, onTap: (index) { // 切换底部导航tab navigationShell.goBranch( index, initialLocation: index == navigationShell.currentIndex, ); }, items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'), BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'), ], ), ); }, // 底部导航对应的路由分支 branches: [ // 首页分支 StatefulShellBranch( routes: [ GoRoute( path: '/', builder: (context, state) => const HomePage(), // 嵌套子路由:从首页跳转的详情页 routes: [ GoRoute( path: 'detail', builder: (context, state) => const DetailPage(), ), ], ), ], ), // 设置页分支 StatefulShellBranch( routes: [ GoRoute( path: '/settings', builder: (context, state) => const SettingsPage(), ), ], ), ], ), ], ); // 首页页面 class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Center( child: ElevatedButton( onPressed: () => context.go('/detail'), // 跳转到详情页 child: const Text('跳转到详情页'), ), ); } } // 详情页面 class DetailPage extends StatelessWidget { const DetailPage({super.key}); @override Widget build(BuildContext context) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('详情页面'), ElevatedButton( onPressed: () => context.go('/'), // 返回首页 child: const Text('返回首页'), ), ], ), ); } } // 设置页面 class SettingsPage extends StatelessWidget { const SettingsPage({super.key}); @override Widget build(BuildContext context) { return const Center(child: Text('设置页面')); } }
效果说明
- 首页截图:顶部显示固定的"固定导航栏"AppBar,底部有"首页"/"设置"导航项,中间是跳转按钮。
- 详情页截图:顶部AppBar和底部导航栏完全保留,仅中间区域替换为详情页内容,跳转过程中父容器无重建。
关键细节
StatefulShellRoute.indexedStack使用IndexedStack管理分支页面,会保留每个tab的页面状态(比如首页是StatefulWidget时,输入内容不会因切换tab丢失)。- 子路由跳转(如首页→详情页)仅替换
navigationShell的内容,父Scaffold的AppBar和底部导航栏不会触发重建。 - 若不需要保留tab状态,也可使用普通
ShellRoute,但StatefulShellRoute更贴合底部导航的常用场景。
内容的提问来源于stack exchange,提问作者Tipu Sultan
相关产品推荐
相关产品推荐

