Flutter登录后在Scaffold Body内路由,保留通用AppBar与BottomBar
在Scaffold Body内实现路由并保留通用AppBar/BottomBar的方案
完全可行,核心思路是通过嵌套Navigator作为Scaffold的body区域,让外层的AppBar和BottomNavigationBar保持固定,内部由独立的Navigator管理页面路由切换。以下是具体实现步骤和代码示例:
1. 定义通用的AppBar和BottomBar组件
先封装可复用的通用组件,避免在每个页面重复编写:
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { final String title; const CustomAppBar({super.key, required this.title}); @override Widget build(BuildContext context) { return AppBar( title: Text(title), centerTitle: true, leading: IconButton( icon: const Icon(Icons.arrow_back), onPressed: () => Navigator.of(context).pop(), ), // 可添加更多通用操作,比如右侧菜单按钮 ); } @override Size get preferredSize => const Size.fromHeight(kToolbarHeight); } class CustomBottomBar extends StatelessWidget { final int currentIndex; final Function(int) onTap; const CustomBottomBar({super.key, required this.currentIndex, required this.onTap}); @override Widget build(BuildContext context) { return BottomNavigationBar( currentIndex: currentIndex, onTap: onTap, type: BottomNavigationBarType.fixed, items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'), BottomNavigationBarItem(icon: Icons.person, label: '我的'), BottomNavigationBarItem(icon: Icons.notifications, label: '通知'), ], ); } }
2. 构建带内部Navigator的主页面
创建一个主页面,将Navigator作为Scaffold的body,同时绑定底部导航的切换逻辑:
class MainScreen extends StatefulWidget { const MainScreen({super.key}); @override State<MainScreen> createState() => _MainScreenState(); } class _MainScreenState extends State<MainScreen> with RouteAware { final GlobalKey<NavigatorState> _innerNavigatorKey = GlobalKey<NavigatorState>(); int _currentBottomIndex = 0; final RouteObserver<PageRoute> _routeObserver = RouteObserver<PageRoute>(); // 底部导航点击切换内部路由 void _onBottomTap(int index) { setState(() => _currentBottomIndex = index); switch (index) { case 0: _innerNavigatorKey.currentState?.pushReplacementNamed('/home'); break; case 1: _innerNavigatorKey.currentState?.pushReplacementNamed('/profile'); break; case 2: _innerNavigatorKey.currentState?.pushReplacementNamed('/notifications'); break; } } // 监听内部路由变化,同步底部导航选中状态 @override void didPush() { super.didPush(); _syncBottomIndexWithRoute(); } @override void didPop() { super.didPop(); _syncBottomIndexWithRoute(); } void _syncBottomIndexWithRoute() { final currentRoute = _innerNavigatorKey.currentState?.currentRoute; final routeName = currentRoute?.settings.name; setState(() { if (routeName == '/home') _currentBottomIndex = 0; else if (routeName == '/profile') _currentBottomIndex = 1; else if (routeName == '/notifications') _currentBottomIndex = 2; }); } @override void didChangeDependencies() { super.didChangeDependencies(); _routeObserver.subscribe(this, ModalRoute.of(context)! as PageRoute); } @override void dispose() { _routeObserver.unsubscribe(this); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: CustomAppBar(title: _getCurrentTitle()), body: Navigator( key: _innerNavigatorKey, initialRoute: '/home', onGenerateRoute: (settings) { Widget page; switch (settings.name) { case '/home': page = const HomePage(); break; case '/profile': page = const ProfilePage(); break; case '/notifications': page = const NotificationsPage(); break; case '/detail': page = DetailPage(id: settings.arguments as int); break; default: page = const HomePage(); } return MaterialPageRoute(builder: (_) => page); }, observers: [_routeObserver], ), bottomNavigationBar: CustomBottomBar( currentIndex: _currentBottomIndex, onTap: _onBottomTap, ), ); } // 根据当前路由动态生成AppBar标题 String _getCurrentTitle() { switch (_currentBottomIndex) { case 0: return '首页'; case 1: return '个人中心'; case 2: return '通知中心'; default: return '我的应用'; } } }
3. 内部页面示例
内部页面可以直接使用内部Navigator进行跳转,无需重复定义AppBar和BottomBar:
class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return ListView.builder( itemCount: 10, itemBuilder: (_, index) { return ListTile( title: Text('条目 $index'), onTap: () { // 内部路由跳转到详情页 Navigator.of(context).pushNamed('/detail', arguments: index); }, ); }, ); } } class ProfilePage extends StatelessWidget { const ProfilePage({super.key}); @override Widget build(BuildContext context) { return const Center(child: Text('个人信息设置')); } } class NotificationsPage extends StatelessWidget { const NotificationsPage({super.key}); @override Widget build(BuildContext context) { return const Center(child: Text('最新通知列表')); } } class DetailPage extends StatelessWidget { final int id; const DetailPage({super.key, required this.id}); @override Widget build(BuildContext context) { return Scaffold( // 详情页可复用通用AppBar,也可自定义 appBar: CustomAppBar(title: '详情页 $id'), body: Center(child: Text('详情内容 ID: $id')), ); } }
4. 登录后的跳转逻辑
登录成功后,直接导航到MainScreen即可,外层的AppBar和BottomBar会自动加载:
// 登录页面的登录按钮点击事件 ElevatedButton( onPressed: () async { // 执行登录请求逻辑 bool loginSuccess = await _performLogin(); if (loginSuccess) { // 替换当前登录页为MainScreen,避免返回登录页 Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (_) => const MainScreen()), ); } }, child: const Text('登录'), );
核心优势
- 复用性高:通用AppBar和BottomBar只需维护一份代码,无需在每个页面重复实现
- 路由独立:内部Navigator管理页面栈,不影响外层路由逻辑
- 状态同步:通过RouteObserver监听路由变化,自动同步底部导航选中状态
内容的提问来源于stack exchange,提问作者Hari .S
相关产品推荐
相关产品推荐

