Skip to content

Flutter 路由导航

什么是路由导航

路由导航是指在应用中不同页面之间的切换。在 Flutter 中,路由导航是通过 Navigator 组件实现的。

基本导航

1. 命名路由

命名路由是在应用初始化时定义的路由,可以通过名称进行导航。

配置路由

dart
void main() {
  runApp(
    MaterialApp(
      initialRoute: '/',
      routes: {
        '/': (context) => HomePage(),
        '/details': (context) => DetailsPage(),
        '/settings': (context) => SettingsPage(),
      },
    ),
  );
}

导航到路由

dart
// 导航到新页面
Navigator.pushNamed(context, '/details');

// 带参数导航
Navigator.pushNamed(
  context, 
  '/details',
  arguments: 'Hello from home',
);

获取路由参数

dart
class DetailsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final String message = ModalRoute.of(context)!.settings.arguments as String;
    
    return Scaffold(
      appBar: AppBar(title: Text('Details')),
      body: Center(child: Text(message)),
    );
  }
}

2. 匿名路由

匿名路由是通过 MaterialPageRoute 直接创建的路由。

导航到新页面

dart
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => DetailsPage(),
  ),
);

// 带参数
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => DetailsPage(message: 'Hello from home'),
  ),
);

返回上一页

dart
Navigator.pop(context);

// 返回并带结果
Navigator.pop(context, 'Result from details');

接收返回结果

dart
// 导航并等待结果
final result = await Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => DetailsPage()),
);

// 使用结果
print('Result: $result');

路由替换

1. 替换当前路由

用新路由替换当前路由,这样用户就无法通过返回按钮回到当前页面。

dart
Navigator.pushReplacement(
  context,
  MaterialPageRoute(builder: (context) => NewPage()),
);

// 命名路由
Navigator.pushReplacementNamed(context, '/new');

2. 替换所有路由

替换整个路由栈,通常用于登录后跳转到主页面。

dart
Navigator.pushAndRemoveUntil(
  context,
  MaterialPageRoute(builder: (context) => MainPage()),
  (route) => false, // 移除所有之前的路由
);

// 命名路由
Navigator.pushNamedAndRemoveUntil(
  context,
  '/main',
  (route) => false,
);

路由动画

1. 自定义页面过渡动画

dart
Navigator.push(
  context,
  PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => DetailsPage(),
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      const begin = Offset(1.0, 0.0);
      const end = Offset.zero;
      const curve = Curves.ease;

      var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));

      return SlideTransition(
        position: animation.drive(tween),
        child: child,
      );
    },
  ),
);

2. 预定义动画

Flutter 提供了一些预定义的页面过渡动画:

  • MaterialPageRoute - 标准的 iOS 和 Android 过渡动画
  • CupertinoPageRoute - iOS 风格的过渡动画
dart
// iOS 风格动画
Navigator.push(
  context,
  CupertinoPageRoute(builder: (context) => DetailsPage()),
);

路由管理库

1. auto_route

auto_route 是一个功能强大的路由管理库,支持代码生成。

安装

bash
flutter pub add auto_route
flutter pub add --dev build_runner auto_route_generator

配置

dart
// 1. 定义路由
@MaterialAutoRouter(
  replaceInRouteName: 'Page,Route',
  routes: <AutoRoute>[
    AutoRoute(page: HomePage, initial: true),
    AutoRoute(page: DetailsPage),
    AutoRoute(page: SettingsPage),
  ],
)
class $AppRouter extends RootStackRouter {}

// 2. 生成路由代码
// 运行命令: flutter pub run build_runner build

// 3. 使用路由
class MyApp extends StatelessWidget {
  final _appRouter = AppRouter();

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerDelegate: _appRouter.delegate(),
      routeInformationParser: _appRouter.defaultRouteParser(),
    );
  }
}

// 4. 导航
context.router.push(DetailsRoute());

2. go_router

go_router 是 Flutter 官方推荐的路由管理库,使用声明式 API。

安装

bash
flutter pub add go_router

配置

dart
final goRouter = GoRouter(
  initialLocation: '/',
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => HomePage(),
    ),
    GoRoute(
      path: '/details/:id',
      builder: (context, state) {
        final id = state.params['id']!;
        return DetailsPage(id: id);
      },
    ),
    GoRoute(
      path: '/settings',
      builder: (context, state) => SettingsPage(),
    ),
  ],
);

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: goRouter,
    );
  }
}

// 导航
goRouter.push('/details/123');

// 带查询参数
goRouter.push('/details/123?name=flutter');

路由守卫

1. 实现路由守卫

路由守卫用于控制路由的访问权限,例如检查用户是否登录。

使用 go_router 实现

dart
final goRouter = GoRouter(
  initialLocation: '/',
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => HomePage(),
    ),
    GoRoute(
      path: '/login',
      builder: (context, state) => LoginPage(),
    ),
    GoRoute(
      path: '/profile',
      builder: (context, state) => ProfilePage(),
      redirect: (context, state) {
        // 检查用户是否登录
        if (!isLoggedIn) {
          return '/login';
        }
        return null; // 允许访问
      },
    ),
  ],
);

2. 全局路由守卫

dart
final goRouter = GoRouter(
  initialLocation: '/',
  redirect: (context, state) {
    // 全局路由守卫逻辑
    final isLoggedIn = checkIfLoggedIn();
    final isLoginPage = state.location == '/login';

    if (!isLoggedIn && !isLoginPage) {
      return '/login';
    }
    if (isLoggedIn && isLoginPage) {
      return '/';
    }
    return null;
  },
  routes: [
    // 路由定义
  ],
);

深度链接

1. 配置深度链接

Android 配置

AndroidManifest.xml 中添加:

xml
<activity
  android:name=".MainActivity"
  android:exported="true">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data
      android:scheme="myapp"
      android:host="example.com"
      android:pathPrefix="/details"
    />
  </intent-filter>
</activity>

iOS 配置

Info.plist 中添加:

xml
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myapp</string>
    </array>
    <key>CFBundleURLName</key>
    <string>com.example.myapp</string>
  </dict>
</array>

2. 处理深度链接

dart
final goRouter = GoRouter(
  initialLocation: '/',
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => HomePage(),
    ),
    GoRoute(
      path: '/details/:id',
      builder: (context, state) {
        final id = state.params['id']!;
        return DetailsPage(id: id);
      },
    ),
  ],
);

// 现在可以通过 myapp://example.com/details/123 打开应用并导航到详情页

导航最佳实践

  1. 使用命名路由:对于复杂应用,使用命名路由可以使代码更清晰
  2. 合理使用路由管理库:对于大型应用,考虑使用 auto_route 或 go_router
  3. 实现路由守卫:保护需要权限的页面
  4. 处理返回结果:正确处理页面间的返回结果
  5. 添加适当的过渡动画:提升用户体验
  6. 配置深度链接:支持从外部打开应用的特定页面

总结

Flutter 提供了强大的路由导航系统,支持多种导航方式和自定义动画。通过合理使用路由导航,可以创建流畅的用户体验。对于复杂应用,建议使用专门的路由管理库来简化路由管理。

基于 VitePress 的本地知识库