Flutter 自定义命名路由跳转步骤

Flutter 自定义命名路由跳转步骤

Flutter 项目开发中,实现自定义命名路由跳转能让页面导航更灵活、易于管理。以下是详细的实现步骤,以及可能遇到的报错信息及处理方法。重点是我又不想使用第三方的路由插件,所以自己采用自带的路由 Navigator 实现,手写一个。

一、创建路由配置数据结构

  • 定义RouteConfig类:
    • 用于表示路由配置信息,包含路由名称、对应的页面构建函数以及参数相关配置。
    • 示例代码如下:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      // 路由配置类
      class RouteConfig {
      final String routeName;
      final Widget Function(BuildContext, Map<String, dynamic>?) pageBuilder;
      final List<String> requiredParams;

      RouteConfig({
      required this.routeName,
      required this.pageBuilder,
      this.requiredParams = const [],
      });
      }
  • 在这个结构中:
    • routeName:作为路由的唯一标识符,类似命名路由的名字,方便后续通过名称来查找和跳转。
    • pageBuilder:是一个函数,它接受BuildContext和可选的参数映射(用于传递数据),并返回对应的Widget,也就是要展示的页面。
    • requiredParams:是一个字符串列表,用于指定该路由跳转时必须要传递的参数名称,,方便做参数校验。

      二、管理路由配置列表

  • 定义RouteManager类:
    • 该类负责管理所有的路由配置,包括注册路由和生成路由。
    • 示例代码如下:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      // 路由管理类
      class RouteManager {
      static final List<RouteConfig> _routeConfigs = [];
      static final RouteConfig _defaultRouteConfig = RouteConfig(
      routeName: '/default',
      pageBuilder: (context, args) => DefaultPage(), // 这里假设存在一个默认页面 DefaultPage
      );

      // 注册路由的方法
      static void registerRoute(RouteConfig config) {
      _routeConfigs.add(config);
      }

      static Route<dynamic>? onGenerateRoute(RouteSettings settings) {
      final String? routeName = settings.name;
      if (routeName == null) {
      return null;
      }
      final RouteConfig config = _routeConfigs.firstWhere(
      (c) => c.routeName == routeName,
      orElse: () {
      throw ArgumentError(
      'Route configuration not found for route name: $routeName');
      },
      );
      final Map<String, dynamic>? args = settings.arguments as Map<String, dynamic>?;
      if (config.requiredParams.isNotEmpty) {
      for (String param in config.requiredParams) {
      if (!args!.containsKey(param)) {
      throw ArgumentError(
      'Missing required parameter: $param for route $routeName');
      }
      }
      }
      return MaterialPageRoute(
      builder: (context) => config.pageBuilder(context, args),
      settings: settings,
      );
      }
      }
  • 关键功能解释:
    • registerRoute 方法:用于将各个路由配置添加到内部的路由配置列表 _routeConfigs 中,模拟后台动态添加路由配置的过程。
    • onGenerateRoute 方法:会被 MaterialApp 调用,它根据传入的 RouteSettings(包含路由名称和参数等信息)来查找对应的路由配置,进行参数校验后构建并返回相应的 MaterialPageRoute 用于页面跳转展示。

三、定义页面及使用示例

  • 定义首页HomePage:

    • 作为应用的起始页面,通常包含导航到其他页面的按钮。
    • 示例代码如下:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      class HomePage extends StatelessWidget {
      @Override
      Widget build(BuildContext context) {
      return Scaffold(
      appBar: AppBar(
      title: Text('Home Page'),
      ),
      body: Center(
      child: ElevatedButton(
      onPressed: () {
      Map<String, dynamic> args = {
      'id': 123,
      'title': 'Some Detail',
      };
      Navigator.pushNamed(context, '/detail', arguments: args);
      },
      child: Text('Go to Detail Page'),
      ),
      ),
      );
      }
      }
  • 这里在点击按钮时,通过Navigator.pushNamed方法,使用命名路由'/detail'进行跳转,并传递了包含idtitle的参数。

  • 定义详情页DetailPage

    • 接收从首页传递过来的参数并展示。
    • 示例代码如下:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      class DetailPage extends StatelessWidget {
      @Override
      Widget build(BuildContext context) {
      final Map<String, dynamic>? args =
      ModalRoute.of(context)?.settings.arguments as Map<String, dynamic>?;
      final int? id = args?['id'];
      final String? title = args?['title'];
      return Scaffold(
      appBar: AppBar(
      title: Text('Detail Page'),
      ),
      body: Center(
      child: Text('Detail ID: $id, Title: $title'),
      ),
      );
      }
      }

      四、在主文件中使用路由配置

  • 注册路由:

    • main函数启动应用前,先调用RouteManagerregisterRoute方法注册所有需要的路由。
    • 示例代码如下:
      路由注册文件:route_registration.dart
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      import 'package:flutter/material.dart';
      import 'route_config.dart';
      import 'home_page.dart';
      import 'detail_page.dart';

      void registerRoutes() {
      // 注册首页路由配置
      RouteManager.registerRoute(
      RouteConfig(
      routeName: '/home',
      pageBuilder: (context, args) => HomePage(),
      ),
      );
      // 注册详情页路由配置
      RouteManager.registerRoute(
      RouteConfig(
      routeName: '/detail',
      pageBuilder: (context, args) => DetailPage(),
      requiredParams: ['id', 'title'],
      ),
      );
      // 这里假设存在一个默认页面 DefaultPage
      RouteManager.registerRoute(
      RouteConfig(
      routeName: '/default',
      pageBuilder: (context, args) => DefaultPage(),
      requiredParams: ['id', 'title'],
      ),
      );
      }
  • 配置MaterialApp

    • MyAppbuild方法中,将MaterialApponGenerateRoute属性指向RouteManageronGenerateRoute方法,以便在路由跳转时按照自定义配置进行处理。
    • 示例代码如下:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      import 'package:flutter/material.dart';
      import 'route_config.dart';
      import 'route_registration.dart';

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

      class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
      return MaterialApp(
      title: 'Dynamic Routes',
      onGenerateRoute: RouteManager.onGenerateRoute,
      initialRoute: '/home',
      );
      }
      }