Flutter - 資料共享,通訊,狀態管理 - 01 - InheritedWidget

_樟清_發表於2019-06-27

開始介紹inheritedWidget之前,先來介紹一個 知識點 為後面的內容做鋪墊

先介紹ancestorWidgetOfExactType

1. 先看下 Demo 程式碼結構

demo地址: github.com/LZQL/flutte…

可以說是目前 全網 最完整的demo演示了

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

2. ancestorWidgetOfExactType

返回給定型別的最近父widget,該widget的型別必須是具體widget的子類。

一般來說,inheritFromWidgetOfExactType更有用,因為繼承的widget將在更改時觸發消費者重新構建。ancestorWidgetOfExactType適用於互動事件處理程式(例如手勢回撥)或執行一次性任務,例如斷言您擁有或不具有作為特定型別的父widgetwidget的構建方法的返回值不應該依賴於該方法返回的值,因為如果該方法的返回值發生更改,構建上下文將不會重新生成。這可能會導致生成方法中使用的資料發生更改,但是沒有重新生成widget

總結一下上面的意思:

  1. ancestorWidgetOfExactType 一般用於 斷言,是否有特定型別的父widget
  2. ancestorWidgetOfExactType可以用來獲取父widget的一些資訊
  3. 如果想要根據方法的返回值來判斷是否重新構建,ancestorWidgetOfExactType並不適用

下面會分別給出 上面的3點總結來給出demo場景

1. 斷言

這邊就不給出自己的demo,直接看原始碼,原始碼的應用場景說明一切,這邊也是官方翻譯的最好證明

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget
從上圖可以看出,這是Hero的原始碼,做了斷言,斷言就寫到這裡

2. 獲取父widget的一些資訊

1. 程式碼結構

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

2. 執行效果

WidgeC是一個 加號按鈕, 點選了WidgeC,獲取HomepageState 呼叫incrementCounter 方法, , widgetAwidgetBwidgetC 會重新build,看下圖

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

3. 具體程式碼

程式碼位置 ancestor01.dart

/// ancestorWidgetOfExactType 獲取父widget的一些資訊
class MyAncestorTree01 extends StatefulWidget {
  @override
  _MyAncestorTree01State createState() => _MyAncestorTree01State();
}

class _MyAncestorTree01State extends State<MyAncestorTree01> {
  @override
  Widget build(BuildContext context) {
    return TopPage01();
  }
}

class TopPage01 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Demo'),
        ),
        body: HomePage(),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  final HomePageState state = HomePageState();

  @override
  HomePageState createState() {
    return state;
  }
}

class HomePageState extends State<HomePage> {
  int counter = 0;

  void incrementCounter() {

    setState(() {
      counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          WidgetA(),
          WidgetB(),
          WidgetC(),
        ],
      ),
    );
  }
}

class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    /// 獲取 HomePageState 來獲取 counter
    final HomePage widget =
        context.ancestorWidgetOfExactType(HomePage);
    final HomePageState state = widget?.state;

    return Center(
      child: Text(
        '${state == null ? 0 : state.counter}',
        style: Theme.of(context).textTheme.display1,
      ),
    );
  }
}

class WidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text('Widget  B  Text');
  }
}

class WidgetC extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    /// 獲取 HomePageState 來 呼叫 加法操作
    final HomePage widget = context.ancestorWidgetOfExactType(HomePage);
    final HomePageState state = widget?.state;

    return RaisedButton(
      onPressed: () {
        state?.incrementCounter();
      },
      child: Icon(Icons.add),
    );
  }
}
複製程式碼

3. 子widget無法檢測到父widget的更改

demo 用來演示 子widget無法檢測到父widget的更改 的具體情況 為後面的內容做鋪墊 我將ancestorWidgetOfExactType封裝了一個 of 方法放到了TopPage02裡面, 與 2. 獲取父widget 的一些資訊 的使用場景不同。

1. 程式碼結構

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

2. 執行效果

注意看下圖,當我點選了1 - AncestorWidgetOfExactType02 演示 按鈕,進入 demo頁面,從右側可以看出,因為是第一次進去,所以全部widget都進行了build操作,但是當我 點選 了Add item按鈕,TopPage02rebuild但是WidgetAWidgetBWidgetC,並不會進行rebuild

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

3. 具體程式碼

