路由的简单实用
- 基本的界面跳转
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("first Screen"),
),
body: Center(
child: RaisedButton(
child: Text("this is first screen"),
onPressed: () {
//go to second screen
Navigator.push(context, MaterialPageRoute(builder: (context)=>SecodeScreen()));
},
)),
);
}
}
如上图所示,第一个界面中包含了一个按钮,在按钮的点击事件中
Navigator.push(context, MaterialPageRoute(builder: (context)=>SecondScreen()));
这句代码的意思将页面SecondScreen压入路由栈,在Android开发中我们也是同样的使用一个回退栈管理我们的界面,既然有入栈操作,那么一定有出栈了,没错,下面看第二个界面的代码:
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("second screen"),
),
body: Center(
child: RaisedButton(
child: Text("go back"),
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
}
我们使用了
Navigator.pop(context);
将该本页面弹出路由栈,从而返回到第一个页面。
以上就是例子可以在官方文档中找到,这只是最简单的路由跳转,但是平时我们开发经常需要在页面之间传值,所以下面我们来看看,flutter中路由如何传递参数。
- 传递参数到新页面
我们继续使用官方的例子,首先定义一个实体类,
class Todo{
final String title;
final String desc;
Todo(this.title,this.desc);
}
然后我们创建一个todoList
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: TodoScreen(
todos: List.generate(10, (i) => Todo("title$i", "Desc$i"))),
);
}
}
//列表页
class TodoScreen extends StatelessWidget {
final List<Todo> todos;
TodoScreen({Key key, @required this.todos}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("todos"),
),
body: ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(todos[index].title),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(todo: todos[index])));
},
);
},
),
);
}
}
//详情页
class DetailScreen extends StatelessWidget {
final Todo todo;
DetailScreen({Key key, @required this.todo}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(todo.title),
),
body: Center(
child: Text(todo.desc),
),
);
}
}
在代码里我们通过
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(todo: todos[index])));
},
);
将todo传到了详情页,也就是通过构造函数传值,在flutter中一切都是widget,我们在DetailScreen里通过构造函数接收即可。
- 携带参数返回
在Android开发中,我们通常使用startActivityForResult启动新页面,这样可以在当前中重写onActivityForResult来接收新页面返回的参数,那么在Flutter中该怎么坐呢,答案是使用Navigator.Pop(),
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: HomeScreen(),
);
}
}
class SelectionButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return RaisedButton(
child: Text('pick an option,any option'),
onPressed: () {
_navigateAndDisplaySelection(context);
},
);
}
}
_navigateAndDisplaySelection(BuildContext context) async{
final result = await Navigator.push(context,
MaterialPageRoute(builder: (context)=>SelectionScreen())
);
Scaffold.of(context)
..removeCurrentSnackBar()
..showSnackBar(SnackBar(content:Text("$result")));
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('home screen'),
),
body: Center(child: SelectionButton()),
);
}
}
class SelectionScreen extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("pick on option"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
onPressed: (){
Navigator.pop(context,"yep");
},
child: Text("yep"),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
onPressed: (){
Navigator.pop(context,"nope");
},
child: Text("nope"),
),
)
],
),
),
);
}
}
Pop方法的第二个参数是一个
T result
这样我们在出栈的时候可以将参数带回到上一个页面,在上一个页面中,我们这样来接收
_navigateAndDisplaySelection(BuildContext context) async{
final result = await Navigator.push(context,
MaterialPageRoute(builder: (context)=>SelectionScreen())
);
Scaffold.of(context)
..removeCurrentSnackBar()
..showSnackBar(SnackBar(content:Text("$result")));
}
- 命名路由
首先我们先定义一个路由映射关系
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: FirstScreen(),
routes: {
'/second':(context)=>SecondScreen()
},
);
}
}
routes
是一个map,用来管理我们定义的命名路由,之后我们就可以使用
Navigator.pushNamed(context, "/second");
来进行路由的跳转,但是命名路由的方式有一个缺点,就是不能直接携带参数,只能静态的在注册路由的时候这么写:
routes: {
'/second':(context)=>SecondScreen('params')
},
这样在传递一些动态的改变的参数时候就显得不方便。
- Hero控件
这个很像Android里的共享元素,比如页面A和页面B都有一张相同的图片,让他们之间跳转的时候,图片无缝过渡,在Flutter使用Hero可以实现这一效果。下面来看看代码中如何实现。
class HeroApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Transition Demo',
home: MainScreen(),
);
}
}
class MainScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Main Screen'),
),
body: GestureDetector(
child: Hero(
tag: 'imageHero',
child: Image.network(
'https://raw.githubusercontent.com/flutter/website/master/src/_includes/code/layout/lakes/images/lake.jpg',
),
),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return DetailScreen();
}));
},
),
);
}
}
class DetailScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
child: Center(
child: Hero(
tag: 'imageHero',
child: Image.network(
'https://raw.githubusercontent.com/flutter/website/master/src/_includes/code/layout/lakes/images/lake.jpg',
),
),
),
onTap: () {
Navigator.pop(context);
},
),
);
}
}
在不同页面通过给Hero设置相同的tag,使它们关联起来。
总结
Flutter中路由的常用使用方式基本介绍完了,主要参考官方例子,如有不足,欢迎指正。