路由(Route)在移动开发中通常指页面(Page),这跟web开发中单页应用的Route概念意义是相同的,Route在Android中通常指一个Activity,在iOS中指一个ViewController。所谓路由管理,就是管理页面之间如何跳转,通常也可被称为导航管理。Flutter中的路由管理和原生开发类似,无论是Android还是iOS,导航管理都会维护一个路由栈,路由入栈(push)操作对应打开一个新页面,路由出栈(pop)操作对应页面关闭操作,而路由管理主要是指如何来管理路由栈。
// 路由管理
// 导入包
import 'package:flutter/material.dart';
// 应用入口,启动Flutter应用,runApp它接受一个Widget参数
void main() => runApp(MyApp());
// MyApp类代表Flutter应用,它继承了 StatelessWidget类,这也就意味着应用本身也是一个widget
class MyApp extends StatelessWidget {
// widget的主要工作是提供一个build()方法来描述如何构建UI界面(通常是通过组合、拼装其它基础widget)
@override
Widget build(BuildContext context) {
// MaterialApp是Material库中提供的Flutter APP框架,通过它可以设置应用的名称、主题、语言、首页以及路由列表等,它本身也是一个widget
return MaterialApp(
// 应用名称
title: 'Flutter Demo',
// 名为"home"的路由作为应用的home(首页)
initialRoute: 'hmoe',
// 应用主题
theme: ThemeData(
primarySwatch: Colors.blue,
),
// 注册路由表
routes: {
'new page':(context) => NewRoute(),
// 将首页路由也注册为命名路由
'home':(context) => MyHomePage(title: 'Flutter Demo Home Page'),
},
// 应用首页路由,本身也是一个widget
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
// Flutter应用的首页
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
// 有状态的组件(Stateful widget) 和无状态的组件(Stateless widget)有两点不同:
// 1. Stateful widget可以拥有状态,这些状态在widget生命周期中是可以变的,而Stateless widget是不可变的。
// 2. Stateful widget至少由两个类组成:
// 一个StatefulWidget类;
// 一个State类,StatefulWidget类本身是不变的,但是State类中持有的状态在widget生命周期中可能会发生变化。
// 新路由,显示一句话
class NewRoute extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('New route'),
),
body: Center(
child: Text('This is new route'),
),
);
}
}
// 路由传值。它接受一个提示文本参数,负责将传入它的文本显示在页面上,
// 另外TipRoute中我们添加一个“返回”按钮,点击后在返回上一个路由的同时会带上一个返回参数。
class TipRoute extends StatelessWidget {
final String text;
TipRoute({
Key key,
@required this.text, // 接收一个text参数
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('提示'),
),
body: Padding(
padding: EdgeInsets.all(18),
child: Column(
children: [
Text(text),
RaisedButton(
onPressed: () => Navigator.pop(context, '我是返回值'),
child: Text('返回'),
)
],
),
),
);
}
}
//// 打开TipRoute,已合并到_MyHomePageState了
//class RouterTestRoute extends StatelessWidget {
// @override
// Widget build(BuildContext context) {
// return Center(
// child: RaisedButton(
// onPressed: () async {
// // 打开TipRoute,并等待返回结果
// var result = await Navigator.push(context, MaterialPageRoute(
// builder: (context) {
// return TipRoute(
// // 路由参数
// text: '我是提示***',
// );
// }
// ));
// // 输出TipRoute路由返回结果
// print('路由返回值:$result');
// },
// child: Text('打开提示页'),
// ),
// );
// }
//}
class _MyHomePageState extends State {
// 组件的状态,记录按钮点击的总次数
int _counter = 0;
// 设置状态的自增函数
/* 当按钮点击时,会调用此函数,该函数的作用是先自增_counter,然后调用setState 方法。
setState方法的作用是通知Flutter框架,有状态发生了改变,Flutter框架收到通知后,会执行build方法来根据新的状态重新构建界面,
Flutter 对此方法做了优化,使重新执行变的很快,所以你可以重新构建任何需要更新的东西,而无需分别去修改各个widget。
*/
void _incrementCounter() {
setState(() {
_counter++;
});
}
//
@override
Widget build(BuildContext context) {
// Scaffold是Material库中提供的页面脚手架,它提供了默认的导航栏、标题和包含主屏幕widget树的body属性。
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
// Center组件,可以将其子组件树对齐到屏幕中心
body: Center(
// Column组件,可以将其所有子组件沿屏幕垂直方向依次排列
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
// 添加一个新按钮
FlatButton(
child: Text('Open new route!'),
textColor: Colors.blue,
onPressed: () {
// // 导航到新路由
// Navigator.push(context,
// MaterialPageRoute(builder: (context) {
// return NewRoute();
// }));
// 通过路由名称来打开新路由
Navigator.pushNamed(context, 'new page');
},
),
// 添加一个新按钮
RaisedButton(
onPressed: () async {
// 打开TipRoute,并等待返回结果
var result = await Navigator.push(context, MaterialPageRoute(
builder: (context) {
return TipRoute(
// 路由参数
text: '我是提示信息哦!',
);
}
));
// 输出TipRoute路由返回结果
print('路由返回值:$result');
},
child: Text('打开提示页'),
),
],
),
),
// 页面右下角的带“+”的悬浮按钮,它的onPressed属性接受一个回调函数,代表它被点击后的处理器
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
总结
介绍了Flutter中路由管理、传参的方式,然后又着重介绍了命名路由相关内容。在此需要说明一点,由于命名路由只是一种可选的路由管理方式,在实际开发中,可能心中会犹豫到底使用哪种路由管理方式。在此,根据经验,建议最好统一使用命名路由的管理方式,这将会带来如下好处:
- 语义化更明确。
- 代码更好维护;如果使用匿名路由,则必须在调用Navigator.push的地方创建新路由页,这样不仅需要import新路由页的dart文件,而且这样的代码将会非常分散。
- 可以通过onGenerateRoute做一些全局的路由跳转前置处理逻辑。