程式碼位置ancestor02.dart

/// ancestorWidgetOfExactType
/// 子widget無法檢測到父widget的更改
/// (父widget rebuild 子widget no rebuild)
class MyAncestorTree02 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new TopPage02(
      child: new Scaffold(
        appBar: new AppBar(
          title: new Text('Title'),
        ),
        body: new Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            new WidgetA(),
            new WidgetB(),
            new WidgetC(),
          ],
        ),
      ),
    );
  }
}

class Item {
  String reference;

  Item(this.reference);
}

class TopPage02 extends StatefulWidget {
  TopPage02({
    Key key,
    this.child,
  }) : super(key: key);

  final Widget child;

  final TopPage02State state = new TopPage02State();

  @override
  TopPage02State createState() {
    return state;
  }

  static TopPage02State of(BuildContext context) {
    /// 通過從 TopPage02 的 context 得到樹結構來返回第一個 TopPage02State
    return (context.ancestorWidgetOfExactType(TopPage02)
            as TopPage02)
        .state;

  }
}

class TopPage02State extends State<TopPage02> {

  List<Item> _items = <Item>[];

  int get itemsCount => _items.length;

  void addItem(String reference) {
    setState(() {
      _items.add(new Item(reference));
    });
  }

  @override
  Widget build(BuildContext context) {
    return widget.child;
  }
}

class WidgetA extends StatefulWidget {
  @override
  _WidgetAState createState() => _WidgetAState();
}

class _WidgetAState extends State<WidgetA> {
  @override
  Widget build(BuildContext context) {
    final TopPage02State state = TopPage02.of(context);
    return new Center(
      child: new RaisedButton(
        child: new Text('WidgetA :Add Item'),
        onPressed: () {
          /// 這邊呼叫 addItem 方法,但是WidgetA,WidgetB,WidgetC
          /// 並不會 build
          /// 這就說明了:widget的構建方法的返回值不應該依賴於該方法返回的值,
          /// 因為如果該方法的返回值發生更改,構建上下文將不會重新生成。
          /// 這可能會導致生成方法中使用的資料發生更改,但是沒有重新生成widget
          state.addItem('new item');
        },
      ),
    );
  }
}

class WidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final TopPage02State state = TopPage02.of(context);
    return new Text('widgetB itemCount:${state.itemsCount}');
  }
}

class WidgetC extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Text('I am Widget C');
  }
}
複製程式碼

3. InheritedWidget

1. 什麼是 InheritedWidget

特性:

  1. InheritedWidget是一個可以在樹中高效地向下傳遞資料的元件:我們可以在以InheritedWidget 為節點的樹下任一Widget中呼叫 BuildContext.inheritFromWidgetOfExactType 來獲取離其最近的 InheritedWidget例項
  2. 當以上面這種方式(呼叫inheritFromWidgetOfExactType方法時)被引用後,每當InheritedWidget自身的狀態改變時,會導致 “consumer”(呼叫inheritFromWidgetOfExactType方法的這個Child) 重新build

第一點:在Flutter中,正是通過InheritedWidget來共享應用主題(Theme)和Locale(當前語言環境)資訊的 第二點:解決了 上面提到ancestorWidgetOfExactType 的第三點不能通過返回值來進行重新build的情況

InheritedWidget的在Widget樹中資料傳遞方向是從上到下的,這和Notification的傳遞方向正好相反。(後面系列文章介紹Notification)

2. didChangeDependencies

StatefulWidgetState物件有一個回撥didChangeDependencies,它會在“依賴”發生變化時被Flutter Framework呼叫。而這個“依賴”指的就是是否使用了父widgetInheritedWidget的資料,如果使用了,則代表有依賴,如果沒有使用則代表沒有依賴。這種機制可以使子元件在所依賴的主題、locale等發生變化時有機會來做一些事情。

4. 通過 2 - InheritedWidget演示01 ,發現問題

這個例子 是參照 book.flutterchina.club/chapter7/in… 寫的一樣,相信很多人都看過

1. 程式碼結構

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

2. 效果圖

當我點選了 click me按鈕 加1,widgetAwidgeB,RaisedButton會重新build

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

3. 程式碼

程式碼位置:inheritedwidget01.dart

/// InheritedWidget01 , 會導致 `widgetA`` widgeB`,`RaisedButton `會重新`build`
  class InheritedWidgetTest01 extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return InheritedWidgetTest01State();
  }
}

