Flutter functional components: InheritedWidget

Foreword

InheritedWidget provided a data transfer in the widget tree from top to bottom, sharing manner, for example at the root of the widget application through InheritedWidget a shared data, then it may be acquired in the shared data in the sub-widget. InheritedWidget is from top to bottom in the widget tree data transfer direction, and which Notification transmission direction is reversed.

Interface Description


The sample code

// 数据共享(InheritedWidget)

// InheritedWidget提供了一种数据在widget树中从上到下传递、共享的方式,例如在应用的根widget中通过InheritedWidget共享了一个数据,
// 那么便可以在子widget中来获取该共享的数据。InheritedWidget在widget树中数据传递方向是从上到下的,这和Notification的传递方向是相反的。

// InheritedWidget版本的计数器,用来演示InheritedWidget的功能特性
import 'package:flutter/material.dart';

class ShareDataWidget extends InheritedWidget{

  ShareDataWidget({
    @required this.data,
    Widget child
  }) :super(child: child);

  // 需要在子树中共享的数据,保存点击次数
  final int data;

  // 定义一个便捷方法,方便子树中的widget获取共享数据
  static ShareDataWidget of(BuildContext context){
    return context.inheritFromWidgetOfExactType(ShareDataWidget);
  }

  // 深入了解InheritedWidget
  // 现在来思考一下,如果只想在__TestWidgetState中引用ShareDataWidget数据,但却不希望在ShareDataWidget发生变化时调用__TestWidgetState的didChangeDependencies()方法应该怎么办?
  // 其实答案很简单,我们只需要将ShareDataWidget.of()的实现改一下即可
//  static ShareDataWidget of(BuildContext context){
//    return context.ancestorInheritedElementForWidgetOfExactType(ShareDataWidget).widget;
//  }

  // 该回调决定当data发生变化时,是否通知子树中依赖data的Widget
  @override
  bool updateShouldNotify(ShareDataWidget old){
    // 如果返回true,则子树中依赖本widget的子widget的state.didChangeDependencies会被调用
    return old.data == data;
  }
}

// 实现一个子组件_TestWidget,在其build方法中引用ShareDataWidget中的数据。同时,在其didChangeDependencies() 回调中打印日志
class _TestWidget extends StatefulWidget{
  
  __TestWidgetState createState() => __TestWidgetState();
}

class __TestWidgetState extends State<_TestWidget>{
  @override
  Widget build(BuildContext context){
    //使用InheWidget中的共享数据
    return Text(ShareDataWidget
        .of(context)
        .data
        .toString()
    );
  }

  // 问题:应该在didChangeDependencies()中做什么?
  // 一般来说,子widget很少会重写此方法,因为在依赖改变后framework也都会调用build()方法。
  // 但是,如果你需要在依赖改变后执行一些昂贵的操作,比如网络请求,这时最好的方式就是在此方法中执行,这样可以避免每次build()都执行这些昂贵操作。
  @override
  void didChangeDependencies(){
    super.didChangeDependencies();
    // 父widget中的InheritedWidget改变(即updateShouldNotify返回true)时会被调用
    // 如果build没有依赖InheritedWidget,则此回调不会被调用
    print('Dependencies change');
  }
}

// 创建一个按钮,每点击一次,就将ShareDataWidget的值自增
class InheritedWidgetTest extends StatefulWidget{
  _InheritedWidgetTestState createState() => _InheritedWidgetTestState();
}

class _InheritedWidgetTestState extends State<InheritedWidgetTest>{

  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('InheritedWidget'),
      ),
      body: Center(
        child: ShareDataWidget(
          data: count,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.only(bottom: 20.0),
                // 子widget中依赖ShareDataWidget
                child: _TestWidget(),
              ),
              RaisedButton(
                child: Text('Increment'),
                // 每点击一个,将count自增,然后build,ShareDataWidget的data将被更新
                onPressed: () => setState(() => ++count),
              )
            ],
          ),
        ),
      ),
    );
  }
}

to sum up

didChangeDependencies

When introduced before StatefulWidget, mentioned State callback object has a didChangeDependencies, it will be called Flutter Framework in the "dependency" change. And this "dependency" refers to the child widget is used the data of the parent widget in InheritedWidget! If used, it represents the dependent child widget that depend InheritedWidget; if no represents no dependencies. This mechanism allows subassembly depend upon InheritedWidget change to update itself! For example, when the theme, locale (language) and other changes, the method relies didChangeDependencies their child widget will be called.

In-depth understanding InheritedWidget

For the code below:

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

static ShareDataWidget of(BuildContext context){
    return context.ancestorInheritedElementForWidgetOfExactType(ShareDataWidget).widget;
  }

@override
InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType) {
  final InheritedElement ancestor = _inheritedWidgets == null ? null :  _inheritedWidgets[targetType];
  return ancestor;
}

@override
InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
  final InheritedElement ancestor = _inheritedWidgets == null ? null :   _inheritedWidgets[targetType];
  //多出的部分
  if (ancestor != null) {
    assert(ancestor is InheritedElement);
    return inheritFromElement(ancestor, aspect: aspect);
  }
  _hadUnsatisfiedDependencies = true;
  return null;
}

// 可以看到,inheritFromWidgetOfExactType() 比 ancestorInheritedElementForWidgetOfExactType()多调了inheritFromElement方法,inheritFromElement源码如下:

@override
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
  //注册依赖关系
  _dependencies ??= HashSet<InheritedElement>();
  _dependencies.add(ancestor);
  ancestor.updateDependencies(this, aspect);
  return ancestor.widget;
}

We can see inheritFromElement method is mainly registered dependency! See here also clear the call inheritFromWidgetOfExactType () and ancestorInheritedElementForWidgetOfExactType () difference is that the former will register dependencies, and the latter will not, so when you call inheritFromWidgetOfExactType (), InheritedWidget and its descendants dependent component relationships will complete the registration after InheritedWidget when changes, will update its descendants dependent components, i.e. can adjust these didChangeDependencies () method and a build () method descendants assembly. And when the call is ancestorInheritedElementForWidgetOfExactType (), since there is no register dependencies, so then when InheritedWidget changes, it will not update the descendants Widget.
Note that if the above example ShareDataWidget.of () method implementation calls into ancestorInheritedElementForWidgetOfExactType (), after running the sample, click on the "Increment" button, you will find __TestWidgetState of didChangeDependencies () method does no longer be called, but build () it will still be called! The reason for this is actually, click on "Increment" button, the call setState _InheritedWidgetTestRouteState () method, then will rebuild the entire page, as an example, __ TestWidget does not have any cache, so it will be rebuilt, so it will call the build () method.
One problem: in fact, we just want to update the ShareDataWidget subtree dependent components, and now just call _InheritedWidgetTestRouteState of setState () method, all child nodes will be re-build, it's not necessary, so is there any way to avoid it? The answer is cached! A simple approach is a StatefulWidget through the package, the sub-tree cached Widget

Guess you like

Origin www.cnblogs.com/parzulpan/p/12200203.html