Flutter: go_router 入门

Flutter: go_router 入门(面向 Android 开发者)

目标:用 Android 的路由思维,快速上手 Flutter 的 go_router,从依赖引入 → 初始化 → 路由定义 → 导航与参数 → 守卫/重定向 → 嵌套路由/ShellRoute → 错误页与深链。


1. 依赖引入(pubspec.yaml)

你的项目已存在 go_router(版本可能较低)。建议与现有项目版本保持一致;若新建工程,可使用较新版本:

yaml 复制代码
dependencies:
  go_router: ^14.2.0   # 或与你项目一致的版本
bash 复制代码
flutter pub get

2. 初始化 Router(在应用入口)

dart 复制代码
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

void main() => runApp(const MyApp());

final GoRouter _router = GoRouter(
  initialLocation: '/',
  routes: <RouteBase>[
    GoRoute(
      path: '/',
      name: 'home',
      builder: (context, state) => const HomePage(),
      routes: <RouteBase>[
        GoRoute(
          path: 'detail/:id', // 完整路径:/detail/:id
          name: 'detail',
          builder: (context, state) {
            final id = state.pathParameters['id']; // 路径参数
            final msg = state.uri.queryParameters['msg']; // 查询参数
            final extra = state.extra as String?; // 额外对象
            return DetailPage(id: id ?? '0', msg: msg, extra: extra);
          },
        ),
      ],
    ),
  ],
  errorBuilder: (context, state) => ErrorPage(error: state.error),
);

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router, // 新写法(旧版可用 routeInformationParser/Delegate)
      debugShowCheckedModeBanner: false,
      theme: ThemeData.light(),
    );
  }
}

3. 页面与导航(常用 API)

dart 复制代码
// HomePage → 导航到详情
class HomePage extends StatelessWidget {
  const HomePage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Home')),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            ElevatedButton(
              onPressed: () {
                // 路径导航(带路径参数与查询参数)
                context.go('/detail/100?msg=hello');
              },
              child: const Text('Go Detail (path + query)'),
            ),
            ElevatedButton(
              onPressed: () {
                // 命名导航 + 参数 + extra
                context.pushNamed(
                  'detail',
                  pathParameters: {'id': '200'},
                  queryParameters: {'msg': 'from-named'},
                  extra: 'EXTRA_PAYLOAD',
                );
              },
              child: const Text('Push Detail (named + extra)'),
            ),
          ],
        ),
      ),
    );
  }
}

class DetailPage extends StatelessWidget {
  final String id;
  final String? msg;
  final String? extra;
  const DetailPage({super.key, required this.id, this.msg, this.extra});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Detail #$id')),
      body: Center(child: Text('msg=$msg, extra=$extra')),
    );
  }
}
  • context.go():替换式导航(相当于 Android 的 replace/clearTop)。
  • context.push():入栈导航(类似 startActivity 入栈)。
  • context.pop():返回。

4. 参数传递(路径/查询/extra)

  • 路径参数:路由定义 path: 'detail/:id',读取 state.pathParameters['id']
  • 查询参数:/detail/1?msg=hi,读取 state.uri.queryParameters['msg']
  • Extra:适合传对象(比如复杂实体),读取 state.extra 并做类型断言。

5. 守卫与重定向(Auth、版本引导等)

最简示例(登录态守卫):

dart 复制代码
class AuthState extends ChangeNotifier {
  bool loggedIn = false;
}
final auth = AuthState();

final GoRouter router = GoRouter(
  refreshListenable: auth, // 登录态变化时触发重定向
  routes: [
    GoRoute(path: '/', builder: (_, __) => const HomePage()),
    GoRoute(path: '/login', builder: (_, __) => const LoginPage()),
    GoRoute(path: '/profile', builder: (_, __) => const ProfilePage()),
  ],
  redirect: (context, state) {
    final loggingIn = state.matchedLocation == '/login';
    if (!auth.loggedIn && !loggingIn) {
      // 未登录访问受限页 → 重定向到 /login
      return '/login';
    }
    if (auth.loggedIn && loggingIn) {
      // 已登录不该再看到登录页 → 回首页
      return '/';
    }
    return null; // 不重定向
  },
);

