Flutter - 數據共享,通訊,狀態管理 - 01 - InheritedWidget

開始介紹inheritedWidget以前,先來介紹一個 知識點 爲後面的內容作鋪墊html

先介紹ancestorWidgetOfExactTypegit

1. 先看下 Demo 代碼結構

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

能夠說是目前 全網 最完整的demo演示了c#

2. ancestorWidgetOfExactType

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

通常來講,inheritFromWidgetOfExactType更有用,由於繼承的widget將在更改時觸發消費者從新構建。ancestorWidgetOfExactType適用於交互事件處理程序(例如手勢回調)或執行一次性任務,例如斷言您擁有或不具備做爲特定類型的父widgetwidget的構建方法的返回值不該該依賴於該方法返回的值,由於若是該方法的返回值發生更改,構建上下文將不會從新生成。這可能會致使生成方法中使用的數據發生更改,可是沒有從新生成widget微信

總結一下上面的意思:markdown

  1. ancestorWidgetOfExactType 通常用於 斷言,是否有特定類型的父widget
  2. ancestorWidgetOfExactType能夠用來獲取父widget的一些信息
  3. 若是想要根據方法的返回值來判斷是否從新構建,ancestorWidgetOfExactType並不適用

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

1. 斷言

這邊就不給出本身的demo,直接看源碼,源碼的應用場景說明一切,這邊也是官方翻譯的最好證實less

從上圖能夠看出,這是 Hero的源碼,作了斷言,斷言就寫到這裏

2. 獲取父widget的一些信息

1. 代碼結構

2. 運行效果

WidgeC是一個 加號按鈕, 點擊了WidgeC,獲取HomepageState 調用incrementCounter 方法, , widgetAwidgetBwidgetC 會從新build,看下圖ide

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. 代碼結構

2. 運行效果

注意看下圖,當我點擊了1 - AncestorWidgetOfExactType02 演示 按鈕,進入 demo頁面,從右側能夠看出,由於是第一次進去,因此所有widget都進行了build操做,可是當我 點擊 了Add item按鈕,TopPage02rebuild可是WidgetAWidgetBWidgetC,並不會進行rebuild

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. 代碼結構

2. 效果圖

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

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. 代碼結構

2. 效果圖

WidgeAWidgeBFlatButton是處於同一級別的

從下圖能夠看到 當我點擊了click mewidgetB並不會被 rebuild, 可是 WidgeAFlatButtonrebuild,這樣解決了WidgeB rebuild的問題,可是 FlateButton 並不涉及到 頁面的數據刷新,若是我想要讓 FlatButton也不rebuild呢? 看constdemo02

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. 代碼結構

2. 效果圖

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

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. 代碼結構

2. 效果圖

ShareInheritedof方法 ,增長了 是否 rebuild的參數 你會發現 ,如今只有WidgeArebuild ,完美啊

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

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. 代碼結構

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

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. 代碼結構

2. 效果圖

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

若是想要 讓 Widge C no rebuild,看out demo 03

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. 代碼結構

2. 效果圖

由於這個demoPerfomance 界面展現不夠形象,就直接展現輸出log查看結果比較形象

能夠發現 Widget A rebuild, Widget B Widget C no rebuild ,完美啊 ,寫到這裏已經要吐了

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/…

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

點擊下面閱讀原文,用電腦看,有目錄,更舒服哦
相關文章
相關標籤/搜索