Flutter路由终极指南:为什么90%的开发者都选错了?

68 阅读5分钟

如果你正在用 Flutter,却依然被“路由”折磨到想跑路——这篇就是为你写的。


图片

页面跳转写半天、传参像走钢丝、Web 端刷新一下直接 404……

别担心,你不是一个人。

今天,我们就把 Navigator 1.0、Navigator 2.0、GoRouter 的迷雾全部吹散。一次性打通 Flutter 路由任督二脉,从此页面跳转不再头疼!

🤯 为什么 Flutter 路由让人抓狂?

因为 Flutter 路由历史上发生过“三次进化”,导致网上教程新旧混杂,坑多到埋不完:

●Navigator 1.0 —— 简单粗暴,但功能弱鸡

●Navigator 2.0 —— 功能无敌,但复杂到劝退 99% 的人

●GoRouter —— 终于出现一个“人类可用”的官方方案

但现在的问题是:新手不知道用哪个?老项目不敢升级?Web 端 Link 更是压力山大?

这篇文章帮你彻底解决“到底选谁”的终极疑问。


❤️ Navigator 1.0:你入门 Flutter 的初恋

适合场景: 小项目、个人 Demo、纯粹的新手学习

这是大家最熟悉的代码:

●●● Dart// 基础使用 - 简单到哭
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => DetailsPage()),
);

// 命名路由 - 稍微优雅点
MaterialApp(
  routes: {
    '/': (context) => HomePage(),
    '/details': (context) => DetailsPage(),
  },
);
// 跳转时
Navigator.pushNamed(context, '/details');

✅ 优点:

●符合直觉,栈的操作(Push/Pop)。

●3分钟就能学会。

❌ 致命缺点:

●深层链接(Deep Link)无能为力: 你没法通过 URL 直接打开 App 的特定页面。

●Web 端体验极差: 浏览器的地址栏不会变,刷新一下直接回到首页,用户心态崩了。

●嵌套路由困难: 想做复杂的后台管理系统?难如登天。

🧠 Navigator 2.0:真正的强者,但学习门槛极高

为什么会出现 2.0?因为 Google 想让 Flutter 完美支持 Web。 你需要 URL 同步、浏览器前进后退、多 Tab 独立导航栈... 1.0 架构根本做不到。

于是 2.0 来了,但它的 API 设计极其抽象:

●●● Dart// 2.0的典型用法 - 确实复杂一些
MaterialApp.router(
  routeInformationParserMyRouteInformationParser(),
  routerDelegateMyRouterDelegate(),
);

// 但理解后会发现其强大之处
class MyRouterDelegate extends RouterDelegate<String>
    with PopNavigatorRouterDelegateMixin<String>, ChangeNotifier {
  final List<String> _pages = ['/home'];

  @override
  Widget build(BuildContext context) {
    returnNavigator(
      key: navigatorKey,
      pages: _pages.map((route) => 
        MaterialPage(
          keyKey(route),
          child_generatePage(route),
        )).toList(),
      onPopPage: (route, result) {
        if (!route.didPop(result)) returnfalse;
        _pages.removeLast();
        notifyListeners();
        returntrue;
      },
    );
  }
}

你需要手动管理路由栈的状态,代码量是 1.0 的十倍以上。

结论: 除非你是架构师,或者有极致的定制需求,否则千万别直接用原生的 Navigator 2.0,那是给自己找罪受。

🌟 GoRouter:Flutter 官方给全世界的礼物

Google 自己也发现 Navigator 2.0 太难用了,于是推出了 GoRouter。 它底层封装了 Navigator 2.0 的强大能力,表层却提供了极其简单的 API。

它就是目前 90% 项目的最优解。

1. 声明路由简单到哭

●●● Dart// 配置简单到令人发指
final GoRouter routerGoRouter(
  routes: [
    GoRoute(
      path'/',
      builder: (context, state) => constHomeScreen(),
    ),
    GoRoute(
      path'/details/:id',
      builder: (context, state) {
        final String id = state.pathParameters['id']!;
        returnDetailsScreen(id: id);
      },
    ),
  ],
);

2. 跳转极其优雅

