zl程序教程

您现在的位置是:首页 >  工具

当前栏目

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

路由flutter 参数 页面 导航 21 Route navigator
2023-09-14 09:00:05 时间

本文地址


目录

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

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

我们首先需要知道目标页面对象,在完成目标页面初始化后,用框架提供的方式打开它。比如,在 Android/iOS 中我们通常会初始化一个 Intent 或 ViewController,通过 startActivity 或 pushViewController 来打开一个新的页面;而在 React 中,我们使用 navigation 来管理所有页面,只要知道页面的名称,就可以导航到这个页面。

路由管理

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

  • Route 是页面的抽象,主要负责创建对应的界面,接收参数,响应 Navigator 打开和关闭
  • Navigator 会维护一个路由栈管理 Route 的入栈/出栈,还可以直接替换栈内的某一个 Route

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

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

基本路由

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

  • 要导航到一个新页面,需要创建一个 Route,调用 Navigator.push 将新页面压到栈顶
  • 要返回到上一个页面,需要调用 Navigator.pop 方法,将栈顶的页面从堆栈中删除
class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text(widget.title)),
        body: Text(widget.title),
        floatingActionButton: FloatingActionButton(
          onPressed: _onPressed,
          child: Text(widget.title),
        ));
  }

  void _onPressed() {
    if (widget.title == "白乾涛") {
      Route<HomePage> route = MaterialPageRoute(builder: (context) => const HomePage(title: "页面2"));
      Navigator.push(context, route);
    } else {
      Navigator.pop(context);
    }
  }
}

MaterialPageRoute 是一种路由模板,定义了路由创建及切换过渡动画的相关配置,可以针对不同平台,实现与平台页面切换动画风格一致的路由切换动画。

命名路由

基本路由适用于应用中页面不多的场景。在应用中页面比较多的情况下,Flutter 提供了另外一种方式来简化路由管理,即命名路由。我们给页面起一个名字,然后就可以直接通过页面名字打开它了。

路由表 routes

要想通过名字来指定页面切换,我们必须先给应用程序 MaterialApp 提供一个页面名称映射规则,即路由表 routes,这样 Flutter 才知道名字与页面 Widget 的对应关系。

路由表 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: (RouteSettings 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())); // 设置目标页面关闭时的监听函数
  }
}

页面参数

Flutter 提供了路由参数的机制,可以在打开路由时传递相关参数,在目标页面通过 RouteSettings 获取页面参数。

启动参数

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

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

返回参数

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

下面的代码演示了如何获取参数:在 SecondPage 页面关闭时,传递了一个字符串参数,随后在上一页监听函数中,我们取出了这个参数,并将它展示了出来。

// 设置目标页面关闭时的监听函数
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