背景
前段时间做了一个新项目,因为上线审核迟迟不过,于是上级决定趁这段时间将项目转为Flutter,试一下水,看一下它在安卓和iOS两端的真实表现.目前第一个版本已经开发完毕,有一些开发上的收获,后面会陆续总结一下.今天给大家带来我所知道的Flutter的四种通信方式.
补充一种异常好用的响应式工具MobX
概览
四种通信方式分别为:
- Callback
- Notification
- EventBus
- Provider
下面分别介绍每种方式的用法.
Callback
这种方式类似于iOS中的block,使用起来也非常像.
这里举个栗子:我要从home页
进入子页面
,,并且传递一个参数进入子页面
,然后从子页面
返回的时候可以回调一个参数到home页
.
首先我们创建两个文件,一个Home.dart
,一个Detail.dart
.
Detail.dart :
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class Detail extends StatelessWidget{
final String param; // home过来的参数
final Function callBack; // 回调方法
BookDetail({Key key,this.param,this.callBack}):super(key : key);
_printParam(){
print(this.param);
}
@override
Widget build(BuildContext context) {
_printParam();
return Scaffold(
appBar: AppBar(
title: Text('子页面'),
),
body: Container(
child: RaisedButton(
onPressed: (){
this.callBack('我从子页面返回了');
Navigator.of(context).pop();
},
child: Text('返回'),
),
),
);
}
}
Home.dart:
import 'package:book_ios/Shelf/Detail.dart';
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
@override
BookShelfWidgetState createState() => BookShelfWidgetState();
}
class BookShelfWidgetState extends State<BookShelfWidget> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("home"),
),
body: Center(
child: RaisedButton(
child: Text('跳转'),
onPressed: (){
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context){
return Detail(param: 'home给子页面的参数',callBack: (String msg){
print(msg);
},);
}
)
);
},
),
),
);
}
}
代码很简单,非常类似在iOS中的block回调用法.这里,在Detail
初始化构造方法中,添加了两个成员变量param
和callback
,param
就是home
传递到Detail
中的参数,而callback
负责回调Detail
中的值给Home
.并且这种callback
还支持多参数回调,例如:
// Detail中:
RaisedButton(
onPressed: (){
// 回调了多个参数
this.callBack('我从子页面返回了','我是第二个参数');
Navigator.of(context).pop();
},
child: Text('返回'),
),
// Home中:
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context){
// 接收多个参数
return BookDetail(param: 'home给子页面的参数',callBack: (String msg,String ext){
print(msg+','+ext);
},);
}
)
要注意的就是接收参数时,我们的参数类型一定要判断准确.另外还有一个技巧,如果你想同时传递多个参数并且回调的话,可以用Map
包裹住参数和callback作为一个参数传递到子页面
,在适当的时候进行回调即可.
Notification
Flutter中的Notification
官方解释是:widget树中,父节点通过NotificationListener
用来监听子节点的变化.这里就说明了一个信息,这个Notification
做不到跨层通信,只能是父节点去监听子节点的变化.所以通知通常都是用于同级页面之间的通信.
使用也很简单:
先自定义一个通知类:
class MyNotification extends Notification{
final String msg;
MyNotification({this.msg});
}
在Home中使用:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("home"),
),
body: NotificationListener<MyNotification>(
onNotification: (noti) {
setState(() {
_msg = noti.msg;
});
},
child: Center(
child: Column(
children: <Widget>[
// 新建一个Builder()
Builder(
builder: (context) {
return RaisedButton(
child: Text('发通知'),
onPressed: () {
MyNotification(msg: '平级的msg').dispatch(context);
},
);
},
),
Text(_msg)
],
)),
));
}
这里有几个注意点:
-
NotificationListener
也是一个widget,用它作为顶级的widget来监听子节点的变化. - dispatch的时候,不能直接用父节点的context,而是需要通过
Builder
来产生子节点的context.
EventBus
EventBus是一个通过事件命名的方式,可以实现跨层通信的工具.
需要在pubspec.yaml文件中引入包:
dependencies:
flutter:
sdk: flutter
event_bus: ^1.1.0
然后导入头文件:
import 'package:event_bus/event_bus.dart';
首先我们创建一个Event类,来存放各种通信的事件,类似于iOS中通知的name.
class TestEvent{
final String msg; // 传递的参数
TestEvent(this.msg);
}
这里我创建了一个单例对象GlobalEvent
来保存一个全局可使用的event
,好处是不用每次都将event进行传递.
class GlobalEvent {
EventBus eventBus;
factory GlobalEvent() =>_getInstance();
static GlobalEvent get instance => _getInstance();
static GlobalEvent _instance;
GlobalEvent._internal() {
eventBus = EventBus();
}
static GlobalEvent _getInstance() {
if (_instance == null) {
_instance = GlobalEvent._internal();
}
return _instance;
}
}
然后我们在子页面中发送这个event:
// 使用EventBus自带的fire方法进行发送
GlobalEvent().eventBus.fire(TestEvent('子页面返回了数据'));
在Home中进行事件监听:
_onListenEvent(){
// 这里可以指定事件,也可以不指定,不指定默认接收全部事件
eventBus.on<TestEvent>().listen((event){
print(event.msg);
});
}
Provider
Provider是谷歌官方提供的一种状态管理工具,用它可以绑定视图层,Provider变化时,视图也会跟着变化.
官方使用文档
在pubspec.yaml中:
provider: ^3.0.0+1
在使用的dart文件里导入头文件:
import 'package:provider/provider.dart';
首先我们需要创建一个model类,承载状态变化的属性和操作.
class TestModel extends ChangeNotifier{
String _msg = '初始值';
String get msg => _msg;
set msg(String outMsg) {
_msg = outMsg;
notifyListeners();
}
}
在需要改变的地方调用一下notifyListeners()
方法.
我们可以在main.dart
最顶级的方法中监听这些状态.
void main() {
runApp(ChangeNotifierProvider<TestModel>.value(
value:TestModel(),
child: MYApp(),
));
}
在Home中,我们直接使用Text(Provider.of<TestModel>(context).msg)
这种方式进行赋值.
在子页面中,如果要改变这个Text的值,那么只需要在响应方法里
// 这里是通过TestModel中的set方法进行修改,这个set方法会调用
// notifyListeners()方法,从而改变状态通知到页面进行修改
Provider.of<TestModel>(context).msg = '子页面修改了数据';
当然,通常我们不会在main.dart
只监听一个对象的状态,可能会有多个状态要去监听,这时候就可以使用MultiProvider了.
void main() {
runApp(ZNOVApp());
}
class ZNOVApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (_) => TestModel()),
],
child: Consumer<TestModel>(builder: (context, model, _) {
return MaterialApp(
title: 'home',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: BaseTabBarPage(),
);
}));
}
}
想要了解更多可以去看看官方使用文档.
先写到这里,主要是一些基础的用法,先学会使用吧.