Flutter路由管理和页面参数的传递(获取&返回)

前言

在做 Flutter 开发的时候所有的页面以及页面上的元素都变成了 Widget ,创建一个页面或者视图直接 new 一个新的 widget 就可以,相关的参数我们可以直接通过构造函数直接传递。

我们做 Android 开发的人员都知道 Android 应用程序在进行页面跳转的时候可以利用Intent进行参数传递,那么再开发 Flutter 的时候有类似的方式可以进行参数传递么?答案当然是有。

Flutter中文网 中有一段话,大多数应用程序包含多个页面。例如,我们可能有一个显示产品的页面,然后,用户可以点击产品,跳到该产品的详情页。

在Android中,页面对应的是Activity,在iOS中是ViewController。而在Flutter中,页面只是一个widget!

在Flutter中,我们那么我们可以使用Navigator在页面之间跳转。

所以我们下边讲述 widget 的参数传递,从简单到简便:

widget构造参数传递

route参数传递

上面两种方式进混合(onGenerateRoute)

widget构造参数传递

class Page extends StatelessWidget{
  Page({this.arguments});
  final Map arguments;

  @override
  Widget build(BuildContext context) {
    return Material(
      child: Center(
        child: Text("this page name is ${arguments != null ? arguments['name'] : 'null'}"),
      ),
    );
  }
}

上面是一个简单的 Flutter 的视图组件,我们在使用参数 arguments 的时候只需要将其传入到 Page({this.arguments}) 的构造函数中。

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Page(arguments: {"name": 'Flutter Demo Home Page'}),
    );
  }
}

这种方式进行的参数传递只能单向往下一个页面传递,不能像Android的 setResult 一样往上一级页面传递数据。

Route

在讲 Route 传参的时候,我们先讲讲 FlutterRoute 相关的知识点。

路由( Route )在移动开发中通常指页面( Page ),这跟 web 开发中单页应用的 Route 概念意义是相同的,RouteAndroid 中通常指一个 Activity ,在 iOS 中指一个 ViewController 。所谓路由管理,就是管理页面之间如何跳转,通常也可被称为导航管理。这和原生开发类似,无论是 Android 还是 iOS ,导航管理都会维护一个路由栈,路由入栈( push )操作对应打开一个新页面,路由出栈( pop)操作对应页面关闭操作,而路由管理主要是指如何来管理路由栈。

MaterialPageRoute

MaterialPageRoute 是我们使用最为广泛的路由类,它继承自 PageRoute 类, PageRoute 类是一个抽象类继承抽象类 ModalRoute,下面我们介绍一下 MaterialPageRoute 构造函数的各个参数的意义:

MaterialPageRoute({
  @required this.builder,
  RouteSettings settings,
  this.maintainState = true,
  bool fullscreenDialog = false,
}) : assert(builder != null),
      assert(maintainState != null),
      assert(fullscreenDialog != null),
      assert(opaque),
      super(settings: settings, fullscreenDialog: fullscreenDialog);
  • builder 是一个WidgetBuilder类型的回调函数,它的作用是构建路由页面的具体内容,返回值是一个widget。我们通常要实现此回调,返回新路由的实例。
  • settings 包含路由的配置信息,如路由名称、路由参数、是否初始路由(首页)。
  • maintainState:默认情况下,当入栈一个新路由时,原来的路由仍然会被保存在内存中,如果想在路由没用的时候释放其所占用的所有资源,可以设置maintainState为false。
  • fullscreenDialog表示新的路由页面是否是一个全屏的模态对话框,在iOS中,如果fullscreenDialogtrue,新页面将会从屏幕底部滑入(而不是水平方向)。

如果想自定义路由切换动画,可以自己继承PageRoute来实现,我们将在后面介绍动画时,实现一个自定义的路由Widget。

命名路由

所谓命名路由(Named Route)即给路由起一个名字,然后可以通过路由名字直接打开新的路由。这为路由管理带来了一种直观、简单的方式。和 Android 中的 ARrouter 页面跳转框架所定义的 path 非常的类似。

路由表

要想使用命名路由,我们必须先提供并注册一个路由表(routing table),这样应用程序才知道哪个名称与哪个路由Widget对应。路由表的定义是一个 Map<String, WidgetBuilder> 结构的 Map , key 为路由的名称,是个字符串;value是个builder回调函数,用于生成相应的路由Widget。我们在通过路由名称入栈新路由时,应用会根据路由名称在路由表中找到对应的WidgetBuilder回调函数,然后调用该回调函数生成路由widget并返回。

我们在创建 MaterialApp 的时候就有一个 routes 构造参数:

const MaterialApp({
  Key key,
  this.navigatorKey,
  this.home,
  this.routes = const <String, WidgetBuilder>{},
  this.initialRoute,
  this.onGenerateRoute,
  this.onUnknownRoute,
  this.navigatorObservers = const <NavigatorObserver>[],
  /***********/
})

Navigator

Navigator是一个路由管理的widget,它通过一个栈来管理一个路由widget集合。通常当前屏幕显示的页面就是栈顶的路由。Navigator提供了一系列方法来管理路由栈,我们主要使用 pushpop 连个操作进行页面的入栈和出栈。

push

