前面,我们已经知道如何简单在路由栈中 push、pop 实例,然而,当遇到一些特殊的情况,这显然不能满足需求。学习 Android 的同学知道 Activity 的各种启动模式可以完成相应需求,Flutter 当然也有类似的可以解决各种业务需求的实现方式!

请看下面使用方法与案例分析。

1.1 pushReplacementNamed 与 popAndPushNamed

RaisedButton(
onPressed: () {
Navigator.pushReplacementNamed(context, "/screen4");
},
child: Text("pushReplacementNamed"),
),
RaisedButton(
onPressed: () {
Navigator.popAndPushNamed(context, "/screen4");
},
child: Text("popAndPushNamed"),
),

我们在 Screen3 页面使用 pushReplacementNamed 与 popAndPushNamed 方法 push 了 Screen4。

此时路由栈情况如下:

Screen4 代替了 Screen3

pushReplacementNamed 与 popAndPushNamed 的区别在于: popAndPushNamed 能够执行 Screen2 弹出的动画与 Screen3 推进的动画而 pushReplacementNamed 仅显示 Screen3 推进的动画。

案例:

pushReplacementNamed:当用户成功登录并且现在在 HomeScreen 上时,您不希望用户还能够返回到 LoginScreen。因此,登录应完全由首页替换。另一个例子是从 SplashScreen 转到 HomeScreen。 它应该只显示一次,用户不能再从 HomeScreen 返回它。 在这种情况下,由于我们要进入一个全新的屏幕,我们可能需要借助此方法。

popAndPushNamed:假设您正在有一个 Shopping 应用程序,该应用程序在 ProductsListScreen 中显示产品列表,用户可以在 FiltersScreen 中应用过滤商品。 当用户单击“应用筛选”按钮时,应弹出 FiltersScreen 并使用新的过滤器值推回到 ProductsListScreen。 这里 popAndPushNamed 显然更为合适。

1.2 pushReplacementNamed 与 popAndPushNamed区别

pushReplacement和pushReplacementNamed的功能一致,它们二者的区别与push和pushNamed的区别一样——前者直接将页面入栈,后者通过路由命名的名字将页面入栈。这两对的使用方法也一致,不同的是pushReplacement和pushReplacementNamed不是讲新的页面直接入栈,而是替换掉栈顶的页面。类比Android原生可以理解为:在启动新的Activity时,finish()掉当前页面。

Navigator.of(context).pushReplacementNamed('/d');

1.3 pushNamedAndRemoveUntil

用户已经登陆进入 HomeScreen ,然后经过一系列操作回到配合只界面想要退出登录,你不能够直接 Push 进入 LoginScreen 吧?你需要将之前路由中的实例全部删除是的用户不会在回到先前的路由中。

pushNamedAndRemoveUntil 可实现该功能:

Navigator.of(context).pushNamedAndRemoveUntil('/screen4', (Route<dynamic> route) => false);

这里的 (Route<dynamic> route) => false 能够确保删除先前所有实例。

现在又有一个需求:我们不希望删除先前所有实例,我们只要求删除指定个数的实例。

我们有一个需要付款交易的购物应用。在应用程序中,一旦用户完成了支付交易,就应该从堆栈中删除所有与交易或购物车相关的页面,并且用户应该被带到 PaymentConfirmationScreen ,单击后退按钮应该只将它们带回到 ProductsListScreen 或 HomeScreen。

Navigator.of(context).pushNamedAndRemoveUntil('/screen4', ModalRoute.withName('/screen1'));

通过代码,我们推送 Screen4 并删除所有路由,直到 Screen1:

1.4 pushAndRemoveUntil和pushNamedAndRemoveUntil区别

两者的区别不在赘述,它们的实现的功能为:向栈添加新的路由,并删除所有先前的路由,直到路由为指定的路由为止——例如在退出登录时我们可以直接清除栈内的页面返回首页。用法如下:

Navigator.pushNamedAndRemoveUntil(context, "/a",ModalRoute.withName("/a"));

假如原来的页面顺序为a->b->c->d,执行完上述代码后为:a->a。

removeRoute 和removeRouteBelow区别

removeRoute表示从Navigator中删除路由,同时执行Route.dispose释放Route自身资源,路由的生命周期结束。
removeRouteBelow从Navigator中删除路由,同时执行Route.dispose操作,要替换的路由是传入参数anchorRouter里面的路由。

replace 和replaceRouteBelow

replace将Navigator中的路由替换成一个新路由。 replaceRouteBelow 将Navigator中的路由替换成一个新路由,要替换的路由是是传入参数anchorRouter里面的路由。

popUntil

