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

能否用GoRouter实现带BottomNavBar的PageView功能?

用GoRouter实现带BottomNavBar和PageView的视图方案

你的思路完全可行

借助PageView.builder配合ShellRoute的itemBuilder是GoRouter实现这类底部导航布局的标准方案之一,完美适配动态生成页面的需求,不需要预先传入所有children,完全可以落地。

具体实现步骤

1. 配置路由结构

先定义底部导航对应的子路由,再通过ShellRoute包裹这些路由,统一管理底部导航栏和PageView容器:

final router = GoRouter(
  initialLocation: '/home',
  routes: [
    ShellRoute(
      builder: (context, state, child) {
        return RootShell(state: state);
      },
      routes: [
        GoRoute(
          path: '/home',
          builder: (context, state) => const HomePage(),
        ),
        GoRoute(
          path: '/profile',
          builder: (context, state) => const ProfilePage(),
        ),
        GoRoute(
          path: '/settings',
          builder: (context, state) => const SettingsPage(),
        ),
      ],
    ),
  ],
);

2. 实现RootShell组件

在Shell组件中集成BottomNavigationBar和PageView.builder,通过ShellRouteState动态获取子路由页面:

class RootShell extends StatelessWidget {
  const RootShell({super.key, required this.state});

  final ShellRouteState state;

  @override
  Widget build(BuildContext context) {
    // 定义底部导航项与路由的映射
    final tabItems = [
      (icon: Icons.home, label: '首页', route: state.routeList[0]),
      (icon: Icons.person, label: '我的', route: state.routeList[1]),
      (icon: Icons.settings, label: '设置', route: state.routeList[2]),
    ];

    // 获取当前路由对应的索引
    final currentIndex = tabItems.indexWhere(
      (item) => item.route.location == state.location,
    );

    return Scaffold(
      body: PageView.builder(
        itemCount: tabItems.length,
        itemBuilder: (context, index) {
          // 通过ShellRouteState生成对应路由的页面
          return tabItems[index].route.builder(context, state);
        },
        // 若要禁止手动滑动切换,可添加此属性;保留滑动则删除
        physics: const NeverScrollableScrollPhysics(),
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: currentIndex,
        type: BottomNavigationBarType.fixed,
        items: tabItems.map((item) {
          return BottomNavigationBarItem(
            icon: Icon(item.icon),
            label: item.label,
          );
        }).toList(),
        onTap: (index) {
          // 点击导航项切换路由
          context.go(tabItems[index].route.location!);
        },
      ),
    );
  }
}

其他可选方案

方案一:手动管理索引与路由同步

无需ShellRoute,直接用StatefulWidget管理PageController,监听路由变化同步页面索引:

class RootPage extends StatefulWidget {
  const RootPage({super.key});

  @override
  State<RootPage> createState() => _RootPageState();
}

class _RootPageState extends State<RootPage> {
  final PageController _pageController = PageController();
  final _routePaths = ['/home', '/profile', '/settings'];
  int _currentIndex = 0;

  @override
  void initState() {
    super.initState();
    // 监听路由变化更新页面索引
    GoRouter.of(context).addListener(_syncRouteAndPage);
  }

  @override
  void dispose() {
    GoRouter.of(context).removeListener(_syncRouteAndPage);
    _pageController.dispose();
    super.dispose();
  }

  void _syncRouteAndPage() {
    final currentPath = GoRouter.of(context).location;
    final index = _routePaths.indexOf(currentPath);
    if (index != -1 && index != _currentIndex) {
      setState(() => _currentIndex = index);
      _pageController.jumpToPage(index);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: PageView(
        controller: _pageController,
        onPageChanged: (index) {
          // 滑动页面时同步路由
          if (index != _currentIndex) {
            context.go(_routePaths[index]);
          }
        },
        children: const [
          HomePage(),
          ProfilePage(),
          SettingsPage(),
        ],
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: '我的'),
          BottomNavigationBarItem(icon: Icons.settings, label: '设置'),
        ],
        onTap: (index) {
          setState(() => _currentIndex = index);
          _pageController.jumpToPage(index);
          context.go(_routePaths[index]);
        },
      ),
    );
  }
}

方案二:基于GoRouter的refreshListenable同步状态

通过自定义ChangeNotifier监听路由变化,同步BottomNavBar和PageView的状态,适合更复杂的状态管理场景。

内容的提问来源于stack exchange,提问作者Karol Wiśniewski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 14:37:06