Flutter 中的資料傳遞

移動的小太陽發表於2021-03-28

Flutter 中的資料傳遞

在開發中,資料從一個頁面傳遞到另一個頁面事很常用的,在Android 開發中,通常是通過把資料放到 intent 中傳遞過去。在 Flutter 中,資料是如何傳遞的呢?

在Flutter 中一切都是Widget,所以資料的傳遞就成了資料才Widget 中的傳遞。在之前的學習中,資料從一個Widget 傳遞到 子 Widget 是通過建構函式,一層一層的往裡面傳,要是 widget 的層級比較少,還沒什麼問題,要是層級很多,這樣傳遞就太麻煩了。

還好Flutter 還提供了三種方案:InheritedWidget、Notification 和 EventBus來解決資料傳遞問題。

InheritedWidget

InheritedWidget 是 Flutter 中的一個功能型 Widget,適用於在 Widget 樹中共享資料的場景。通過它,我們可以高效地將資料在 Widget 樹中進行跨層傳遞。

下面看計數器的例子:

// 1.InheritedWidget,我們定義了一個繼承自它的新類 CountContainer,裡面存放需要共享的資料
//然後,我們將計數器狀態 count 屬性放到 CountContainer 中,並提供了一個 of 方法方便其子 Widget 在 Widget 樹中找到它。
//最後,我們重寫了 updateShouldNotify 方法,這個方法會在 Flutter 判斷 InheritedWidget 是否需要重建,
class CountContainer extends InheritedWidget {
  static CountContainer of(BuildContext context) =>
      context.dependOnInheritedWidgetOfExactType<CountContainer>();
  final _InheritedWidgetHomeState mode;
  final Function() function;

  CountContainer(
      {Key key,
      @required this.mode,
      @required this.function,
      @required Widget child})
      : super(key: key, child: child);

  @override
  bool updateShouldNotify(covariant InheritedWidget oldWidget) {
    return this != oldWidget;
  }
}



// 2. 通過構建方法,把資料放到 InheritedWidget中
class _InheritedWidgetHomeState<InheritedWidgetHome> extends State {
  int count = 0;

  void _incrementCounter() => setState(() {
    count++;
  });

  @override
  Widget build(BuildContext context) {
    return CountContainer(
      mode: this,
      function: _incrementCounter,
      child: CountWidget(),
    );
  }
}

// 3. 在子 widget 通過 CountContainer.of方法,獲取到自定義的 InheritedWidget,並從中取得共享的資料
class CountWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    CountContainer state = CountContainer.of(context);
    return Scaffold(
      appBar: AppBar(
        title: Text("InheritedWidget demo"),
      ),
      body: Text("current count is ${state.mode.count}"),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: state.function,
      ),
    );
  }
}
複製程式碼

可以看到,InheritedWidget 的資料流動方式是從父 Widget 到子 Widget 逐層傳遞。

  1. 首先把通過建構函式需要共享的資料放到 InheritedWidget 中,然後提供一個靜態方法,返回自身;
  2. 然後在把自定義的 InheritedWidget做為父容器,傳入需要共享的資料;
  3. 最後在子widget 中,通過靜態方法獲取到 InheritedWidget 物件,自然就拿到裡面的資料了。

EventBus

無論是 InheritedWidget 還是 Notificaiton,它們的使用場景都需要依靠 Widget 樹,在使用起來就有點極限了,但Flutter 提供了一個更好的資料傳遞方法--EventBus,傳遞資料不再受到限制了。

在原生開發中,也有使用過 事件匯流排EventBus,Flutter 中實現跨元件通訊的機制也是一樣。它遵循釋出 / 訂閱模式,允許訂閱者訂閱事件,當釋出者觸發事件時,訂閱者和釋出者之間可以通過事件進行互動。釋出者和訂閱者之間無需有父子關係,甚至非 Widget 物件也可以釋出 / 訂閱。這些特點與其他平臺的事件匯流排機制是類似的。

由於 EventBus是第三方庫,所以需要引入:

event_bus: 2.0.0
複製程式碼

從第二個頁面,把資料回傳到第一個頁面

//建立公共的event bus
EventBus eventBus = EventBus();

class CustomEvent {
  String msg;

  CustomEvent(this.msg);
}

class _EventBusPager1State extends State {
  String message = "原來的資料";
  StreamSubscription subscription;

  @override
  void initState() {
    subscription = eventBus.on<CustomEvent>().listen((event) {
      setState(() {
        message = event.msg;
      });
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("EventBusPager1"),
      ),
      body: Center(
        child: Text(message),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.open_in_browser),
        onPressed: () => Navigator.push(
            context, MaterialPageRoute(builder: (context) => EventBusPager2())),
      ),
    );
  }

  @override
  void dispose() {
    subscription.cancel();
    super.dispose();
  }
}

class EventBusPager2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("EventBusPager2"),
      ),
      body: Center(
        child: Text("EventBusPager1"),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.send),
        onPressed: () {
          eventBus.fire(CustomEvent("data from page 2"));
          Navigator.pop(context);
        },
      ),
    );
  }
}
複製程式碼

QQ20210321-192448.gif

總結

通過學習瞭解了 在Flutter 中如何傳遞資料的,大致分為四種方式:

  1. 通過屬性,一層一層往下傳
  2. 通過 把資料寫到 InheritedWidget的子類,然後把共享的資料放到裡面,並提獲取自身的供靜態方法,在需要的地方通過靜態方法獲取到 InheritedWidget物件,並獲取資料,這種方式是能從父widget 傳遞到子widget;
  3. 通過 Notifaction 傳送訊息,然後再父 widget 進行監聽;
  4. 通過eventBus ,通過釋出 / 訂閱模式,來完成資料的傳遞,也是開發中常用的。

相關文章