You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 22:20:39