popUntil作用是反复执行pop 直到返回到我们指定的页面为止,popUntil接收一个函数,等到该函数的参数predicate返回true为结束pop操作:

Navigator.popUntil(context, ModalRoute.withName('/a'));

假如原来的页面顺序为a->b->c->d,执行完上述代码后为:a。

该方法可以帮助我们清除栈内指定页面之上所有的页面,然后显示指定页面(例如快速回到主页)。

1.3 Popup routes(弹出路由)

路由不一定要遮挡整个屏幕。 PopupRoutes 使用 ModalRoute.barrierColor 覆盖屏幕,ModalRoute.barrierColor 只能部分不透明以允许当前屏幕显示。 弹出路由是“模态”的,因为它们阻止了对下面其他组件的输入。

有一些方法可以创建和显示这类弹出路由。 例如showDialogshowMenu和showModalBottomSheet。 如上所述,这些函数返回其推送路由的 Future(异步数据,参考下面的数据部分)。 执行可以等待返回的值在弹出路由时执行操作。

还有一些组件可以创建弹出路由,如PopupMenuButton和DropdownButton。这些组件创建 PopupRoute 的内部子类,并使用 Navigator 的push 和 pop 方法来显示和关闭它们。

1.4 自定义路由

您可以创建自己的一个窗口组件库路由类(如PopupRoute,ModalRoute或 PageRoute)的子类,以控制用于显示路径的动画过渡,路径的模态屏障的颜色和行为以及路径的其他各个特性。

PageRouteBuilder 类可以根据回调定义自定义路由。 下面是一个在路由出现或消失时旋转并淡化其子节点的示例。 此路由不会遮挡整个屏幕,因为它指定了opaque:false,就像弹出路由一样。

Navigator.push(context, PageRouteBuilder(
opaque: false,
pageBuilder: (BuildContext context, _, __) {
return Center(child: Text('My PageRoute'));
},
transitionsBuilder: (___, Animation<double> animation, ____, Widget child) {
return FadeTransition(
opacity: animation,
child: RotationTransition(
turns: Tween<double>(begin: 0.5, end: 1.0).animate(animation),
child: child,
),
);
}
)); 

路由两部分构成,“pageBuilder”和“transitionsBuilder”。

该页面成为传递给 buildTransitions 方法的子代的后代。 通常,页面只构建一次,因为它不依赖于其动画参数(在此示例中以_和__表示)。 过渡是建立在每个帧的持续时间。

1.5 嵌套路由

一个应用程序可以使用多个路由导航器。将一个导航器嵌套在另一个导航器下方可用于创建“内部旅程”,例如选项卡式导航,用户注册,商店结帐或代表整个应用程序子部分的其他独立个体。

iOS应用程序的标准做法是使用选项卡式导航,其中每个选项卡都维护自己的导航历史记录。因此,每个选项卡都有自己的导航器,创建了一种“并行导航”。

除了选项卡的并行导航之外,还可以启动完全覆盖选项卡的全屏页面。例如:入职流程或警报对话框。因此,必须存在位于选项卡导航上方的“根”导航器。因此,每个选项卡的 Navigators 实际上都是嵌套在一个根导航器下面的Navigators。

用于选项卡式导航的嵌套导航器位于 WidgetApp 和 CupertinoTabView 中,因此在这种情况下您无需担心嵌套的导航器,但它是使用嵌套导航器的真实示例。

以下示例演示了如何使用嵌套的 Navigator 来呈现独立的用户注册过程。

尽管此示例使用两个 Navigators 来演示嵌套的 Navigators,但仅使用一个 Navigato r就可以获得类似的结果。

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// ...some parameters omitted...
// MaterialApp contains our top-level Navigator
initialRoute: '/',
routes: {
'/': (BuildContext context) => HomePage(),
'/signup': (BuildContext context) => SignUpPage(),
},
);
}
} class SignUpPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// SignUpPage builds its own Navigator which ends up being a nested
// Navigator in our app.
return Navigator(
initialRoute: 'signup/personal_info',
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
switch (settings.name) {
case 'signup/personal_info':
// Assume CollectPersonalInfoPage collects personal info and then
// navigates to 'signup/choose_credentials'.
builder = (BuildContext _) => CollectPersonalInfoPage();
break;
case 'signup/choose_credentials':
// Assume ChooseCredentialsPage collects new credentials and then
// invokes 'onSignupComplete()'.
builder = (BuildContext _) => ChooseCredentialsPage(
onSignupComplete: () {
// Referencing Navigator.of(context) from here refers to the
// top level Navigator because SignUpPage is above the
// nested Navigator that it created. Therefore, this pop()
// will pop the entire "sign up" journey and return to the
// "/" route, AKA HomePage.
Navigator.of(context).pop();
},
);
break;
default:
throw Exception('Invalid route: ${settings.name}');
}
return MaterialPageRoute(builder: builder, settings: settings);
},
);
}
}

