2. Flutter InheritedWidget 原理

60 阅读3分钟

Flutter InheritedWidget, 就是一个观察者模式,里面两大东西,一个是收集,一个通知, 接下来我们看一个demo例子一步一步分析里面的原理

1. 收集过程

import 'package:flutter/material.dart';

class MyCounterWidget extends InheritedWidget {
  final int counter;

  // 抛出去对象
  static MyCounterWidget of(BuildContext context) {
    // 沿着 elment树 => 找最近的 MyCounterWidget 这个widget
    final MyCounterWidget? result =
        context.dependOnInheritedWidgetOfExactType<MyCounterWidget>();
    assert(result != null, 'No MyCounterWidget found in context');
    return result!;
  }

  const MyCounterWidget({required super.child, super.key, this.counter = 100});

  @override
  // state => did
  bool updateShouldNotify(MyCounterWidget oldWidget) {
    return oldWidget.counter != counter;
  }
}

class ShareInHeritedState extends StatefulWidget {
  const ShareInHeritedState({super.key});

  @override
  State<ShareInHeritedState> createState() => _ShareInHeritedStateState();
}

class _ShareInHeritedStateState extends State<ShareInHeritedState> {
  int _count = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("InheritedWidget test"),
      ),
      body: MyCounterWidget(
        counter: _count,
        child: Column(
          children: [
            HYShowData01(),
            HYShowData02(),
            FloatingActionButton(
                child: Icon(Icons.pets),
                onPressed: () {
                  setState(() {
                    _count += 1;
                  });
                })
          ],
        ),
      ),
    );
  }

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

  @override
  void didUpdateWidget(covariant ShareInHeritedState oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("didUpdateWidget: _ShareInHeritedStateState");
  }
}

class HYShowData01 extends StatelessWidget {
  const HYShowData01({super.key});

  @override
  Widget build(BuildContext context) {
    return Card(
      color: Colors.red,
      child: Text("当前计数1:${MyCounterWidget.of(context).counter}"),
    );
  }
}

class HYShowData02 extends StatefulWidget {
  const HYShowData02({super.key});

  @override
  State<HYShowData02> createState() => _HYShowData02State();
}

class _HYShowData02State extends State<HYShowData02> {
  @override
  void initState() {
    super.initState();

    print("initState: _HYShowData02State");
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      color: Colors.blue,
      child: Text(
          "当前计数2:${MyCounterWidget.of(context).counter}"), // statelessBUild的时候  => 触发收集
    );
  }

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

  @override
  void didUpdateWidget(covariant HYShowData02 oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("didUpdateWidget: _HYShowData02State");
  }
}

哪里收集? 当调用build的时候, 就会调用 MyCounterWidget.of(context).counter => context.dependOnInheritedWidgetOfExactType<MyCounterWidget>(); 这里的context 就是 HYShowData02Element ,相当于从 子的Element中去 拿到 MyCounterWidget 实例? 有点疑惑先放这里;

class _HYShowData02State {
 Widget build(BuildContext context) {
    return Card(
      color: Colors.blue,
      child: Text(
          "当前计数2:${MyCounterWidget.of(context).counter}"), // statelessBUild的时候  => 触发收集
    );
  }
}

进入 context.dependOnInheritedWidgetOfExactType , 在 里面完成了 收集过程 ancestor._dependents[子Element] = null;

// Element 类中
 @override
  T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
    
    //  _inheritedWidgets 是一个Map <InheritedWidget 类型, InheritedElement 实例>
    final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
    if (ancestor != null) {
      // 通过拿到 InheritedElement实例 去 获取 element.widget, 并返回回去
      return dependOnInheritedElement(ancestor, aspect: aspect) as T;
    }
    _hadUnsatisfiedDependencies = true;
    return null;
  }
  
  
   @override
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
    assert(ancestor != null);
    
    // 子Element 通过 _dependencies持有  ancestor: InheritedElement
    // 其实 子Element 可以通过 _inheritedWidgets 根据 InheritedElement 类型可以拿到,为什么需要有个数组去添加,确实有点奇怪,可能是有其他的作用????
    _dependencies ??= HashSet<InheritedElement>();
    _dependencies!.add(ancestor);
    
    // ancestor: InheritedElement 持有  子Element 
    // ancestor._dependents[this] = aspect;
    // 这里就完成了 收集过程
    ancestor.updateDependencies(this, aspect);
    
    return ancestor.widget as InheritedWidget;
  }
  

这里有点迷惑的是 为什么 _inheritedWidgets 这里包含了 我们所需要的 InheritedElement 其实这个在 Element mount 的时候 从 父node 传递过来的 有个 _updateInheritance(); 就是把父node的传递给子node _inheritedWidgets = _parent?._inheritedWidgets;

// Element
void mount(Element? parent, Object? newSlot) {
 
    _parent = parent;
    _slot = newSlot;
    _lifecycleState = _ElementLifecycle.active;
    _depth = _parent != null ? _parent!.depth + 1 : 1;
    if (parent != null) {
      // Only assign ownership if the parent is non-null. If parent is null
      // (the root node), the owner should have already been assigned.
      // See RootRenderObjectElement.assignOwner().
      _owner = parent.owner;
    }
    assert(owner != null);
    final Key? key = widget.key;
    if (key is GlobalKey) {
      owner!._registerGlobalKey(key, this);
    }
    // 
    _updateInheritance();
    attachNotificationTree();
  }

2. 通知过程

从前面的分析,知道了收集过程 ancestor._dependents[子Element] = null;

接下来我们看看他们是怎么通知的,这里其实就很简单直接遍历 ancestor._dependents

在 MyCounterWidget updateShouldNotify 里面 打个断点,并点击按钮 触发;

bool updateShouldNotify(MyCounterWidget oldWidget) {
    return oldWidget.counter != counter;
}

image.png

InheritedElement extend ProxyElement

  • 下面InhertedElement的 updated方法会 被 @override;
// 这里的child 其实就是对应的 MyCounterWidget的 Element
child.update(newWidget);

// ProxyElement
@override
void update(ProxyWidget newWidget) {
    final ProxyWidget oldWidget = widget as ProxyWidget;

    super.update(newWidget);
    
    // 这里会先进入 InheritedElement的 updated,如果需要更新才会去更新
    updated(oldWidget);
    
    rebuild(force: true);
 }

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

// 进入 ProxyElement 的更新,这里我们就看到  notifyClients(oldWidget)
 @protected
  void updated(covariant ProxyWidget oldWidget) {
    notifyClients(oldWidget);
  }
 
 // InheritedElement
 @override
  void notifyClients(InheritedWidget oldWidget) {
    
    // 拿到  _dependents.keys 进行遍历
    for (final Element dependent in _dependents.keys) {
      // check that it really depends on us
      notifyDependent(oldWidget, dependent);
    }
  }
}

// InheritedElement 
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
   dependent.didChangeDependencies();
}
 
 // Element 
@mustCallSuper
void didChangeDependencies() {
 // 标记更新状态
  markNeedsBuild();
}

以上的分析,我们就能看到 怎么样通知的过程

接下来我们继续看看 为什么需要 rebuild(force: true);

@pragma('vm:prefer-inline')
void rebuild({bool force = false}) {
   performRebuild();
}

// ComponentElement
void performRebuild() {
    Widget? built;
    built = build();
    super.performRebuild(); // clears the "dirty" flag
    // 这里就是更新孩子Element
    // 这里如果没有child 会进行创建 Element newChild = newWidget.createElement();
    _child = updateChild(_child, built, slot);
}