End

Flutter 陈航 21-路由 导航 Route Navigator 页面参数

本文地址


目录

21 | 路由与导航,实现页面切换

如果说 UI 框架的视图元素的基本单位是组件,那应用程序的基本单位就是页面了。对于拥有多个页面的应用程序而言,需要有一个统一的机制来管理页面之间的跳转,这个机制通常被称为路由管理导航管理

路由管理

在 Android/iOS 中,通常会初始化一个 Intent/ViewController,并通过 startActivity/pushViewController 打开一个新的页面;在 React 中,则使用 navigation 来管理所有页面,只要知道页面的名称,就可以导航到这个页面。

在 Flutter 中,页面之间的跳转是通过 RouteNavigator 来管理的:

  • Route:页面的抽象,主要负责创建界面、接收参数、响应 Navigator 的打开和关闭
  • Navigator:负责路由栈的管理,例如 Route 的入栈/出栈、替换栈内的某个 Route

根据是否需要提前注册页面标识符,Flutter 中的路由管理可以分为两种方式:

  • 基本路由:无需提前注册,在页面切换时需要自己构造页面实例
  • 命名路由:需要提前注册,在页面切换时通过页面标识符打开新的路由

基本路由

基本路由适用于应用中页面不多的场景。

类似 Android 中的显式意图,即直接指定要跳转的目标 Activity

在 Flutter 中,基本路由的使用方法和 Android/iOS 中打开新页面的方式非常相似:

  • 要导航到一个新页面,需要创建一个 Route,调用 Navigator.push 将新页面压到栈顶
  • 要返回到上一个页面,需要调用 Navigator.pop 方法,将栈顶的页面从路由栈中删除
// 导航到一个新页面
Route<HomePage> route = MaterialPageRoute(builder: (context) => const HomePage(title: "页面2"));
Navigator.push(context, route);

// 返回到上一个页面
Navigator.pop(context);

MaterialPageRoute 是一种路由模板,定义了路由创建、切换过渡动画的相关配置,可以实现与当前平台风格一致的路由切换动画

命名路由

在应用中页面较多的场景,Flutter 提供了另外一种方式来简化路由管理,即命名路由。

类似 Android 中的隐式意图,即不指定要跳转的目标 Activity,而是通过各种 Scheme 来匹配

命名路由最重要的作用,是建立了字符串标识符与各个页面之间的映射关系,使得各个页面之间完全解耦,应用内页面的切换只需要通过一个字符串标识符就可以搞定,为后期模块化打好基础。

路由表 routes

要想通过名字来切换页面,必须先给应用程序 MaterialApp 提供一个页面与名称的映射规则,即路由表 routes

路由表 routes 的类型是 Map<String, WidgetBuilder>

  • key:对应页面名字,定义好后,就可以使用 Navigator.pushNamed 来打开页面了
  • value:是一个 WidgetBuilder 回调函数,需要在这个函数中创建对应的页面

Flutter 提供了 onUnknownRoute 属性,可以对未知的路由标识符进行统一的页面跳转处理。

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

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

  @override
  Widget build(BuildContext context) => MaterialApp(
        home: const HomePage(title: "白乾涛"),
        routes: {
          "first_page": (context) => const HomePage(title: "first_page"),
          "second_page": (context) => const HomePage(title: "second_page"),
        },
        onUnknownRoute: (setting) => MaterialPageRoute(builder: (context) => const UnknownPage()),
      );
}

页面 Page

class HomePage extends StatefulWidget {
  final String title;

  const HomePage({super.key, required this.title});

  @override
  State<HomePage> createState() => HomePageState();
}

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

  @override
  Widget build(BuildContext context) => const Text("UnknownPage,统一的错误路由页");
}
class HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    Object? arguments = ModalRoute.of(context)?.settings.arguments; // 取出参数
    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: GestureDetector(
        child: Text("${widget.title}\narguments = $arguments"),
        onTap: () => Navigator.pop(context, "finished from ${widget.title}"), // 页面关闭时传递参数
      ),
      floatingActionButton: FloatingActionButton(onPressed: _onPressed),
    );
  }

  void _onPressed() {
    String routeName = "未知路由";
    if (widget.title == "白乾涛") {
      routeName = "first_page";
    } else if (widget.title == "first_page") {
      routeName = "second_page";
    }
    flog("widget.title = ${widget.title}, routeName = $routeName");
    //Navigator.pushNamed(context, routeName);
    var future = Navigator.pushNamed(context, routeName, arguments: "[from: ${widget.title}]"); // 页面启动时传递参数
    future.then((msg) => flog(msg.toString())); // 设置目标页面关闭时的监听函数
  }
}

页面参数

在打开路由时,可以传递相关参数,并在目标页面通过 RouteSettings 获取页面参数。

启动参数

// 命名路由 传递参数
Navigator.pushNamed(context, routeName, arguments: "[from: ${widget.title}]");
Navigator.of(context).pushNamed(routeName, arguments: "from ${widget.title}");

// 基本路由 传递参数
Route<HomePage> route = MaterialPageRoute(
  builder: (context) => const HomePage(title: "页面2"),
  settings: const RouteSettings(arguments: "[from: ${widget.title}]"),
);
Navigator.push(context, route);

// 取出参数
Object? arguments = ModalRoute.of(context)?.settings.arguments;

返回参数

与 Android 提供的 startActivityForResult 方法可以监听目标页面的处理结果类似,Flutter 也提供了返回参数的机制。 在 push 目标页面时,可以设置目标页面关闭时的监听函数,以获取目标页面在关闭路由时传递的参数。

// 设置目标页面关闭时的监听函数,以获取目标页面在关闭路由时传递的参数
Navigator.pushNamed(context, routeName).then((msg) => flog(msg.toString()));

// 在页面关闭时传递参数
Navigator.pop(context, "finished from ${widget.title}")

总结

Flutter 提供了基本路由和命名路由两种方式来管理页面间的跳转。其中,基本路由需要自己手动创建页面实例,通过 Navigator.push 完成页面跳转;而命名路由需要提前注册页面标识符和页面创建方法,通过 Navigator.pushNamed 传入标识符实现页面跳转。

Flutter 提供了页面打开与页面关闭的参数机制,可以在页面创建和目标页面关闭时,取出相应的参数。

2023-1-7

posted @ 2023-01-07 23:15  白乾涛  阅读(294)  评论(0编辑  收藏  举报