Navigator.of 在给定 BuildContext 中最近的根 Navigator 上运行。 确保在预期的 Navigator 下面提供BuildContext,尤其是在创建嵌套 Navigators 的大型构建方法中。 Builder 组件可用于访问组件子树中所需位置的 BuildContext。

2. 页面间数据传递

2.1 数据传递

在上面的大多数示例中,我们推送新路由时没有发送数据,但在实际应用中这种情况应用很少。 要发送数据,我们将使用 Navigator 将新的 MaterialPageRoute 用我们的数据推送到堆栈上(这里是 userName)

String userName = "John Doe";
Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) =>
new Screen5(userName)));

要在 Screen5 中得到数据,我们只需在 Screen5 中添加一个参数化构造函数:

class Screen5 extends StatelessWidget {

  final String userName;
Screen5(this.userName);
@override
Widget build(BuildContext context) {
print(userName)
...
}
}

这表示我们不仅可以使用 MaterialPageRoute 作为 push 方法,还可以使用 pushReplacement ,pushAndPopUntil 等。基本上从我们描述的上述方法中路由方法,第一个参数现在将采用 MaterialPageRoute 而不是 namedRoute 的 String。

2.2 数据返回

我们可能还想从新页面返回数据。 就像一个警报应用程序,并为警报设置一个新音调,您将显示一个带有音频音调选项列表的对话框。 显然,一旦弹出对话框,您将需要所选的项目数据。 它可以这样实现:

new RaisedButton(onPressed: ()async{
String value = await Navigator.push(context, new MaterialPageRoute<String>(
builder: (BuildContext context) {
return new Center(
child: new GestureDetector(
child: new Text('OK'),
onTap: () { Navigator.pop(context, "Audio1"); }
),
);
}
)
);
print(value); },
child: new Text("Return"),)

在 Screen4 中尝试并检查控制台的打印值。

另请注意:当路由用于返回值时,路由的类型参数应与 pop 的结果类型匹配。 这里我们需要一个 String 数据,所以我们使用了 MaterialPageRoute <String>。 不指定类型也没关系。

3. 其他效果解释

3.1 maybePop

源码:

static Future<bool> maybePop<T extends Object>(BuildContext context, [ T result ]) {
return Navigator.of(context).maybePop<T>(result);
} @optionalTypeArgs
Future<bool> maybePop<T extends Object>([ T result ]) async {
final Route<T> route = _history.last;
assert(route._navigator == this);
final RoutePopDisposition disposition = await route.willPop();
if (disposition != RoutePopDisposition.bubble && mounted) {
if (disposition == RoutePopDisposition.pop)
pop(result);
return true;
}
return false;
}

如果我们在初始路由上并且有人错误地试图弹出这个唯一页面怎么办? 弹出堆栈中唯一的页面将关闭您的应用程序,因为它后面已经没有页面了。这显然是不好的体验。 这就是 maybePop() 起的作用。 点击 Screen1 上的 maybePop 按钮,没有任何效果。 在 Screen3 上尝试相同的操作,可以正常弹出。

这种效果也可通过 canPop 实现:

3.2 canPop

源码:

static bool canPop(BuildContext context) {
final NavigatorState navigator = Navigator.of(context, nullOk: true);
return navigator != null && navigator.canPop();
} bool canPop() {
assert(_history.isNotEmpty);
return _history.length > 1 || _history[0].willHandlePopInternally;
}

如果占中实例大于 1 或 willHandlePopInternally 属性为 true 返回 true,否则返回 false。

我们可以通过判断 canPop 来确定是否能够弹出该页面。

3.3 如何去除默认返回按钮

AppBar({
Key key,
this.leading,
this.automaticallyImplyLeading = true,
this.title,
this.actions,
this.flexibleSpace,
this.bottom,
this.elevation = 4.0,
this.backgroundColor,
this.brightness,
this.iconTheme,
this.textTheme,
this.primary = true,
this.centerTitle,
this.titleSpacing = NavigationToolbar.kMiddleSpacing,
this.toolbarOpacity = 1.0,
this.bottomOpacity = 1.0,
}) : assert(automaticallyImplyLeading != null),
assert(elevation != null),
assert(primary != null),
assert(titleSpacing != null),
assert(toolbarOpacity != null),
assert(bottomOpacity != null),
preferredSize = Size.fromHeight(kToolbarHeight + (bottom?.preferredSize?.height ?? 0.0)),
super(key: key);

