Flutter 学习3:转场、导航
这个是我学习Flutter的一个系列文章:
- Flutter 学习1:开发环境、开发工具、初始化一个项目
- Flutter 学习2:从main.dart文件说起
- Flutter 学习3:转场、导航
- Flutter 学习4:集成到原有的项目中
- Flutter 学习5:开发Dart包和插件包
- Flutter 学习6:绘制动画
- Flutter 学习7:Dart语言基础
- Flutter 学习8:BottomSheet
- Flutter 学习9:Positioned、Transform等控件使用以及手势控制
- Flutter 学习10:NestedScrollView、SliverAppBar、TabBar
上次文章从main.dart文件开始分析,并做出来一个ListView的页面。我跟着官方的例子继续往下看,它写了一个功能,当前的列表数据可以多选,然后可以跳转到一个新的页面查看选中的值。这里就涉及到了页面跳转。具体来看看怎么做。
示例的页面跳转
前面列表页面已经出来,那要多选需要把选中的数据保留下来。
class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
final _saved = new Set<WordPair>(); //保存选中的值
...
}
然后列表的Item我们添加了一个爱心,用来表示是否选中。这里我又要👍一个,Flutter自带的Material主题提供了各种Icon,可以直接拿来用,非常方便!
Widget _buildItem(WordPair pair) {
//这里定义当前项是否已经选中
final alreadySaved = _saved.contains(pair);
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
//这里就是爱心图标,红色表示选中,空心表示未选中
trailing: Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
),
//ListTile的点击事件 选中还是取消选中
onTap: (){
setState(() {
if (alreadySaved) {
_saved.remove(pair);
}else {
_saved.add(pair);
}
});
},
);
}
就是在原来的基础上,添加了一个爱心Icon和一个点击事件。现在可以点击了。
然后在导航栏添加一个菜单按钮,点击跳转到新页面查看选中的那几个值。
appBar: new AppBar(
title: new Text('Welcome to Flutter'),
//添加菜单按钮
actions: <Widget>[
new IconButton(icon: const Icon(Icons.list), onPressed: _navigatePush)
],
),
IconButton
绑定了点击事件_navigatePush
这是一个函数。
void _navigatePush() {
...
}
重点来了,这个函数里面应该是跳转页面的代码,如何写的呢? 官方的例子是这样写的:
//这句是重点跳转到一个新页面。通过一个MaterialPageRoute对象进行的
Navigator.of(context).push(
new MaterialPageRoute<void>(
builder: (BuildContext context) {
final tiles = _saved.map((WordPair pair){
return new ListTile(
title: new Text(pair.asPascalCase, style:_biggerFont),
);
});
final divided = ListTile.divideTiles(context: context, tiles: tiles).toList();
return new Scaffold(
appBar: new AppBar(
title: const Text('Saved Suggestions'),
),
body: new ListView(children: divided),
);
}
)
);
现在点击右上角的列表图标的菜单按钮就会跳转一下,然后展现选中的值。
这个例子它把新页面的代码都写在了这个MaterialPageRoute的builder函数里面了。对于简单业务的页面这样写还是蛮方便的!但这种时候毕竟少,大部分情况是页面结构复杂,逻辑复杂,肯定是要专门一个分开的类来处理的。那这种情况怎么跳转呢?
真实的页面跳转
查询了官方文档后,发现跳转到一个新的页面代码也很简单,跟上面的类似:
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
那前面的例子有把选中的数据传递过去的,前面是因为都在class内部的所以直接用_saved
这个Set就行了。那现在class分开了如何传递呢? 改写下这个SecondScreen的构建函数。
class SecondScreen extends StatelessWidget {
final Set<WordPair> saved ;
// 必须传入一个Set用来展现数据。
SecondScreen({Key key, @required this.saved}) : super(key: key);
...
跳转的时候就把_saved
这个Set传进去就行了。
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(saved: _saved)
)
);
页面关闭返回数据
现在页面跳转有了,有跳转有传入数据,那很容易联想到传出数据,就是把数据返回给前面的那个页面。 还是在这个Demo的基础上继续,比如我们第二个页面也是ListView,点击某一项把数据返回给前面那个页面。 先加点击事件:
return new ListTile(
title: new Text(pair.asPascalCase, style:_biggerFont),
onTap: (){
_itemTapAndNavigationPop(context, pair.asPascalCase);
},
);
点击之后把名字传出去,并且关闭当前页面:
void _itemTapAndNavigationPop(BuildContext context, String title) {
//只要执行pop函数就行了。
Navigator.pop(context, title);
}
但是前面的页面需要接收返回的数据啊,跳转的时候就肯定不是原来的写法了:
//这里这个调用函数加了异步标识async
void _navigatePush(BuildContext context) async {
//页面返回的数据result
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(saved: _saved)
)
);
if (result != null) {
//把接收到的值 用SnackBar显示一下
Scaffold.of(context).showSnackBar(SnackBar(content: Text("$result")));
}else {
debugPrint('没有回传数据!!!!');
}
}
这里遇到一个问题,返回数据的时候showSnackBar这里一直报错!看文档的意思是当BuildContext在Scaffold之前调用Scaffold.of(context)会报错,因为它是在Scaffold的基础上显示的。所以它建议要新建一个Builder。所以前面的列表菜单按钮就重新Builder构建了一次。
actions: <Widget>[
new Builder(builder: (BuildContext context){
return IconButton(
icon: const Icon(Icons.list),
onPressed: () => this._navigatePush(context)
);
})
],
页面跳转,数据传递基本就这样,跟原生的已经很类似了! 全部代码:https://github.com/fancylou/FlutterDemo/tree/2018-12-04
The End !