class InheritedWidgetTest01State extends State<InheritedWidgetTest01> {
  int tmpData = 0;

  @override
  Widget build(BuildContext context) {
    print('InheritedWidgetTest01 build');
    return Scaffold(
      body: Center(
        child: ShareInherited(
          data: tmpData,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              WidgetA(),
              WidgetB(),
              RaisedButton(
                child: Text("Click me"),
                onPressed: () {
                  setState(() {
                    print('onPressed');
                    tmpData += 1;
                  });
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class ShareInherited extends InheritedWidget {
  final int data; //需要在子樹中共享的資料,儲存點選次數

  ShareInherited({@required this.data, @required Widget child}) : super(child: child) {
    print('ShareInherited construct');
  }

  /// 允許所有子 widget 通過包含的 context 獲得最近的 ShareInherited 例項
  /// 定義一個便捷方法,方便子樹中的widget獲取共享資料
  /// 在內部,除了簡單地返回 ShareInherited 例項外,它還訂閱消費者 widget 以便用於通知更改
  static ShareInherited of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(ShareInherited);
  }

  /// 用來告訴 InheritedWidget 如果對資料進行了修改,
  /// 是否必須將通知傳遞給所有子 widget(已註冊/已訂閱)
  @override
  bool updateShouldNotify(ShareInherited oldWidget) {
    // 如果返回true,則子樹中依賴(build函式中有呼叫)本widget
    // 的子widget的`state.didChangeDependencies`會被呼叫
    bool result = oldWidget.data != this.data;
    print('ShareInherited updateShouldNotify result = $result');
    return result;
  }
}

class WidgetA extends StatefulWidget {
  @override
  _WidgetAState createState() => _WidgetAState();
}

class _WidgetAState extends State<WidgetA> {
  @override
  Widget build(BuildContext context) {
    print('WidgetA build');
    int data = ShareInherited.of(context).data;
    return Text('WidgetA data = $data');
  }

  @override
  void didChangeDependencies() {
    print('WidgetA didChangeDependencies');
    super.didChangeDependencies();
  }
}

class WidgetB extends StatefulWidget {
  @override
  _WidgetBState createState() => _WidgetBState();
}

class _WidgetBState extends State<WidgetB> {
  @override
  Widget build(BuildContext context) {
    print('WidgetB build');
    return Text('WidgetB');
  }

  @override
  void didChangeDependencies() {
    print('WidgetB didChangeDependencies');
    super.didChangeDependencies();
  }
}
複製程式碼

4. 發現的現象

WidgetA呼叫了inheritFromWidgetOfExactType方法,獲得了存放在 ShareInherited物件裡的data資料並顯示在WidgetA內容上,同時使得 WidgetAShareInherited產生關聯;

當點選 RaisedButton觸發InheritedWidgetTest01狀態更新時,InheritedWidgetTest01Statebuild 方法回撥,重新構建 ShareInherited物件傳入新值,由於data發生變化,ShareInherited的方法updateShouldNotify中返回了true,最終使得與ShareInherited關聯的WidgetA觸發reBuild

當我們執行並點選RaisedButton後,頁面表現得確實如上述所示,WidgetA的內容由 WidgetA data = 0變為了WidgetA data = 1,似乎 InheritedWidget 正確的使用方式正是如此,但是log裡輸出的卻是如下:

I/flutter (11303): onPressed
I/flutter (11303): InheritedWidgetTest01 build
I/flutter (11303): ShareInherited construct
I/flutter (11303): ShareInherited updateShouldNotify result = true
I/flutter (11303): WidgetA didChangeDependencies
I/flutter (11303): WidgetA build
I/flutter (11303): WidgetB build
複製程式碼

可以看到,結合前面的程式碼邏輯分析,理論上只有 WidgetA 才會reBuild,而現在卻產生了I/flutter (11303): WidgetB build這條記錄。這是為什麼呢?

5. 原因分析

其實可以從前面提到的特性2找到答案。其中說到:InheriteWidget狀態發生變化時會rebuild相關的child。 我們知道,flutterWidget被標識為了@immutable,即是不可變的,那麼所謂的狀態發生變化就意味著InheriteWidget重新構建,由於前面程式碼中在InheriteWidget構造時同時也構造的其child物件,因此當InheriteWidget重新構建時也會導致child跟著重新構建,這樣也就失去了“rebuild相關的child”的意義,

也就是說,要想特性2生效,需要保證InheriteWidget節點下的樹不會被重新構建。

5. 解決方法1:使用 const Widget

InheriteWidgetchild轉化為const,這樣即使在重建 InheriteWidget時,由於其child得到的是同一個物件,也就不會導致這個子樹重建,選擇性reBuild也就得到了保證。但是由於const特性,相關的引數也必須是常量,因此需要重寫或修改的程式碼量相對較多,因此更推薦解決方法2的做法,這個方法在後面回寫

1. const demo 01,發現問題

1. 程式碼結構

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

2. 效果圖

WidgeAWidgeBFlatButton是處於同一級別的

從下圖可以看到 當我點選了click mewidgetB並不會被 rebuild, 但是 WidgeAFlatButtonrebuild,這樣解決了WidgeB rebuild的問題,但是 FlateButton 並不涉及到 頁面的資料重新整理,如果我想要讓 FlatButton也不rebuild呢? 看constdemo02

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

3. 程式碼

程式碼位置:inheritedwidget_const_01.dart

/// 使用 const
/// Widget A ,FlatButton rebuild, Widget B  no rebuild
class InheritedWidgetConst01 extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return InheritedWidgetConst01State();
  }
}

class InheritedWidgetConst01State extends State<InheritedWidgetConst01> {
  int tmpData = 0;



  @override
  Widget build(BuildContext context) {
    print('InheritedWidgetTest02 build');
    return Scaffold(
      body: Center(
        child: ShareInherited(
          data: tmpData,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const WidgetA(),
              const WidgetB(),
              FlatButton(
                child: Text("Click me"),
                onPressed: () {
                  setState(() {
                    print('onPressed');
                    tmpData += 1;
                  });
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class ShareInherited extends InheritedWidget {
  final int data;

  ShareInherited({this.data, @required Widget child}) : super(child: child) {
    print('ShareInherited construct');
  }

  @override
  bool updateShouldNotify(ShareInherited oldWidget) {
    bool result = oldWidget.data != this.data;
    print('ShareInherited updateShouldNotify result = $result');
    return result;
  }

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

class WidgetA extends StatelessWidget {

  const WidgetA();

  @override
  Widget build(BuildContext context) {
    print('WidgetA build');
    int data = ShareInherited.of(context).data;
    return Text('WidgetA data = $data');
  }
}

class WidgetB extends StatelessWidget {

  const WidgetB();

  @override
  Widget build(BuildContext context) {
    print('WidgetB build');
    return Text('WidgetB');
  }
}
複製程式碼

2. const demo 02,繼續發現問題

1. 程式碼結構

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

2. 效果圖

你會發現,WidgeAWidgeC還是rebuild , 要解決這個問題,就需要用到 文章頂部提到的ancestorWidgetOfExactType了,看 const demo 03,終極寫法

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

3. 程式碼

程式碼位置 inheritedwidget_const_02.dart

/// 使用 const
/// Widget A ,Widget C  rebuild, Widget B  no rebuild
class InheritedWidgetConst02 extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return InheritedWidgetConst02State();
  }
}

class InheritedWidgetConst02State extends State<InheritedWidgetConst02> {
  int tmpData = 0;

  void addItem(){
    setState(() {
      tmpData++;
    });
  }

  @override
  Widget build(BuildContext context) {
    print('InheritedWidgetTest02 build');
    return Scaffold(
      body: Center(
        child: ShareInherited(
          data: tmpData,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const WidgetA(),
              const WidgetB(),
              WidgetC(),
            ],
          ),
          state: this,
        ),
      ),
    );
  }
}

class ShareInherited extends InheritedWidget {
  final int data;
  final InheritedWidgetConst02State state;
  ShareInherited({this.data, @required Widget child,this.state}) : super(child: child) {
    print('ShareInherited construct');
  }

  @override
  bool updateShouldNotify(ShareInherited oldWidget) {
    bool result = oldWidget.data != this.data;
    print('ShareInherited updateShouldNotify result = $result');
    return result;
  }

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

class WidgetA extends StatelessWidget {

  const WidgetA();

  @override
  Widget build(BuildContext context) {
    print('WidgetA build');
    int data = ShareInherited.of(context).data;
    return Text('WidgetA data = $data');
  }
}

class WidgetB extends StatelessWidget {

  const WidgetB();

  @override
  Widget build(BuildContext context) {
    print('WidgetB build');
    return Text('WidgetB');
  }
}



class WidgetC extends StatefulWidget {
  @override
  _WidgetCState createState() => _WidgetCState();
}

class _WidgetCState extends State<WidgetC> {
  @override
  Widget build(BuildContext context) {
    print('Widge C build');
    InheritedWidgetConst02State state = ShareInherited.of(context).state;

    return FlatButton(
      child: Text("Click me"),
      onPressed: () {
        setState(() {
          print('onPressed');
          state.addItem();
        });
      },
    );
  }
}
複製程式碼

3. const demo 03,終極寫法

1. 程式碼結構

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

2. 效果圖

ShareInheritedof方法 ,增加了 是否 rebuild的引數 你會發現 ,現在只有WidgeArebuild ,完美啊

  static ShareInherited of([BuildContext context, bool rebuild = true]) {
    return (rebuild
        ? context.inheritFromWidgetOfExactType(ShareInherited)
        : context.ancestorWidgetOfExactType(ShareInherited) );
  }
複製程式碼

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

3. 程式碼

程式碼位置inheritedwidget_const_03.dart

/// 使用 const
/// Widget A rebuild, Widget B Widget C no rebuild
class InheritedWidgetConst03 extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return InheritedWidgetConst03State();
  }
}

class InheritedWidgetConst03State extends State<InheritedWidgetConst03> {
  int tmpData = 0;

  void addItem() {
    setState(() {
      tmpData++;
    });
  }

  @override
  Widget build(BuildContext context) {
    print('InheritedWidgetTest02 build');
    return Scaffold(
      body: Center(
        child: ShareInherited(
          data: tmpData,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const WidgetA(),
              const WidgetB(),
              const WidgetC(),
            ],
          ),
          state: this,
        ),
      ),
    );
  }
}

class ShareInherited extends InheritedWidget {
  final int data;
  final InheritedWidgetConst03State state;

  ShareInherited({this.data, @required Widget child, this.state})
      : super(child: child) {
    print('ShareInherited construct');
  }

  @override
  bool updateShouldNotify(ShareInherited oldWidget) {
    bool result = oldWidget.data != this.data;
    print('ShareInherited updateShouldNotify result = $result');
    return result;
  }

  static ShareInherited of([BuildContext context, bool rebuild = true]) {
    return (rebuild
        ? context.inheritFromWidgetOfExactType(ShareInherited)
        : context.ancestorWidgetOfExactType(ShareInherited) );
  }
}

class WidgetA extends StatelessWidget {

  const WidgetA();

  @override
  Widget build(BuildContext context) {
    print('WidgetA build');
    int data = ShareInherited
        .of(context)
        .data;
    return Text('WidgetA data = $data');
  }
}

class WidgetB extends StatelessWidget {

  const WidgetB();

  @override
  Widget build(BuildContext context) {
    print('WidgetB build');
    return Text('WidgetB');
  }
}

class WidgetC extends StatelessWidget {
  const WidgetC();
  @override
  Widget build(BuildContext context) {
    print('Widge C build');
    InheritedWidgetConst03State state = ShareInherited.of(context,false).state;

    return FlatButton(
      child: Text("Click me"),
      onPressed: () {
        print('onPressed');
        state.addItem();
      },
    );
  }
}
複製程式碼

6. 解決方法2:上移Child物件到InheriteWidgetParent Widget

1. out demo 01,發現問題

1. 程式碼結構

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

2. 效果圖

具體看程式碼吧,稍顯複雜 ,這邊命名不是很規範,懶得改了, 太累了寫demo,看程式碼可以知道

class ShareInherited extends StatelessWidget
複製程式碼

這裡的ShareInherited 是一個 StatelessWidget ,也就是 上移了Child物件到InheriteWidgetParent Widget

class _ShareInherited extends InheritedWidget
複製程式碼

_ShareInherited 才是 具體的 InheritedWidget

看下圖可知 Widget A ,FlatButton rebuild, Widget B no rebuild

這時候我們 一樣的 把 FlatButton 放到 WidgetC 然後上移到 InheriteWidgetParent Widget 看能不能實現讓 WidgeC no rebuild

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

3. 程式碼

程式碼位置inheritedwidget_out_01.dart

/// 上移`Child`物件到`InheriteWidget``Parent Widget`
/// Widget A ,FlatButton rebuild, Widget B  no rebuild
class InheritedWidgetOut01 extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return InheritedWidgetOut01State();
  }
}

class InheritedWidgetOut01State extends State<InheritedWidgetOut01> {
  @override
  Widget build(BuildContext context) {

    return Scaffold(
      body: MyWidget(
          Column(
            children: <Widget>[
              WidgetA(),
              WidgetB()
            ],
          )
      ),
    );
  }
}

class MyWidget extends StatefulWidget {

  final Widget child;
  MyWidget(this.child);

  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {

  int tempData = 0;

  @override
  Widget build(BuildContext context) {
    return ShareInherited(
      data: tempData,
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            widget.child,
            FlatButton(
                  child: Text("Click me"),
                  onPressed: () {
                    setState(() {
                      print('onPressed');
                      tempData += 1;
                    });
                  },
                )
          ],
        ),
      ),
    );
  }
}

class ShareInherited extends StatelessWidget {
  final int data;
  final Widget child;


