[Flutter]四種你想要的通信方式都在這里!

背景

前段時間做了一個新項目,因為上線審核遲遲不過,于是上級決定趁這段時間將項目轉(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)造方法中,添加了兩個成員變量paramcallback,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)
            ],
          )),
        ));
  }

這里有幾個注意點:

  1. NotificationListener也是一個widget,用它作為頂級的widget來監(jiān)聽子節(jié)點的變化.
  2. 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é)會使用吧.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容