共计 5153 个字符,预计需要花费 13 分钟才能阅读完成。
老孟导读 :Flutter 中路由是十分重要的局部,任何一个应用程序都离不开路由治理,此文解说路由相干办法的应用和 路由堆栈 的变动。
Flutter 路由治理中有两个十分重要的概念:
- Route:路由是应用程序 页面 的形象,对应 Android 中 Activity 和 iOS 中的 ViewController,由 Navigator 治理。
- Navigator:Navigator 是一个组件,治理和保护一个基于 堆栈 的历史记录,通过 push 和 pop 进行页面的跳转。
push 和 pop
假如当初有 2 个页面 A 和 B,A 中有一个按钮,点击跳转到 B 页面,A 页面代码:
class APage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
child: RaisedButton(child: Text('A 页面'),
onPressed: () {Navigator.of(context).push(MaterialPageRoute(builder: (context) {return BPage();
}));
},
),
);
}
}
B 页面代码:
class BPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.center,
child: RaisedButton(child: Text('B 页面'),
onPressed: () {},
),
),
);
}
}
当应用程序位于 A 页面时,路由堆栈 中只有 A,点击按钮跳转到 B 页面,路由堆栈中有 B 和 A,且 B 处于栈顶。
点击 B 页面的按钮返回到 A 页面,批改 B 页面按钮点击事件:
RaisedButton(child: Text('B 页面'),
onPressed: () {Navigator.of(context).pop();},
)
路由堆栈的变动:
下面案例的成果是从 B 页面跳转到 A 页面,那是否也能够应用 push 办法?批改 B 页面按钮点击事件:
RaisedButton(child: Text('B 页面'),
onPressed: () {Navigator.of(context).push(MaterialPageRoute(builder: (context) {return APage();
}));
},
)
从成果上看也能够跳转到 A 页面,路由堆栈:
那是否能够应用 push 代替 pop 呢? 答案必定是不能够的,
- 试想如下场景,进入购物 App,展现购物列表,点击其中一个进入商品具体页面,应用 push 再次进入购物列表,而后在进入商品具体页面 …,如此重复,路由堆栈中将会寄存大量的购物列表和商品具体页面的路由,点击返回按钮,会将重复显示购物列表和商品具体页面。
- 页面切换时路由动画 push 和 pop 是不同。
maybePop 和 canPop
下面案例如果点击 A 页面按钮间接调用 pop 会如何?
RaisedButton(child: Text('A 页面'),
onPressed: () {Navigator.of(context).pop();},
)
在 A 页面时路由堆栈中只有 A,调用 pop 后,路由堆栈变动:
此时路由堆栈为空,没有可显示的页面,应用程序将会退出或者黑屏,好的用户体验不应如此,此时能够应用 maybePop,maybePop 只在路由堆栈有可弹前途由时才会弹前途由。
下面的案例在 A 页面执行 maybePop:
RaisedButton(child: Text('A 页面'),
onPressed: () {Navigator.of(context).maybePop();},
)
点击后不会呈现弹前途由,因为以后路由堆栈中只有 A,在 B 页面执行 maybePop,将会返回到 A 页面。
也能够通过 canPop 判断以后是否能够 pop:
RaisedButton(child: Text('B 页面'),
onPressed: () {if(Navigator.of(context).canPop()){Navigator.of(context).pop();}
},
)
pushNamed
pushNamed 是命名路由的形式,须要在 MaterialApp 中配置路由名称:
MaterialApp(
title: 'Flutter Demo',
routes: <String, WidgetBuilder>{'/A': (context) => APage(),
'/B': (context) => BPage(),},
home: Scaffold(body: APage(),
),
)
从 A 跳转到 B:
RaisedButton(child: Text('A 页面'),
onPressed: () {Navigator.of(context).pushNamed('/B');
},
)
pushReplacementNamed 和 popAndPushNamed
有 A、B、C 三个页面,A 页面通过 pushNamed 跳转到 B:
RaisedButton(child: Text('A 页面'),
onPressed: () {Navigator.of(context).pushNamed('/B');
},
)
B 通过 pushReplacementNamed 跳转到 C:
RaisedButton(child: Text('B 页面'),
onPressed: () {Navigator.of(context).pushReplacementNamed('/C');
},
)
点击 C 页面按钮执行 pop:
RaisedButton(child: Text('C 页面'),
onPressed: () {if(Navigator.of(context).canPop()){Navigator.of(context).pop();}
},
)
点击 C 页面按钮间接返回到了 A 页面,而不是 B 页面,因为 B 页面应用 pushReplacementNamed 跳转,路由堆栈变动:
B 页面跳转到 C 页面,应用 popAndPushNamed:
RaisedButton(child: Text('B 页面'),
onPressed: () {Navigator.of(context).popAndPushNamed('/C');
},
)
popAndPushNamed 路由堆栈和 pushReplacementNamed 是一样,惟一的区别就是 popAndPushNamed 有 B 页面退出动画。
popAndPushNamed 和 pushReplacementNamed 使以后页面不在路由堆栈中,所以通过 pop 无奈返回此页面。
实用场景:
- 欢送页面:应用程序关上后首先进入欢送界面,而后进入首页,进入首页后不应该再进入欢送界面。
- 登录页面:登录胜利后进入相干页面,此时按返回按钮,不应再进入登录页面。
pushNamedAndRemoveUntil
有如下场景,应用程序进入首页,点击登录进入登录页面,而后进入注册页面或者遗记明码页面 …,登录胜利后进入其余页面,此时不心愿返回到登录相干页面,此场景能够应用 pushNamedAndRemoveUntil。
有 A、B、C、D 四个页面,A 通过 push 进入 B 页面,B 通过 push 进入 C 页面,C 通过 pushNamedAndRemoveUntil 进入 D 页面同时删除路由堆栈中直到 /B 的路由,C 页面代码:
RaisedButton(child: Text('C 页面'),
onPressed: () {Navigator.of(context).pushNamedAndRemoveUntil('/D', ModalRoute.withName('/B'));
},
),
D 页面按钮执行 pop:
RaisedButton(child: Text('D 页面'),
onPressed: () {Navigator.of(context).pop();},
)
从 C 页面跳转到 D 页面路由堆栈变动:
Navigator.of(context).pushNamedAndRemoveUntil('/D', ModalRoute.withName('/B'));
示意跳转到 D 页面,同时删除 D 到 B 间接所有的路由,如果删除所有路由,只保留 D:
Navigator.of(context).pushNamedAndRemoveUntil('/D', (Route route)=>false);
路由堆栈变动:
popUntil
有如下场景,在入职新公司的时候,须要填写各种信息,这些信息分为不同局部,比方根本信息、工作信息、家庭信息等,这些不同模块在不同页面,填写信息时能够返回上一页,也能够勾销,勾销返回到首页,此场景能够应用 popUntil,始终 pop 到指定的页面。
有 A、B、C、D 四个页面,D 页面通过 popUntil 始终返回到 A 页面,D 页面代码:
RaisedButton(child: Text('D 页面'),
onPressed: () {Navigator.of(context).popUntil(ModalRoute.withName('/A'));
},
)
路由堆栈变动:
传递数据
有如下场景,商品列表页面,点击跳转到商品详情页面,商品详情页面须要商品的惟一 id 或者商品详情数据,有两种形式传递数据:
第一种:通过构造函数形式:
class ProductDetail extends StatelessWidget {
final ProductInfo productInfo;
const ProductDetail({Key key, this.productInfo}) : super(key: key);
@override
Widget build(BuildContext context) {return Container();
}
}
跳转代码:
Navigator.of(context).push(MaterialPageRoute(builder: (context){return ProductDetail(productInfo: productInfo,);
}));
此种形式无奈用于命名路由的跳转形式。
第二种:通过命名路由设置参数的形式:
A 页面传递数据,
RaisedButton(child: Text('A 页面'),
onPressed: () {Navigator.of(context).pushNamed('/B',arguments: '来自 A');
},
)
B 页面通过 ModalRoute.of(context).settings.arguments
接收数据:
RaisedButton(child: Text('${ModalRoute.of(context).settings.arguments}'),
onPressed: () {Navigator.of(context).pushNamed('/C');
},
)
返回数据
B 页面返回代码:
RaisedButton(child: Text('${ModalRoute.of(context).settings.arguments}'),
onPressed: () {Navigator.of(context).pop('从 B 返回');
},
)
A 页面接管返回的数据:
class APage extends StatefulWidget {
@override
_APageState createState() => _APageState();
}
class _APageState extends State<APage> {
String _string = 'A 页面';
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.center,
child: RaisedButton(child: Text(_string),
onPressed: () async {
var result =
await Navigator.of(context).pushNamed('/B', arguments: '来自 A');
setState(() {_string = result;});
},
),
),
);
}
}
push 相干办法返回 Future 类型,应用 await 期待返回后果。
交换
交换
老孟 Flutter 博客(330 个控件用法 + 实战入门系列文章):http://laomengit.com
欢送退出 Flutter 交换群(微信:laomengit)、关注公众号【老孟 Flutter】: