InheritWidget原理解析

先上代码

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter InheritWidget',
      home: Scaffold(
        appBar: AppBar(),
        body: Center(
          child: TestWidget(),
        ),
      ),
    );
  }
}

class TestWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return TestWidgetState();
  }
}

class TestWidgetState extends State<TestWidget> {
  String text = "init";

  @override
  Widget build(BuildContext context) {
    return CustomInheritedWidget(
      text: text,//1
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          InheritedWidgetChild(),
          RaisedButton(
            onPressed: () {
              setState(() {
                text = "modify";//2
              });
            },
            child: Text("修改值"),
          )
        ],
      ),
    );
  }
}

class InheritedWidgetChildState extends State<InheritedWidgetChild> {
  @override
  Widget build(BuildContext context) {
    print("build");
    final CustomInheritedWidget widget = context.dependOnInheritedWidgetOfExactType<CustomInheritedWidget>();
    return Text(widget.text);
  }

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

class CustomInheritedWidget extends InheritedWidget {
  CustomInheritedWidget({Key key, this.text, Widget child}) : super(key: key, child: child);

  final String text;

  @override
  bool updateShouldNotify(CustomInheritedWidget oldWidget) {
    return oldWidget.text != text;
  }
}
复制代码

在注释1,text默认值为 init,当点击按钮的时候,修改text值为 modify并刷新页面,咱们会发现 InheritedWidgetChild调用了 didChangeDependencies()方法,咱们来分析一下,点击按钮的时候都发生了什么。

_inheritedWidgets的传递

首先说一下流程,在Elementactivatemount的时候,会调用_updateInheritance方法,把本身的_inheritedWidgets指向_parent_inheritedWidgets,而InheritedElement覆盖了该方法,代码分别以下app

//Element
  void _updateInheritance() {
    assert(_active);
    _inheritedWidgets = _parent?._inheritedWidgets;
  }
复制代码
//InheritedElement
 @override
 void _updateInheritance() {
   assert(_active);
   final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets;
   if (incomingWidgets != null)
     _inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
   else
     _inheritedWidgets = HashMap<Type, InheritedElement>();
   _inheritedWidgets[widget.runtimeType] = this;
 }
复制代码

能够看到InheritedElement拷贝了_parent_inheritedWidgets并把本身的 widgetruntimeType做为keythis做为value保存在了本身的inheritedWidgets属性里,因此最开始的代码的UI树是这样的less

TestWidgetide

-CustomInheritedWidgetui

--Columnthis

---InheritedWidgetChildspa

---RaisedButtondebug

CustomInheritedWidget拷贝了TestWidget_inheritedWidgets到本身的_inheritedWidgets并将本身保存其中,而后Column_inheritedWidgets直接指向了CustomInheritedWidget_inheritedWidgets,而后InheritedWidgetChildRaisedButton_inheritedWidgets直接指向了Column_inheritedWidgetscode

InheritedElement的查找

InheritedWidgetChild里调用context.dependOnInheritedWidgetOfExactType<CustomInheritedWidget>()方法能够查找到CustomInheritedWidget,代码以下:cdn

//Element
 @override
 T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
   assert(_debugCheckStateIsActiveForAncestorLookup());
   final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
   if (ancestor != null) {
     assert(ancestor is InheritedElement);
     return dependOnInheritedElement(ancestor, aspect: aspect) as T;
   }
   _hadUnsatisfiedDependencies = true;
   return null;
 }

 @override
 InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
   assert(ancestor != null);
   _dependencies ??= HashSet<InheritedElement>();
   _dependencies.add(ancestor);
   ancestor.updateDependencies(this, aspect);
   return ancestor.widget;
 }
复制代码
//InheritedElement
@protected
void updateDependencies(Element dependent, Object aspect) {
  setDependencies(dependent, null);
}
@protected
void setDependencies(Element dependent, Object value) {
  _dependents[dependent] = value;
}
复制代码

咱们能够看到,在dependOnInheritedWidgetOfExactType这个方法里,查找到泛型Widget对应的InheritedElement,保存到ancestor变量里,而后在本身(InheritedWidgetChild)的_dependencies集合中添加ancestor,并把本身保存到找到ancestor_dependents集合里,这样在InheritedElement更新内部数据的时候,就能够通知到全部依赖了本InheritedElementWidget。同时InheritedWidgetChild_dependencies集合中添加了ancestor,因此在InheritedWidgetChild销毁的时候,能够把本身(InheritedWidgetChild)从ancestor_dependents集合中移除,避免没必要要的更新和内存泄漏。blog

总结

总结一下上面的内容,在Elementactivatemount的时候,会调用_updateInheritance方法,把本身的_inheritedWidgets指向_parent_inheritedWidgets,而InheritedElement覆盖了该方法,将本身保存到了_inheritedWidgets中,因此在层层向下传递的时候,_inheritedWidgets就包含了传递过程当中全部的InheritedElement,而后在经过dependOnInheritedWidgetOfExactType方法获取InheritedElement的时候,方法调用方把本身保存到了InheritedElement_dependencies集合中,而且方法调用方在本身的_dependencies集合中也添加了本身所依赖的InheritedElement的引用,在方法调用方销毁的时候,把本身从本身所依赖的InheritedElement_dependencies集合中删除本身除,避免没必要要的更新和内存泄漏。

思考题

  1. Elementactivatemount方法在何时被调用?
  2. CustomInheritedWidget改变内部的数据的时候,为何InheritedWidgetChild会调用didChangeDependencies()
相关文章
相关标签/搜索