  ShareInherited({
    Key key,
    this.data,
    this.child
  }): assert(child != null),
        assert(data != null),
        super(key: key);

  static int of(BuildContext context) {
    final _ShareInherited inheritedTheme = context.inheritFromWidgetOfExactType(_ShareInherited);
    return inheritedTheme.shareInherited.data;
  }

  @override
  Widget build(BuildContext context) {
    return _ShareInherited(shareInherited:this , child: child,);
  }
}

class _ShareInherited extends InheritedWidget{

  final ShareInherited shareInherited;

  _ShareInherited({
    Key key,
    @required this.shareInherited,
    @required Widget child,
  }):assert(shareInherited != null),
  super(key: key, child: child);

  @override
  bool updateShouldNotify(_ShareInherited oldWidget) {
    return shareInherited.data != oldWidget.shareInherited.data;
  }

}

class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('WidgetA build');
    int data = ShareInherited.of(context);
    return Text('WidgetA data = $data');
  }
}

class WidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('WidgetB build');
    return Text('WidgetB');
  }
}
複製程式碼

2. out demo 02,繼續發現問題

1. 程式碼結構

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

2. 效果圖

你會發現 Widget A ,Widget C rebuild, Widget B no rebuild

如果想要 讓 Widge C no rebuild,看out demo 03

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

