0%

flutter路由与导航

flutter 中,路由(Route)与导航器(Navigator)搭配在一起,但能够实现页面的跳转。

导航器(Navigator

路由(Route)对应着一个一个页面,页面的之间的切换便是由导航器(Navigator)控件管理。导航器管理着一个页面堆栈,在 MaterialApp 应用中, home 对应的页面则为最底层。 导航器(Navigator)通过提供的 pushpop 等方法来实现页面的切换。

1
2
3
4
5
6
7
8
9
Future push(
BuildContext context,
Route route,
)

bool pop(
BuildContext context,
[result],
)

在页面中通过使用 Nabigator.of(context) 来调用导航器。

返回值

当路由被推送,用于获取用户的输入时,可以通过 pop 方法的 result 参数返回用户的输入值。推送路由的方法会返回 Future 类型, Future 会在该路由被弹出式解析,其值位 pop 方法返回的 result 参数。若未传参则值为 null

1
2
3
4
5
6
7
8
9
10
bool value = await Navigator.of(context).push(MaterialPageRoute<bool>(
builder: (BuildContext context) {
return Center(
child: GestureDetector(
child: Text("确定"),
onTap: () { Navigator.of(context).pop(true); },
),
);
}
));

弹出路由

路由不一定需要掩盖整个屏幕,PopupRoutes 覆盖屏幕,屏幕颜色只能部分不透明,以允许当前屏幕显示。弹出路由是模态的,因为它们阻止了下面的控件的输入。

框架有创建和显示弹出路由的功能,例如:showDialogshowMenushowModalBottomSheet。这些功能如上所述返回其推送的路由的 Future ,调用时可以等待返回的值在路由弹出时采取行动,或者发现路由的值。

还有一些创建弹出路由的控件,如PopupMenuButtonDropdownButton。这些控件创建PopupRoute的内部子类,并使用路由的pushpop方法来显示和关闭它们。

1
2
3
4
5
6
7
8
9
10
11
12
Future<Null> fun() async {
bool value = await showDialog(
context: 'context',
barrierDismissible: true,
child: new Center(
child: new GestureDetector(
child: new Text("sure"),
onTap: () { Navigator.of(context).pop(true); },
),
)
);
}

路由(Route

路由(Route)分为两种,一种是静态路由,另一种是动态路由。

静态路由

所谓静态路由就是在 flutter 初始化时, flutter 就知道该应用存在那些路由。这些路由定义在 MaterialApp 控件的 routers 属性中。

routers 需要 Map 类型,第一个参数是路由名称,第二个参数是对应的页面。

下面是以简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// main.dart
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Page1(),
routes: <String, WidgetBuilder>{'/page2': (_) => Page2('from page1')},
);
}
}

// page1.dart
class Page1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Page1"),
),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.of(context).pushNamed("/page2");
},
child: Text("点我跳转"),
),
),
);
}
}

// page2.dart
class Page2 extends StatelessWidget {
Page2(this.content);

final String content;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Page2"),
),
body: Center(
child: Text(content),
),
);
}
}

从示例中可以看到我们在定义路由的时候是可以参入参数的,但是这种静态路由的参数是固定的,也就是说我们需要在定义的时候就把参数传递进去,若是参数需要动态进行获取那么这种静态路由可能就不太适合了。

在定义路由的时候,我们可以直接把路由写在 routers 参数后面,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
...
routes: <String, WidgetBuilder> {
'/page1': Page1(),
'/page2': Page2(),
'/page3': Page3(),
},
...
);
}
}

也可采用官方的路由统一管理的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// page2.dart
class Page2 extends StatelessWidget {
static const String routeName = '/page2';

Page2(this.content);

final String content;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Page2"),
),
body: Center(
child: Text(content),
),
);
}
}

// router.dart
List<RouteData> routerList = <RouteData>[
RouteData(
routeName: Page1.routeName,
buildRoute: (BuildContext context) => Page1(),
),
RouteData(
routeName: Page2.routeName,
buildRoute: (BuildContext context) => Page2('from somewhere'),
),
];

class RouteData {
const RouteData({@required this.routeName, @required this.buildRoute});

final String routeName;
final WidgetBuilder buildRoute;

@override
String toString() {
return '$runtimeType($routeName $routeName)';
}
}

// main.dart
class MyApp extends StatelessWidget {
Map<String, WidgetBuilder> _createRouter() {
return Map<String, WidgetBuilder>.fromIterable(
routerList,
key: (dynamic route) => '${route.routeName}',
value: (dynamic route) => route.buildRoute,
);
}

...

@override
Widget build(BuildContext context) {
return MaterialApp(
...
routes: _createRouter(),
...
);
}
}

动态路由

在导航器(Navigator)的 push 方法需要传入一个 Route 对象,我们可是使用 PageRouteBuilder 方法来创建这个对象。

1
2
3
4
5
6
7
8
9
10
11
12
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (
BuildContext context,
_,
__,
) {
return Page();
},
),
);

这样的路由我们就可以进行动态传参,根据不同的状态传递不同的参数到路由中。

这种路由方式我们可以自己定义路由动画。在 PageRouteBuilder 中有一个 transitionsBuilder 属性,可以定义动画。

1
2
3
4
5
6
7
8
...
transitionsBuilder: (_, Animation<double> animation, __, Widget child) {
return FadeTransition(
opacity: animation,
child: child,
);
},
...