Flutter原理篇:InheritedWidget原理解析(图文并茂!!!)

做flutter项目永远离不开状态管理,状态管理随处可见,如果用不好就会造成频繁刷新严重影响性能,要学好诸如getx、provider、riverpord等状态管理框架,我们就要先打好flutter自带的InheritedWidget状态管理才能为后面的学习做好铺垫今天我们来窥探状态管理的底层原理,首先我们看看它是怎么用的,然后再做最详细的解析。

第一步创建继承自InheritedWidget的CounterInheritedWidget类

第二步:创建包装CounterInheritedWidget的CounterWidget类

第三步:创建使用CounterInheritedWidget内部变量的CounterText类

///首先创建一个inheritedWidget状态共享类
class CounterInheritedWidget extends InheritedWidget {
  final int counter;
  final Widget child;

  CounterInheritedWidget({
    Key? key,
    required this.counter,
    required this.child,
  }) : super(key: key, child: child);

  @override
  bool updateShouldNotify(CounterInheritedWidget oldWidget) {
    return counter != oldWidget.counter;
  }

  static CounterInheritedWidget? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<CounterInheritedWidget>();
  }
}


//创建一个使用InheritedWidget的类包装的widget
class CounterWidget extends StatefulWidget {
  @override
  _CounterWidgetState createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return CounterInheritedWidget(
      counter: _counter,
      child: Scaffold(
        appBar: AppBar(
          title: Text('InheritedWidget Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                'You have pushed the button this many times:',
              ),
              CounterText(), // 显示计数器值的子组件
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: _incrementCounter,
          tooltip: 'Increment',
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

///使用inheritedWidget中的变量
class CounterText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    ///注意这个方法的使用,非常重要。
    final inheritedWidget = CounterInheritedWidget.of(context);

    return Text(
      '${inheritedWidget?.counter ?? 0}',
      style: Theme.of(context).textTheme.headline4,
    );
  }
}

 1.其中创建了三个widget,一个InheritedWidget类型的widget,一个包装InheritedWidget类型的wiget,一个使用InheritedWidget的widget(这里单独创建一个是为了局部更新)

2.关键点,创建,包装,使用,缺一不可。

3.必须通过CounterInheritedWidget.of(context);这种方式拿到InheritedWidget并使用其中的变量

现在我们开始分析其使用原理

首先分析dependOnInheritedWidgetOfExactType这个方法是做什么的。因为第一步会走这个方法

///CounterText的element
@override
  T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    //这里并不为空,因为顶层的inheritiedwidget挂载的时候已经创建了并将自身保存在了_inheritedElements中
    final InheritedElement? ancestor = _inheritedElements == null ? null : _inheritedElements![T];
    if (ancestor != null) {
      ///由于顶层包装了inheritiedwidget,所以最终会走这里
      return dependOnInheritedElement(ancestor, aspect: aspect) as T;
    }
    _hadUnsatisfiedDependencies = true;
    return null;
  }

  ///这里先取出自身保存的_dependencies节点,顾名思义,依赖,所以最终会将自身依赖的inheritedwidget节点添加进_dependencies集合中
 @override
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
    _dependencies ??= HashSet<InheritedElement>();
    ///这里会将inheritedwidget添加进集合中
    _dependencies!.add(ancestor);
    ///并调用自身所依赖的所有inheritedwidget的updateDependencies方法。就是上面案例中的方法
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget as InheritedWidget;
  }

带着两个疑问向下看

1._inheritedElements为什么不为null?

2._inheritedElements什么时候被初始化?

咱们先总结一下上面的逻辑

1.将当前使用dependOnInheritedWidgetOfExactType这个方法的widget的_inheritedElements拿到,_inheritedElementsmap类型,所以通过_inheritedElements则拿到对应的CounterInheritedWidget,即获取到当前widget即上面案例的CounterText的依赖,也是最近的依赖啦。

2.再去获取CounterText的所有依赖集合_dependencies,即所有的inheritedwidget的集合,然后将CounterInheritedWidget加入到_dependencies集合中,并调用所有的依赖即inheritedwidget的updateDependencies的方法,即案例中的CounterInheritedWidget的方法。

首先我们先解决上面两个疑问

  @mustCallSuper
  void mount(Element? parent, Object? newSlot) {
    if (parent != null) {
      _owner = parent.owner;
    }
    assert(owner != null);
    final Key? key = widget.key;
    if (key is GlobalKey) {
      owner!._registerGlobalKey(key, this);
    }
    ///开始执行
    _updateInheritance();
    attachNotificationTree();
  }


