背景
前段時間做了一個新項目,因為上線審核遲遲不過,于是上級決定趁這段時間將項目轉(zhuǎn)為Flutter,試一下水,看一下它在安卓和iOS兩端的真實表現(xiàn).目前第一個版本已經(jīng)開發(fā)完畢,有一些開發(fā)上的收獲,后面會陸續(xù)總結(jié)一下.今天給大家?guī)砦宜赖腇lutter的四種通信方式.
補充一種異常好用的響應(yīng)式工具MobX
概覽
四種通信方式分別為:
- Callback
- Notification
- EventBus
- Provider
下面分別介紹每種方式的用法.
Callback
這種方式類似于iOS中的block,使用起來也非常像.
這里舉個栗子:我要從home頁進入子頁面,,并且傳遞一個參數(shù)進入子頁面,然后從子頁面返回的時候可以回調(diào)一個參數(shù)到home頁.
首先我們創(chuàng)建兩個文件,一個Home.dart,一個Detail.dart.
Detail.dart :
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class Detail extends StatelessWidget{
final String param; // home過來的參數(shù)
final Function callBack; // 回調(diào)方法
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('跳轉(zhuǎn)'),
onPressed: (){
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context){
return Detail(param: 'home給子頁面的參數(shù)',callBack: (String msg){
print(msg);
},);
}
)
);
},
),
),
);
}
}
代碼很簡單,非常類似在iOS中的block回調(diào)用法.這里,在Detail初始化構(gòu)造方法中,添加了兩個成員變量param和callback,param就是home傳遞到Detail中的參數(shù),而callback負責(zé)回調(diào)Detail中的值給Home.并且這種callback還支持多參數(shù)回調(diào),例如:
// Detail中:
RaisedButton(
onPressed: (){
// 回調(diào)了多個參數(shù)
this.callBack('我從子頁面返回了','我是第二個參數(shù)');
Navigator.of(context).pop();
},
child: Text('返回'),
),
// Home中:
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context){
// 接收多個參數(shù)
return BookDetail(param: 'home給子頁面的參數(shù)',callBack: (String msg,String ext){
print(msg+','+ext);
},);
}
)
要注意的就是接收參數(shù)時,我們的參數(shù)類型一定要判斷準確.另外還有一個技巧,如果你想同時傳遞多個參數(shù)并且回調(diào)的話,可以用Map包裹住參數(shù)和callback作為一個參數(shù)傳遞到子頁面,在適當?shù)臅r候進行回調(diào)即可.
Notification
Flutter中的Notification官方解釋是:widget樹中,父節(jié)點通過NotificationListener用來監(jiān)聽子節(jié)點的變化.這里就說明了一個信息,這個Notification做不到跨層通信,只能是父節(jié)點去監(jiān)聽子節(jié)點的變化.所以通知通常都是用于同級頁面之間的通信.
使用也很簡單:
先自定義一個通知類:
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('發(fā)通知'),
onPressed: () {
MyNotification(msg: '平級的msg').dispatch(context);
},
);
},
),
Text(_msg)
],
)),
));
}
這里有幾個注意點:
-
NotificationListener也是一個widget,用它作為頂級的widget來監(jiān)聽子節(jié)點的變化. - dispatch的時候,不能直接用父節(jié)點的context,而是需要通過
Builder來產(chǎn)生子節(jié)點的context.
EventBus
EventBus是一個通過事件命名的方式,可以實現(xiàn)跨層通信的工具.
需要在pubspec.yaml文件中引入包:
dependencies:
flutter:
sdk: flutter
event_bus: ^1.1.0
然后導(dǎo)入頭文件:
import 'package:event_bus/event_bus.dart';
首先我們創(chuàng)建一個Event類,來存放各種通信的事件,類似于iOS中通知的name.
class TestEvent{
final String msg; // 傳遞的參數(shù)
TestEvent(this.msg);
}
這里我創(chuàng)建了一個單例對象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;
}
}
然后我們在子頁面中發(fā)送這個event:
// 使用EventBus自帶的fire方法進行發(fā)送
GlobalEvent().eventBus.fire(TestEvent('子頁面返回了數(shù)據(jù)'));
在Home中進行事件監(jiān)聽:
_onListenEvent(){
// 這里可以指定事件,也可以不指定,不指定默認接收全部事件
eventBus.on<TestEvent>().listen((event){
print(event.msg);
});
}
Provider
Provider是谷歌官方提供的一種狀態(tài)管理工具,用它可以綁定視圖層,Provider變化時,視圖也會跟著變化.
官方使用文檔
在pubspec.yaml中:
provider: ^3.0.0+1
在使用的dart文件里導(dǎo)入頭文件:
import 'package:provider/provider.dart';
首先我們需要創(chuàng)建一個model類,承載狀態(tài)變化的屬性和操作.
class TestModel extends ChangeNotifier{
String _msg = '初始值';
String get msg => _msg;
set msg(String outMsg) {
_msg = outMsg;
notifyListeners();
}
}
在需要改變的地方調(diào)用一下notifyListeners()方法.
我們可以在main.dart最頂級的方法中監(jiān)聽這些狀態(tài).
void main() {
runApp(ChangeNotifierProvider<TestModel>.value(
value:TestModel(),
child: MYApp(),
));
}
在Home中,我們直接使用Text(Provider.of<TestModel>(context).msg)這種方式進行賦值.
在子頁面中,如果要改變這個Text的值,那么只需要在響應(yīng)方法里
// 這里是通過TestModel中的set方法進行修改,這個set方法會調(diào)用
// notifyListeners()方法,從而改變狀態(tài)通知到頁面進行修改
Provider.of<TestModel>(context).msg = '子頁面修改了數(shù)據(jù)';
當然,通常我們不會在main.dart只監(jiān)聽一個對象的狀態(tài),可能會有多個狀態(tài)要去監(jiān)聽,這時候就可以使用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(),
);
}));
}
}
想要了解更多可以去看看官方使用文檔.
先寫到這里,主要是一些基礎(chǔ)的用法,先學(xué)會使用吧.