Flutter widget 間傳遞數據的方案 InheritedWidget、Notification、EventBus

Flutter是由衆多widget構成的UI框架,之前的文章我們在不同的widget之間傳遞數據是通過構造函數傳參的方式傳遞。如果嵌套的widget過多,這麼寫不免有些麻煩且層級複雜。所以Flutter還提供了其他方案來實現跨 widget 間數據的傳遞,下面就介紹InheritedWidget、Notification 和 EventBus這三種方案。

InheritedWidget

InheritedWidgetwidget的基類,可有效地向下傳播信息。
可以理解爲子 widget可以在任何位置獲取繼承了InheritedWidget父 widget中的數據。

示例

示例中通過構造函數和繼承InheritedWidget兩種方式實現父 widget :FrogColor子 widget:FColor傳值。

構造函數傳值:

在這裏插入圖片描述

繼承 InheritedWidget 傳值:
  1. 首先定義一個類FrogColor繼承InheritedWidget,並在構造方法中傳遞數據和方法:model 和 doSomeThing
class FrogColor extends InheritedWidget {

  const FrogColor({
    Key key,
    @required this.model,
    @required this.doSomeThing,
    @required Widget child,
  }) : assert(model != null),
        assert(child != null),
        super(key: key, child: child);

  // 直接從 _FirstPageState 中的 State 獲取數據,這裏可指定爲泛型
  final _FirstPageState model;
  // 方法
  final Function() doSomeThing;

  // 獲取實例
  static FrogColor of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<FrogColor>();
  }

  // 判斷是否需要更新
  @override
  bool updateShouldNotify(FrogColor old) => model != old.model;
}
  1. 在佈局中使用FrogColor包裹FColorwidget,同時傳遞data和 doSomeThing 方法。由於上面定義的data_FirstPageState所以這裏直接傳遞thisdoSomeThing方法中我們傳遞了_changeColor用於改變mColor的值,並且在子佈局中能夠刷新顯示最新的color
    在這裏插入圖片描述
  2. 最後我們在FColor中獲取到FrogColor中的date數據和調用doSomeThing 方法
    在這裏插入圖片描述

上面的FrogColormodel是直接傳入_FirstPageState,這樣耦合度會比較高
所以我們將這個對象轉爲泛型,封裝後如下:

新建 state_provider.dart文件:

import 'package:flutter/material.dart';

/// 使用泛型傳入數據
class IProvider<T> extends InheritedWidget {
  // 數據
  final T data;
  // 方法
  final Function() doSomeThing;

  IProvider({Key key, Widget child, this.data,  this.doSomeThing})
      : super(key: key, child: child);

  @override
  bool updateShouldNotify(IProvider oldWidget) {
    return data != oldWidget.data;
  }
  static IProvider<T> of<T>(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<IProvider<T>>();
  }
}

封裝好的使用需要傳入數據類

@override
Widget build(BuildContext context) {

  return Scaffold(
      appBar: AppBar(
        title: Text('InheritedWidget'),
      ),
      body: IProvider<_FirstPageState>(// 傳入 _FirstPageState
        data: this
        doSomeThing: _changeColor,//提供修改數據的方法
        child: FColor('哈哈哈'),
      )
  );
}

以上就是InheritedWidget的使用,使用InheritedWidget的方式可以降低數據和UI界面的耦合。
直接使用InheritedWidget不免有些麻煩,Flutter提供了更加強大的 Provider 庫,用於實現依賴注入和狀態管理(後續瞭解之後詳細介紹)。

InheritedWidget僅限與從父widget向下傳遞數據。那麼從子widget向上傳遞數據如何實現呢?Notifiation就可以實現。

Notification

Notification 數據共享方式是從子 widgetw 向上傳遞至父widget`。

在之前的文章 Flutter 嵌套滾動 CustomScrollView 示例 中,我們使用NotificationListener去監聽子child CustomScrollView的滑動距離。

同理,將子widget的數據傳遞給父widget也可以用這個方式實現

示例

從子widget向上發送數據,在父widget中監聽到消息並打印在控制檯。
在這裏插入圖片描述
在這裏插入圖片描述
以上就是Notification的使用,使用Notification的方式可以子widget的數據通過dispatch發送給父widget
無論是 InheritedWidget還是 Notificaiton,都需要依賴與父子關係的widget
而一些場景下的數據傳遞是沒有這層關係的,
Android中有事件總線的方式可以很方便的實現這類場景的數據傳遞,
Flutter中也有這樣類似的庫:EventBus

EventBus

使用Dart Streams進行應用程序解耦的簡單事件總線。

示例

  1. 引入庫
dependencies:
  event_bus: ^1.1.1
  1. 初始化EventBus,並定義消息數據類
final eventBus = new EventBus();

/// 傳遞的數據
class MsgEvent{
  final String msg;
  MsgEvent(this.msg);
}
  1. 監聽
 // 監聽數據變化
eventBus.on<MsgEvent>().listen((event) {
   print("接收到的數據:${event.msg}");
 });
  1. 發送數據
 eventBus.fire(MsgEvent("子 widget 發送過來的數據:老頭子"));

通過以上的4步即可完成不依賴widget的數據傳輸。

示例完整源碼

import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';

final eventBus = EventBus();

class FirstPage extends StatefulWidget {
  @override
  _FirstPageState createState() {
    return _FirstPageState();
  }
}

class _FirstPageState extends State<FirstPage> {

  @override
  void initState() {
    super.initState();
    // 監聽數據變化
    eventBus.on<MsgEvent>().listen((event) {
      print("接收到的數據:${event.msg}");
    });
  }

  @override
  void dispose() {
    super.dispose();
    // 釋放資源
    eventBus.destroy();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('EventBus'),
        ),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            // 子 widget
            RaisedButton(
              // 按鈕點擊時分發通知
              onPressed: () {
                eventBus.fire(MsgEvent("父 widget 發送過來的數據:我是你爸爸"));
              },
              child: Text("父 widget 發送數據"),
            ),
            CustomChild(),
          ],
        ));
  }
}

class CustomChild extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      // 按鈕點擊時分發通知
      onPressed: () {
        eventBus.fire(MsgEvent("子 widget 發送過來的數據:老頭子"));
      },
      child: Text("子 widget 發送數據"),
    );
  }
}

/// 消息數據類
class MsgEvent{
  final String msg;
  MsgEvent(this.msg);
}

wan~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章