///InheritedElement
  @override
  void _updateInheritance() {
    assert(_lifecycleState == _ElementLifecycle.active);
    final PersistentHashMap<Type, InheritedElement> incomingWidgets =
        _parent?._inheritedElements ?? const PersistentHashMap<Type, InheritedElement>.empty();
    _inheritedElements = incomingWidgets.put(widget.runtimeType, this);
  }


  ///element
 void _updateInheritance() {
    assert(_lifecycleState == _ElementLifecycle.active);
    _inheritedElements = _parent?._inheritedElements;
  }

1.当widget开始挂载的时候,调用mout,同时调用实现了_updateInheritance的widget,即先调用inheritedElement的_updateInheritance并初始化_inheritedElements。

2.直到子widget即上面案例中的CounterText开始挂载的时候拿到父widget的_inheritedElements。

前置逻辑

解决了这两个问题, 我们继续跟着代码往下看inheritedElement中的updateDependencies方法

class InheritedElement extends ProxyElement {

    final Map<Element, Object?> _dependents = HashMap<Element, Object?>();

    @protected
      void updateDependencies(Element dependent, Object? aspect) {
        setDependencies(dependent, null);
      }

    @protected
      void setDependencies(Element dependent, Object? value) {
        ///记录调用InheritedElement的子widget
        _dependents[dependent] = value;
      }
}

1.InheritedElement同时也对子widget即调用了dependOnInheritedWidgetOfExactType的widget做了记录保存

2.这样操作是为了后面方便更新。

重点:inheritedElement也对子widget进行了保存的操作

疑问:InheritedElement怎么做到局部更新的呢?


abstract class ProxyElement extends ComponentElement {
  /// Initializes fields for subclasses.
  ProxyElement(ProxyWidget super.widget);

  @override
  Widget build() => (widget as ProxyWidget).child;

  ///当调用了setstate等标记脏数据,即开始进入build流程
  @override
  void update(ProxyWidget newWidget) {
    ///先记录旧的widget配置,即当前inheritedWidget
    final ProxyWidget oldWidget = widget as ProxyWidget;
    assert(widget != newWidget);
    ///更新widget,当然满足条件的话就会更新
    super.update(newWidget);
    assert(widget == newWidget);
    ///主要看这个方法做了什么
    updated(oldWidget);
    rebuild(force: true);
  }
  @protected
  void updated(covariant ProxyWidget oldWidget) {
    notifyClients(oldWidget);
  }
}

class InheritedElement extends ProxyElement {
 @override
  void notifyClients(InheritedWidget oldWidget) {
    for (final Element dependent in _dependents.keys) {
      ///遍历所有的key,即前面InheritedElement保存的所有依赖其自身的子widget!
      notifyDependent(oldWidget, dependent);
    }
  }

    @protected
  void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
    ///将所有依赖InheritedElement的子widget调用didChangeDependencies的生命周期方法。
    dependent.didChangeDependencies();
  }

    @override
  void updated(InheritedWidget oldWidget) {
    if ((widget as InheritedWidget).updateShouldNotify(oldWidget)) {
      super.updated(oldWidget);
    }
  }
}

1.当调用setstate的时候,首先会调用ProxyElement(即InheritedElement的父类)的update方法,先保存记录当前的widget。

2.调用InheritedElement的updated方法,其中调用了实现Inheritedwidget的CounterInheritedWidgetupdateShouldNotify方法进行对比,如果返回true,即内部使用的变量发生变化,则调用父类的updated方法。

3.在父类的updated方法中再调用InheritedElement的notifyDependent方法,遍历调用所有依赖inheritedwidget的子widget的didChangeDependencies方法。

关键就在didChangeDependencies方法!!!我们重点看看

///element
@mustCallSuper
  void didChangeDependencies() {
    assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op
    assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
    ///标记脏数据,请求更新一帧
    markNeedsBuild();
  }

从setstate开始执行

  • 更新逻辑第一次走进了InheritedElement的父类ProxyElement的update方法
  • 然后进了InheritedElementupdated中执行updateShouldNotifyCounterInheritedWidget实现的)的方法做变量判断是否有变化
  • 如果有变化执行父类ProxyElement的updated方法调用notifyClients通知依赖发生变化
  • 此时所有依赖inheritedelement的即使用了inheritedwidget的变量的widget都开始执行didChangeDependencies方法,此方法内部调用了markNeedsBuild即请求刷新数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超盘守

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值