3. 程式碼

程式碼位置inheritedwidget_out_02.dart

/// 上移`Child`物件到`InheriteWidget``Parent Widget`
/// Widget A ,Widget C  rebuild, Widget B  no rebuild
class InheritedWidgetOut02 extends StatefulWidget {
  @override
  _InheritedWidgetOut02State createState() => new _InheritedWidgetOut02State();
}

class _InheritedWidgetOut02State extends State<InheritedWidgetOut02> {
  @override
  Widget build(BuildContext context) {
    return new MyInheritedWidget(
      child: new Scaffold(
        appBar: new AppBar(
          title: new Text('Title'),
        ),
        body: Center(
          child: new Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[new WidgetA(), new WidgetB(), new WidgetC()],
          ),
        ),
      ),
    );
  }
}

class MyInheritedWidget extends StatefulWidget {
  MyInheritedWidget({
    Key key,
    this.child,
  }) : super(key: key);

  final Widget child;

  @override
  MyInheritedWidgetState createState() => new MyInheritedWidgetState();

  static MyInheritedWidgetState of([BuildContext context]) {
    return (context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited).data;
    // 通過從 MyInheritedWidget 的 context 得到樹結構來返回第一個 MyInheritedWidgetState
  }
}

class MyInheritedWidgetState extends State<MyInheritedWidget> {

