本文主要讲一个Flutter中局部替换路由的方案,在实际开发中,需要局部替换的情况似乎还蛮多的,所以还是讲一下这个小技巧把。
效果图:
实现其实超级简单,主要是在压路由的时候注意下用哪个context就行了。
代码:https://gist.github.com/yumi0629/a22bc590bbe7a3d10d3436afb27c7043
首先页面分成两部分,上下的appBar和bottomNavigationBar是在局部替换的时候不变的,中间的body是需要局部替换的。我们知道,路由的替换是Navigator操控的,所以中间的body部分,我们只要自己维护一个Navigator,就可以实现局部替换了。而Flutter早就为我们设计好了,Navigator是一个Widget,我们可以直接拿来用:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('PartNavigator'),
),
body: Navigator(
// 这里维护局部路由
),
bottomNavigationBar: Container(
height: 40.0,
color: Colors.red,
),
);
}
定义一个Navigator很简单,点进文档就能看到很详细的example,这里再次感谢Flutter爸爸文档写得真良心!
Navigator(
initialRoute: 'part/part1',
onGenerateRoute: (RouteSettings settings) {
RoutePageBuilder builder;
switch (settings.name) {
case 'part/part1':
builder = (_, __, ___) => PartPage1(
pageContext: context,
);
break;
case 'part/part2':
builder = (_, __, ___) => PartPage2(
pageContext: context,
);
break;
default:
throw Exception('Invalid route: ${settings.name}');
}
return PageRouteBuilder(
pageBuilder: builder,
transitionDuration: const Duration(milliseconds: 0),
);
},
)
其中,onGenerateRoute是必传的,initialRoute
、onGenerateRoute
、onUnknownRoute
都是定义路由跳转的,他们之间有优先级关系,这个我以前讲过,还是再说一遍吧:
Navigator
会按照initialRoute----onGenerateRoute---->onUnknownRoute
的顺序去寻找路由:
-
initialRoute
,也就是初始路由; -
onGenerateRoute
用来处理路由,我们可以在里面定义路由映射,所以一般返回非空值; -
onUnknownRoute
如果说某个路由上面都没处理,那么就会由onUnknownRoute
来处理这个路由。
上述例子中我们将part1
控件作为初始路由,我们在里面写两个按钮,一个按钮点击后做布局跳转到part2
,另一个按钮点击后做全局的页面跳转:
class PartPage1 extends StatelessWidget {
final BuildContext pageContext;
const PartPage1({Key key, this.pageContext}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text.rich(
TextSpan(
style: TextStyle(fontSize: 20.0),
text: 'This is part',
children: [
TextSpan(
style: TextStyle(fontSize: 40.0, color: Colors.red),
text: ' 1',
)
]),
),
RaisedButton(
onPressed: () {
Navigator.of(context).pushNamed('part/part2');
},
child: Text('To Part 2'),
),
RaisedButton(
onPressed: () {
Navigator.of(pageContext)
.push(MaterialPageRoute(builder: (_) => AnotherPage()));
},
child: Text('To Another Page'),
)
],
);
}
}
我们可以看到,两次跳转时获取Navigator的context明显是不一样的,我开头就说了,压路由一定要使用对应的Navigator的context。PartPage1
中的context是局部Navigator给它的,所以维护的是局部路由;而变量pageContext
是Scaffold
给它的,所以它并不属于Navigator。所以,Navigator.of(context)
和Navigator.of(pageContext)
自然不是同一个实例。
到这里,很多小伙伴肯定会问了,Scaffold
的Navigator是哪里来的呢?翻下源码就知道啦,Flutter在WidgetsApp
中维护了一个全局的路由池来供开发者使用:
@override
Widget build(BuildContext context) {
Widget navigator;
if (_navigator != null) {
navigator = Navigator(
key: _navigator,
// If window.defaultRouteName isn't '/', we should assume it was set
// intentionally via `setInitialRoute`, and should override whatever
// is in [widget.initialRoute].
initialRoute: WidgetsBinding.instance.window.defaultRouteName != Navigator.defaultRouteName
? WidgetsBinding.instance.window.defaultRouteName
: widget.initialRoute ?? WidgetsBinding.instance.window.defaultRouteName,
onGenerateRoute: _onGenerateRoute,
onUnknownRoute: _onUnknownRoute,
observers: widget.navigatorObservers,
);
}
网友评论