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

Flutter Web:如何用go_router实现点击NavigationRail改URL不跳转新页面

用GoRouter实现固定NavigationRail的页面切换效果

1. 引入依赖

在pubspec.yaml中添加go_router依赖:

dependencies:
  flutter:
    sdk: flutter
  go_router: ^latest_version # 替换为最新版本号

执行flutter pub get完成依赖安装。

2. 定义路由与页面组件

先创建几个示例页面,再配置GoRouter的路由表:

// 示例页面组件
class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(child: Text('首页'));
  }
}

class ComponentsPage extends StatelessWidget {
  const ComponentsPage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(child: Text('组件库'));
  }
}

class AboutPage extends StatelessWidget {
  const AboutPage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(child: Text('关于页面'));
  }
}

// GoRouter路由配置
final GoRouter _router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const MainLayout(),
      routes: [
        GoRoute(
          path: 'components',
          builder: (context, state) => const ComponentsPage(),
        ),
        GoRoute(
          path: 'about',
          builder: (context, state) => const AboutPage(),
        ),
      ],
    ),
  ],
);

3. 构建固定NavigationRail的主布局

核心用Row实现左右布局,左侧固定NavigationRail,右侧通过RouterOutlet承载路由页面,确保导航栏始终可见:

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

  @override
  State<MainLayout> createState() => _MainLayoutState();
}

class _MainLayoutState extends State<MainLayout> {
  int _selectedIndex = 0;
  final Map<String, int> _routeIndexMap = {
    '/': 0,
    '/components': 1,
    '/about': 2,
  };

  // 监听路由变化,同步导航栏选中状态
  void _syncNavigationIndex() {
    final currentRoute = GoRouter.of(context).location;
    setState(() {
      _selectedIndex = _routeIndexMap[currentRoute] ?? 0;
    });
  }

  @override
  void initState() {
    super.initState();
    GoRouter.of(context).addListener(_syncNavigationIndex);
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
          // 固定左侧的NavigationRail
          NavigationRail(
            selectedIndex: _selectedIndex,
            onDestinationSelected: (index) {
              setState(() => _selectedIndex = index);
              // 根据索引跳转对应路由
              switch (index) {
                case 0:
                  context.go('/');
                  break;
                case 1:
                  context.go('/components');
                  break;
                case 2:
                  context.go('/about');
                  break;
              }
            },
            destinations: const [
              NavigationRailDestination(
                icon: Icon(Icons.home_outlined),
                selectedIcon: Icon(Icons.home),
                label: Text('首页'),
              ),
              NavigationRailDestination(
                icon: Icon(Icons.widgets_outlined),
                selectedIcon: Icon(Icons.widgets),
                label: Text('组件'),
              ),
              NavigationRailDestination(
                icon: Icon(Icons.info_outlined),
                selectedIcon: Icon(Icons.info),
                label: Text('关于'),
              ),
            ],
          ),
          const VerticalDivider(thickness: 1, width: 1),
          // 右侧内容区域:路由页面容器
          Expanded(
            child: const RouterOutlet(),
          ),
        ],
      ),
    );
  }
}

4. 启动应用

在main函数中使用MaterialApp.router配置路由:

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Fixed NavigationRail Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      routerConfig: _router,
    );
  }
}

核心逻辑说明

  • 固定布局:通过Row+Expanded实现左侧导航栏固定、右侧内容区域自适应的布局结构。
  • 路由与导航同步:监听GoRouter的路由变化事件,自动更新NavigationRail的选中索引;点击导航项时调用context.go()切换路由,保证URL和选中状态一致。
  • URL同步更新:使用context.go()而非传统的Navigator.push,确保浏览器地址栏URL同步变化,支持地址栏直接导航和Deep Link功能。

内容的提问来源于stack exchange,提问作者SoulCRYSIS

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 04:35:36