要点:

  • refreshListenable 可以监听状态变化(如登录态变更,自动触发路由刷新/重定向)。
  • redirect 返回 String?,返回非空字符串表示重定向目的地。

6. 嵌套路由与 ShellRoute(底部导航)

dart 复制代码
final router = GoRouter(
  routes: [
    ShellRoute(
      builder: (context, state, child) {
        return Scaffold(
          body: child,
          bottomNavigationBar: BottomNavigationBar(
            currentIndex: _indexByLocation(state.matchedLocation),
            onTap: (i) => _goTab(context, i),
            items: const [
              BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
              BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Me'),
            ],
          ),
        );
      },
      routes: [
        GoRoute(path: '/', builder: (_, __) => const HomePage()),
        GoRoute(path: '/me', builder: (_, __) => const MePage()),
      ],
    ),
  ],
);

int _indexByLocation(String loc) => loc.startsWith('/me') ? 1 : 0;
void _goTab(BuildContext ctx, int i) {
  if (i == 0) ctx.go('/'); else ctx.go('/me');
}
  • ShellRoute 允许共享外层 UI(如 BottomNavigationBar),内部切换子路由。

7. 错误页与 404

dart 复制代码
class ErrorPage extends StatelessWidget {
  final Exception? error;
  const ErrorPage({super.key, this.error});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Error')),
      body: Center(child: Text('Oops: ${error?.toString() ?? 'Not Found'}')),
    );
  }
}

8. 深度链接(Deep Link)与 Web 支持

  • MaterialApp.router + GoRouter 天然支持 Web URL。
  • 移动端可结合平台通道/第三方库接入 URI Scheme 或 App Links,然后 context.go() 到目标路径。

9. 常见排错清单

  • "页面不刷新/守卫不生效":确认 refreshListenable 正确传入并在状态变化时调用 notifyListeners()
  • "参数为空/拿不到":路径参数用 pathParameters,查询参数用 state.uri.queryParameters,对象用 extra
  • "返回行为异常":合理区分 go(替换)与 push(入栈),栈结构与返回键行为会不同。
  • "嵌套路由找不到页":确认父子 routes 嵌套关系与 path 写法(子路径不要以斜杠起始时会拼接到父路径后)。

10. 最小模板(可直接复制)

dart 复制代码
final router = GoRouter(
  routes: [
    GoRoute(path: '/', builder: (_, __) => const HomePage(), routes: [
      GoRoute(path: 'detail/:id', builder: (_, s) => DetailPage(id: s.pathParameters['id'] ?? '0')),
    ]),
  ],
);

class HomePage extends StatelessWidget {
  const HomePage({super.key});
  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(title: const Text('Home')),
        body: Center(
          child: ElevatedButton(
            onPressed: () => context.push('/detail/1?msg=hello'),
            child: const Text('Go Detail'),
          ),
        ),
      );
}

相关推荐
kirk_wang1 小时前
Flutter三方库鸿蒙适配实战:从原理到落地
flutter·移动开发·跨平台·arkts·鸿蒙
小a彤1 小时前
Flutter 跨平台开发框架详解
flutter
帅气马战的账号2 小时前
开源鸿蒙+Flutter:组件化驱动的跨端开发新范式
flutter
克喵的水银蛇2 小时前
Flutter 通用骨架屏封装实战:提升加载体验的 SkeletonWidget
flutter
子春一2 小时前
Flutter 测试体系全栈指南:从单元测试到 E2E,构建坚如磐石的高质量应用
flutter·单元测试
雨季6663 小时前
Flutter 智慧政务服务平台:跨端协同打造高效便民办事生态
flutter
500843 小时前
鸿蒙 Flutter 权限管理进阶:动态权限、权限组、兼容处理与用户引导
flutter·华为·架构·wpf·开源鸿蒙
stringwu4 小时前
Flutter PopScope:iOS左滑返回失效分析与方案探讨
flutter
500844 小时前
鸿蒙 Flutter 蓝牙与 IoT 开发进阶:BLE 设备连接、数据交互与设备管理
flutter·华为·electron·wpf·开源鸿蒙