●●● Dart// 不需要 MaterialPageRoute,也不需要 Context 传递 Widget
// 跳转简单直观
context.go('/details/123');  // 替换当前栈
context.push('/details/123'); // 推入新页面

3.嵌套路由?So easy!

●●● Dart// 底部导航栏轻松实现
StatefulShellRoute.indexedStack(
  builder: (context, state, navigationShell) {
    returnScaffoldWithNavBar(navigationShell: navigationShell);
  },
  branches: [
    // 首页分支
    StatefulShellBranch(routes: [
      GoRoute(path'/home', builder: (context, state) => HomeScreen()),
    ]),
    // 个人中心分支  
    StatefulShellBranch(routes: [
      GoRoute(path'/profile', builder: (context, state) => ProfileScreen()),
    ]),
  ],
)

🔥 真正的干货:GoRouter 实战必杀技

很多教程只讲基础,下面这两个功能才是 GoRouter 真正的“大杀器”。

⚔️ 杀手锏 1:一行代码搞定“底部导航保活”

这是新手最容易踩的坑:切换底部 Tab 时,页面被销毁了,滚动位置丢了! 以前你可能要用 PageStorage 或者 AutomaticKeepAliveClientMixin 折腾半天。

在 GoRouter 中,使用 StatefulShellRoute,直接起飞:

●●● Dart// 伪代码示例
StatefulShellRoute.indexedStack(
  builder: (context, state, navigationShell) {
    // 这里返回你的 Scaffold + BottomNavigationBar
    returnMainScreen(navigationShell: navigationShell);
  },
  branches: [
    // Tab 1: 首页
    StatefulShellBranch(
      routes: [GoRoute(path'/home', builder: (c, s) => HomePage())],
    ),
    // Tab 2: 个人中心
    StatefulShellBranch(
      routes: [GoRoute(path'/user', builder: (c, s) => UserPage())],
    ),
  ],
)

效果: 无论你怎么切换 Tab,页面状态完美保留,不需要写一行 KeepAlive 代码!

⚔️ 杀手锏 2:Web 端参数传递的“死亡陷阱”

❌ 错误写法(新手必踩): 很多同学喜欢用 extra 传对象:

●●● Dart// 跳转时传了一个 User 对象
context.go('/profile', extra: userObject);

💥 后果: 在 App 端没问题。但在 Web 端,用户按一下 F5 刷新,或者把链接分享给朋友打开。 extra 会变成 null,页面直接红屏报错! 因为对象不能存在 URL 字符串里。

✅ 正确写法: 永远通过 URL Path 或 Query Parameters 传递 ID,然后在页面内根据 ID 重新拉取数据。

●●● Dart// 推荐:将 ID 放在 URL 里
context.go('/profile/${user.id}');

// 目标页面拿到 ID 后,用 Riverpod/Bloc 去读缓存或请求网络

🎯 总结:到底应该怎么选?

别纠结了,我给你最实用的选择逻辑:

✅ 90% 的项目 → 直接锁死 GoRouter

●适用: 商业 App、电商、工具、社交、需要 Web 版、需要 Deep Link。

●理由: 官方维护,生态最好,功能最全,团队协作成本最低。

🟡 纯新手 / 只有2-3个页面的 Demo → Navigator 1.0

●适用: 学习 Dart 语言、写个计算器 Demo。

●理由: 别让路由配置消磨了你入门的热情。

🔥 巨型复杂应用 / 框架开发者 → Navigator 2.0

●适用: 你需要自己写一个路由框架,或者你的 App 导航逻辑反人类。

●理由: 只有它能提供原子级的控制力。


从今天开始,新项目请无脑选择 GoRouter。

它已经成为了 Flutter 开发的事实标准。哪怕你现在只开发 App,未来某天老板突然说:“我们要支持 Web 端分享”,用了 GoRouter 的你只需要微微一笑:“好的,已经支持了。”

这就是架构选型的远见。

🎤 你们项目现在的路由架构是什么?

🎤 有没有遇到过 “安卓返回键退出了 App 却没返回上一页” 的抓狂瞬间?

(觉得有用,记得点赞 分享 收藏,让更多 Flutter 兄弟不再踩坑!)

🎤 欢迎在评论区吐苦水!

所有文章微信公众号:flutter中文社区首发,更多最新文章关注微信公众号