  int tempData = 0;

  /// Helper method to add an Item
  void addItem() {
    setState(() {
      tempData++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new _MyInherited(
      data: this,
      child: widget.child,
    );
  }
}

class _MyInherited extends InheritedWidget {
  _MyInherited({
    Key key,
    @required Widget child,
    @required this.data,
  }) : super(key: key, child: child);

  final MyInheritedWidgetState data;

  @override
  bool updateShouldNotify(_MyInherited oldWidget) {
    return true;
  }
}

class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('Widget A build');
    final MyInheritedWidgetState state = MyInheritedWidget.of(context);
    return Text('WidgetA data = ${state.tempData}');
  }
}

class WidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('Widget B build');
    return Text('WidgetB');
  }
}

class WidgetC extends StatefulWidget {
  @override
  _WidgetCState createState() => _WidgetCState();
}

class _WidgetCState extends State<WidgetC> {
  @override
  Widget build(BuildContext context) {
    print('Widget C build');
    final MyInheritedWidgetState state = MyInheritedWidget.of(context);
    return RaisedButton(
      child: Text("Click me"),
      onPressed: () {
        print('onPressed');
        state.addItem();
      },
    );
  }
}
複製程式碼

3. out demo 03,終極寫法

1. 程式碼結構

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

2. 效果圖