将给定的路由入栈(即打开新的页面),返回值是一个Future对象,用以接收新路由出栈(即关闭)时的返回数据。

push 我们主要使用两个方法一个是直接 push 一个路由,另外一个是 pushNamed 一个命名路由地址(PS:要想使用命名路由必须提供并注册一个路由表,这后面会讲到)。

push方法源码

下边是 Navigator.push 的源码,入参的 Route 对象中有一个 RouteSettings 成员变量,我们可以在构造 Route 对象的时候将需要传递的参数放在 RouteSettings 中。

@optionalTypeArgs
static Future<T> push<T extends Object>(BuildContext context, Route<T> route) {
  return Navigator.of(context).push(route);
}

push方法使用

我们可以将参数放在 SecondScreen 的构造函数中,也可以放在构造的 MaterialPageRouteRouteSettings 中。

Navigator.push(
  context,
  new MaterialPageRoute(builder: (context) => new SecondScreen()),
).then((data){
  //接受返回的参数
  print(data.toString());
};

pushNamed方法源码

第二种方式最终的实现也是调用的 push 方法,这中方法直接暴露了参数 Object arguments

@optionalTypeArgs
static Future<T> pushNamed<T extends Object>(
  BuildContext context,
  String routeName, {
  Object arguments,
  }) {
  return Navigator.of(context).pushNamed<T>(routeName, arguments: arguments);
}
@optionalTypeArgs
Future<T> pushNamed<T extends Object>(
  String routeName, {
  Object arguments,
}) {
  return push<T>(_routeNamed<T>(routeName, arguments: arguments));
}

pushNamed方法使用

使用前提是 /route1 已经被注册到路由表中:

Navigator.of(context)
  .pushNamed(
    '/route1',
    arguments: {
      "name": 'hello'
    }
    ).then((data){
    //接受返回的参数
    print(data.toString());
    };

pop

将栈顶路由出栈,入参为一个 object 类型的对象为当前页面关闭时返回给上一个页面的数据。

@optionalTypeArgs
static bool pop<T extends Object>(BuildContext context, [ T result ]) {
  return Navigator.of(context).pop<T>(result);
}

使用非常简单:

Navigator.of(context).pop("ok~!");

页面参数的传输、获取以及结果返回

参数传输

Navigator.of(context).pushNamed('/route1', arguments: {"name": 'hello'});

参数获取

class Page extends StatelessWidget{
  String name;
  @override
  Widget build(BuildContext context) {
    dynamic obj = ModalRoute.of(context).settings.arguments;
    if (obj != null && isNotEmpty(obj["name"])) {
      name = obj["name"];
    }
    return Material(
      child: Center(
        child: Text("this page name is ${name}"),
      ),
    );
  }
}

参数返回

//页面返回参数
Navigator.of(context).pop("ok~!");
//上一个页面接收参数
Navigator.of(context)
  .pushNamed(
    '/route1',
    arguments: {
      "name": 'hello'
    }
    ).then((data){
    //接受返回的参数
    print(data.toString());
    };

onGenerateRoute构建路由

在说 onGenerateRoute 构建路由之前,我们得先了解他。前面 MaterialApp 的的构造函数中我们看到过它出现, MaterialApp 有一个参数类型为 Function 类型的 onGenerateRoute

void main() => runApp(MyApp());
Map<String, WidgetBuilder> routers = {'/route1': (context, {arguments}) => Page(arguments: arguments)}
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      // 处理Named页面跳转 传递参数
      onGenerateRoute: (RouteSettings settings) {
        // 统一处理
        final String name = settings.name;
        final Function pageContentBuilder = routers[name];
        if (pageContentBuilder != null) {
          final Route route =
          MaterialPageRoute(
                builder: (context) {
                  //将RouteSettings中的arguments参数取出来,通过构造函数传入
                  return pageContentBuilder(context, arguments: settings.arguments);
                },
                settings: settings,
            );
          return route;
        }
      },
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(name: 'Flutter Demo Home Page'),
      //routes优先执行,所以必须注释掉,否则onGenerateRoute方法不会调用
      //routes: routers,
    );
  }
}
class Page extends StatelessWidget{
  Page({this.arguments});
  final Map arguments;
  @override
  Widget build(BuildContext context) {
    return Material(
      child: Center(
        child: Text("this page name is ${arguments != null ? arguments['name'] : 'null'}"),
      ),
    );
  }
}
  • 这种方式统一处理了页面的 arguments 参数,所以必须保证 Map<String, WidgetBuilder> routers 当中注册的所有 Widget 的构造函数中都有一个 Map 类型并且名为 arguments 的参数。
  • 这种方法同时也传递了 RouteSettings ,所以在下一个页面我们也可以通过 ModalRoute.of(context).settings.arguments 方式获取参数。
  • 这种方式可以自定义 PageRoute 的类型,比如自带 IOS 侧滑返回效果的 CupertinoPageRoute 等。之前通过在 WidgetsApp 注册routes 的方式默认生成的 PageRoute 类型为 MaterialPageRoute

源码分析传送门:Flutter路由管理和页面参数的传递(源码分析)

文章到这里就全部讲述完啦,若有其他需要交流的可以留言哦

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容