将 automaticallyImplyLeading置为 false

flutter详解路由栈(二)的更多相关文章

  1. JavaScript学习笔记-实例详解-类(二)

    实例详解-类(二)   //===给Object.prototype添加只读\不可枚举\不可配置的属性objectId(function(){ Object.defineProperty(Object ...

  2. Android开发:文本控件详解——TextView(二)文字跑马灯效果实现

    一.需要使用的属性: 1.android:ellipsize 作用:若文字过长,控制该控件如何显示. 对于同样的文字“Android开发:文本控件详解——TextView(二)文字跑马灯效果实现”,不 ...

  3. Flutter中管理路由栈的方法和应用

    原文地址:https://www.jianshu.com/p/5df089d360e4 本文首先讲的Flutter中的路由,然后主要讲下Flutter中栈管理的几种方法. 了解下Route和Navig ...

  4. 【Java入门提高篇】Day30 Java容器类详解(十二)TreeMap详解

    今天来看看Map家族的另一名大将——TreeMap.前面已经介绍过Map家族的两名大将,分别是HashMap,LinkedHashMap.HashMap可以高效查找和存储元素,LinkedHashMa ...

  5. CocoaPods详解之(二)----进阶篇

    CocoaPods详解之----进阶篇 作者:wangzz 原文地址:http://blog.csdn.net/wzzvictory/article/details/19178709 转载请注明出处 ...

  6. Java源码详解系列(十二)--Eureka的使用和源码

    eureka 是由 Netflix 团队开发的针对中间层服务的负载均衡器,在微服务项目中被广泛使用.相比 SLB.ALB 等负载均衡器,eureka 的服务注册是无状态的,扩展起来非常方便. 在这个系 ...

  7. [转]iOS学习之UINavigationController详解与使用(二)页面切换和segmentedController

    转载地址:http://blog.csdn.net/totogo2010/article/details/7682433 iOS学习之UINavigationController详解与使用(一)添加U ...

  8. dom对象详解--document对象(二)

       dom对象详解--style对象 style对象 style对象和document对象下的集合对象styleSheets有关系,styleSheets是文档中所有style对象的集合,这里讲解的 ...

  9. MySQL存储过程详解 mysql 存储过程(二)

    mysql存储过程详解 1.      存储过程简介 我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然后执行,而存储过程(Stored Procedure)是一组为了完成特定功能的SQL ...

随机推荐

  1. ubuntu16.04安装cuDNN

    cudnn的安装非常简单 (1)下载安装文件 按需求下载cudnn的安装文件:https://developer.nvidia.com/rdp/cudnn-archive Tar File的下载如下图 ...

  2. cyopen注释掉导入的动态函数

    cyopen注释掉导入的动态函数 cyopen注释掉导入的动态函数 cyopen注释掉导入的动态函数

  3. LightOJ-1027-A Dangerous Maze(概率)

    链接: https://vjudge.net/problem/LightOJ-1027#author=634579757 题意: You are in a maze; seeing n doors i ...

  4. 04-01 Django之模板层

    Django之模板层 一 模板简介 在刚刚介绍完的视图层中我们提到,浏览器发送的请求信息会转发给视图函数进行处理,而视图函数在经过一系列处理后必须要有返回信息给浏览器.如果我们要返回html标签.cs ...

  5. C/C++数据类型判断与转换

    最近总想着写一些通用的代码,然集中收纳到自己的私人库中去,这些代码期望能与公司基础数据结构无关.然而这比较难,因为无论如何,必需要用到一些结构 化的东西,这些与基础引擎等有关,必需极度抽象才可以做到层 ...

  6. Java验证身份证是否合法

    import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.GregorianCalendar; im ...

  7. js 数组的forEach 函数

    var numbers = [4, 9, 16, 25]; function myFunction(item, index) { console.log("item:" + ite ...

  8. Python天天学_05_模块

    Python_day_05 金角大王: http://www.cnblogs.com/alex3714/articles/5161349.html ------Python是一个优雅的大姐姐 学习方式 ...

  9. HGOI20190811 省常中互测4

    Problem A magic 给出一个字符串$S$,和数字$n$,要求构造长度为$n$只含有小写字母的字符串$T$, 使得在$T$中存在删除且仅删除一个子串使得$S=T$成立. 输出$T$的构造方案 ...

  10. xwiki使用中的问题

    xwiki 内存限制 问题重现: xwiki启动后内存.cpu一直上涨,不回落,启动后服务访问速度越来越慢,最后无法访问 分析: xwiki在启动时会消耗大量内存和cpu,增加tomcat最大内存限制 ...