因為這個demoPerfomance 介面展示不夠形象,就直接展示輸出log檢視結果比較形象

可以發現 Widget A rebuild, Widget B Widget C no rebuild ,完美啊 ,寫到這裡已經要吐了

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

3. 程式碼

程式碼位置inheritedwidget_out_03.dart

/// 上移`Child`物件到`InheriteWidget``Parent Widget`
/// Widget A rebuild, Widget B Widget C no rebuild
class InheritedWidgetOut03 extends StatefulWidget {
  @override
  _InheritedWidgetOut03State createState() => new _InheritedWidgetOut03State();
}

class _InheritedWidgetOut03State extends State<InheritedWidgetOut03> {
  @override
  Widget build(BuildContext context) {
    return new MyInheritedWidget(
      child: new Scaffold(
        appBar: new AppBar(
          title: new Text('Title'),
        ),
        body: Center(
          child: new Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[new WidgetA(), new WidgetB(), new WidgetC()],
          ),
        ),
      ),
    );
  }
}

class MyInheritedWidget extends StatefulWidget {
  MyInheritedWidget({
    Key key,
    this.child,
  }) : super(key: key);

  final Widget child;

  @override
  MyInheritedWidgetState createState() => new MyInheritedWidgetState();

  static MyInheritedWidgetState of(
      [BuildContext context, bool rebuild = true]) {
    return (rebuild
            ? context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited
            : context.ancestorWidgetOfExactType(_MyInherited) as _MyInherited)
        .data;
    // 通過從 MyInheritedWidget 的 context 得到樹結構來返回第一個 MyInheritedWidgetState
  }
}

class MyInheritedWidgetState extends State<MyInheritedWidget> {

  int tempData = 0;

  /// Helper method to add an Item
  void addItem() {
    setState(() {
      tempData++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new _MyInherited(
      data: this,
      child: widget.child,
    );
  }
}

class _MyInherited extends InheritedWidget {
  _MyInherited({
    Key key,
    @required Widget child,
    @required this.data,
  }) : super(key: key, child: child);

  final MyInheritedWidgetState data;

  @override
  bool updateShouldNotify(_MyInherited oldWidget) {
    return true;
  }
}

class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('Widget A build');
    final MyInheritedWidgetState state = MyInheritedWidget.of(context);
    return Text('WidgetA data = ${state.tempData}');
  }
}

class WidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('Widget B build');
    return Text('WidgetB');
  }
}

class WidgetC extends StatefulWidget {
  @override
  _WidgetCState createState() => _WidgetCState();
}

class _WidgetCState extends State<WidgetC> {
  @override
  Widget build(BuildContext context) {
    print('Widget C build');
    final MyInheritedWidgetState state = MyInheritedWidget.of(context, false);
    return RaisedButton(
      child: Text("Click me"),
      onPressed: () {
        print('onPressed');
        state.addItem();
      },
    );
  }
}
複製程式碼

7. 兩種解決方法,在原始碼中的應用

方法1 : 可以檢視TickerMode這個類

方法2: 可以檢視 Theme

8. 參考文章

book.flutterchina.club/chapter7/in…

qiita.com/ko2ic/items…

juejin.im/post/5c768a…

linjiang.tech/2019/02/25/…

掃一掃,關注我的微信公眾號
都是一些個人學習筆記

Flutter - 資料共享,通訊,狀態管理 - 01 -  InheritedWidget

點選下面閱讀原文,用電腦看,有目錄,更舒服哦

相關文章