InhertedWidget和它的兒子們

0x00 概念介紹

簡單一點說html

  • inhertedwidget 是一個widget,跟其餘widget不同的地方是,他能夠在他所持有的child中共享本身的數據。如Theme。
  • 應用場景:app的複雜度愈來愈大,對於數據之間的傳遞,若是都是根據dic或者model做爲widget內部的參數傳遞,是不友好的方式。正常的想法,此時應該有個數據中心,或eventbus,用於數據傳遞和取用,而在flutter中是inhertwidget
  • 實現: 內部實現數據更新,自動通知的方式,從而自動刷新界面
  • 寫法: 見下面例子

對於趕時間的同窗看到這裏就能夠回去搬磚了。下面留給還有五分鐘時間瀏覽的同窗。數據庫

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

做爲一名高效的搬磚工,先看看它說了啥bash

0x01 前情提要

code4flutter

當應用變得更大時,小部件樹,變得更復雜,傳遞和訪問數據,會變得很麻煩。 若是你有四個或五個小部件一個接一個地嵌套, 而且您須要從頂部獲取一些數據。將它添加到全部這些構造函數中,以及全部這些構建方法。網絡

然而我只是想到達widget來獲取數據。 不想一級一級傳遞數據。怎麼辦?幸運的是,有一個小部件類型容許這樣。 它叫作InheritedWidget。app

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的顏色加深less

效果圖以下ide

code4flutter

0x02 使用建議

InheritedWidgets一般包含一個名爲的靜態方法 of

它爲您調用InheritWidget的精確類型方法。函數

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

final 不可變性

例子中FrogColor 裏面的color就是一個final類型,不能夠改變ui

只能替換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

相關文章
相關標籤/搜索