InhertedWidget和它的繼承者們

code4flutter發表於2019-07-24

0x00 概念介紹

簡單一點說

  • inhertedwidget 是一個widget,跟其他widget不一樣的地方是,他可以在他所持有的child中共享自己的資料。如Theme。
  • 應用場景:app的複雜度越來越大,對於資料之間的傳遞,如果都是根據dic或者model作為widget內部的引數傳遞,是不友好的方式。正常的想法,此時應該有個資料中心,或eventbus,用於資料傳遞和取用,而在flutter中是inhertwidget
  • 實現: 內部實現資料更新,自動通知的方式,從而自動重新整理介面
  • 寫法: 見下面例子

對於趕時間的同學看到這裡就可以回去搬磚了。下面留給還有五分鐘時間瀏覽的同學。

google 在flutter widget of the week 中介紹 inheritedwidget, 短短的兩個簡短的視訊,讓人看到了flutter的用心,外語一般的我也能把概念看得個大概。但是對於真正使用其上手開發的同學總覺得離實際開發距離有點遠,還是得編寫一下例子才能理解更深一點。

作為一名高效的搬磚工,先看看它說了啥

0x01 前情提要

code4flutter

當應用變得更大時,小部件樹,變得更復雜,傳遞和訪問資料,會變得很麻煩。 如果你有四個或五個小部件一個接一個地巢狀, 並且您需要從頂部獲取一些資料。將它新增到所有這些建構函式中,以及所有這些構建方法。

然而我只是想到達widget來獲取資料。 不想一級一級傳遞資料。怎麼辦?幸運的是,有一個小部件型別允許這樣。 它叫做InheritedWidget。

inherted_notify

0x02 使用方式

建立一個InhertWidget

class FrogColor extends InheritedWidget {
  const FrogColor({
    Key key,
    @required this.color,
    @required Widget child,
  }) : assert(color != null),
       assert(child != null),
       super(key: key, child: child);

  final Color color;

  static FrogColor of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(FrogColor) as FrogColor;
  }

  @override
  bool updateShouldNotify(FrogColor old) => color != old.color;
}
複製程式碼

建立子WidgetA B


class TestWidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final inheritedContext = FrogColor.of(context);
    return new Padding(
        padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
        child: Container(
          color: inheritedContext.color,
          height: 100,
          width: 100,
          child: Text('第一個widget'),
        ));
  }
}


class TestWidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final inheritedContext = FrogColor.of(context);
    return new Padding(
        padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
        child: Container(
          color: inheritedContext.color,
          height: 50,
          width: 50,
          child: Text('第二個widget'),
        ));
  }
}
複製程式碼

建立帶狀態的widgetC

class TestWidgetC extends StatefulWidget {
  TestWidgetC({Key key}) : super(key: key);

  _TestWidgetCState createState() => _TestWidgetCState();
}

class _TestWidgetCState extends State<TestWidgetC> {
 

  @override
  Widget build(BuildContext context) {
     final inheritedContext = FrogColor.of(context);
    // print(" 重建c CCC ");
    return Container(
        child: Container(
      color: inheritedContext.color,
      height: 200,
      width: 200,
      child: prefix0.Column(
        children: <Widget>[
          Text("第三個widget"),        
        ],
      ),
    ));
  }

  @override
  void didChangeDependencies() {
    print(" 更改依賴 CCC ");
    super.didChangeDependencies();
  }
}
複製程式碼

組裝


class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  Color color = Color.fromARGB(0, 0xff, 0xdd, 0xdd);
  Color color2 = Color.fromARGB(0, 0xff, 0xdd, 0xdd);

  void _incrementCounter() {
    setState(() {
      _counter = (_counter + 20) % 255;
      Color color = Color.fromARGB(_counter, 0xff, 0xdd, 0xdd);
      this.color = color;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
            new FrogColor(
              color: this.color,
              color2: this.color2,
              child: Column(
                children: <Widget>[
                  new TestWidgetA(),
                  new TestWidgetB(),
                  new TestWidgetC()
                ],
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

複製程式碼

本段程式碼實現了點選+號,數字增加,並且兩個widget的顏色加深

效果圖如下

code4flutter

0x02 使用建議

InheritedWidgets通常包含一個名為的靜態方法 of

它為您呼叫InheritWidget的精確型別方法。

   static FrogColor of(BuildContext context) {
     return context.inheritFromWidgetOfExactType(FrogColor) as FrogColor;
   }
複製程式碼

final 不可變性

例子中FrogColor 裡面的color就是一個final型別,不可以改變

只能替換InheritedWidget的欄位,通過重建整個widget 樹。 這個很重要!!! 只是意味著它無法重新分配。 但是並不意味著它不能在內部改變。

didChangeDependencies 改變時機

inhertedwidget改變了就會觸發,didChangeDependencies,對於耗時操作的業務如網路請求來說可以放置這裡。

從上例中可以做個試驗,在widgetC中移除 FrogColor.of(context) 這句話,可以看到,顏色不好隨著按鈕點選變色,另外也不會呼叫didChangeDependencies 這個方法了。但是widgetc還是會走build方法。

可以印證兩點,widget會重建,但是state不會重建,didChangeDespendice方法呼叫的時機是其依賴的上下文內容改變。

0x03 應用場景

  • Theme實際上是一種InheritedWidget。 Scaffold,Focus Scope等等也是如此。

  • 附加服務物件到InheritedWidget。 如開發資料庫的包裝器

  • Web API的代理或資產提供者。 服務物件可以有自己的內部狀態。 它可以啟動網路呼叫,任何事情。

0x04 繼承者們

老大,InheritedNotifier

繼承自Inhertedwidget,其值可以是被監聽的,並且只要值傳送通知就會通知依賴者。

使用場景有 ChangeNotifierValueNotifier

abstract class InheritedNotifier<T extends Listenable> extends InheritedWidget {

  const InheritedNotifier({
    Key key,
    this.notifier,
    @required Widget child,
  }) : assert(child != null),
       super(key: key, child: child);
 
  @override
  bool updateShouldNotify(InheritedNotifier<T> oldWidget) {
    return oldWidget.notifier != notifier;
  }

  @override
  _InheritedNotifierElement<T> createElement() => _InheritedNotifierElement<T>(this);
}
複製程式碼

老二, InheritedModel

繼承自 Inertedwidget的,允許客戶端訂閱值的子部分的更改。

就比InertedWidget多了一個必要方法updateShouldNotifyDependent,表示可以根據,部分內容的改變傳送依賴變更通知。

class ABModel extends InheritedModel<String> {
  ABModel({this.a, this.b, Widget child}) : super(child: child);

  final int a;
  final int b;

  @override
  bool updateShouldNotify(ABModel old) {
    return a != old.a || b != old.b;
  }

  @override
  bool updateShouldNotifyDependent(ABModel old, Set<String> aspects) {
    return (a != old.a && aspects.contains('a')) ||
        (b != old.b && aspects.contains('b'));
  }
  // ...
}
複製程式碼

一圖說明

inhertedmodel

參考

inhertedwidget 文件

原創不易,版權所有,轉載請備註 code4flutter.com

相關文章