flutter中 InheritedWidget 实现原理

InheritedWidget 是 Flutter 中数据共享和状态传播的底层机制,也是 Provider、Riverpod 等状态管理方案的基石。

一、什么是 InheritedWidget

InheritedWidget 是一种特殊的 Widget,它可以让子树中的 Widget 高效地获取共享数据 ,并在数据变化时自动重建依赖它的 Widget。

复制代码
// 简单示例
class MyInheritedWidget extends InheritedWidget {
  final int data;
  
  const MyInheritedWidget({
    required this.data,
    required Widget child,
  }) : super(child: child);
  
  @override
  bool updateShouldNotify(MyInheritedWidget oldWidget) {
    return data != oldWidget.data;  // 数据变化时通知
  }
  
  static MyInheritedWidget? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
  }
}

二、核心原理

1. 整体架构

2. 核心数据结构

复制代码
// InheritedElement 的核心数据结构
class InheritedElement extends ProxyElement {
  // 依赖映射表:key 是依赖的 Element,value 是依赖的数据
  final Map<Element, Object?> _dependents = HashMap<Element, Object?>();
  
  // 通知所有依赖者数据已变化
  void notifyClients(InheritedWidget oldWidget) {
    for (final dependent in _dependents.keys) {
      notifyDependent(oldWidget, dependent);
    }
  }
  
  // 通知单个依赖者
  void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
    dependent.didChangeDependencies();  // 触发依赖者重建
  }
}

3. 依赖注册过程

复制代码
// Element 中的依赖注册方法
Element {
  Map<Type, InheritedElement>? _dependencies;
  
  void _registerDependency(InheritedElement ancestor) {
    _dependencies ??= HashMap<Type, InheritedElement>();
    _dependencies![ancestor.widget.runtimeType] = ancestor;
    ancestor.updateDependencies(this, null);  // 在 InheritedElement 中注册
  }
  
  // 获取最近的指定类型的 InheritedWidget
  T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>() {
    final ancestor = _getClosestAncestorOfType(T);
    if (ancestor != null) {
      _registerDependency(ancestor);  // 注册依赖
      return ancestor.widget as T;
    }
    return null;
  }
}

三、核心原理

  1. 依赖注册 :子 Widget 调用 dependOnInheritedWidgetOfExactType 时,会在 InheritedElement 中注册

  2. 变化通知 :数据变化时,InheritedElement 遍历 _dependents 调用 didChangeDependencies

  3. 自动重建:依赖的 Element 收到通知后标记为 dirty,下一帧重建

关键点

  • updateShouldNotify 决定是否通知

  • dependOnInheritedWidgetOfExactType 注册依赖

  • 使用 const 和缓存优化性能

  • 适合少量、全局的共享数据

四、InheritedWidget 获取原理

MyInheritedWidget.of(context) 的核心原理是向上遍历 Element 树建立依赖关系

核心调用链

复制代码
MyInheritedWidget.of(context)

// 实际调用
static MyInheritedWidget of(BuildContext context) {
  return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
}

五、dependOnInheritedWidgetOfExactType 源码分析

1. Element 中的实现

复制代码
// Element 类中的核心方法
abstract class Element implements BuildContext {
  
  @override
  T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
    // 步骤1: 获取最近的指定类型的 InheritedElement
    final InheritedElement ancestor = _getInheritedOfExactType<T>();
    
    if (ancestor != null) {
      // 步骤2: 注册依赖关系
      return dependOnInheritedElement(ancestor, aspect) as T;
    }
    
    // 步骤3: 没找到时触发错误
    _hadUnsatisfiedDependencies = true;
    return null;
  }
  
  // 获取最近的 InheritedElement
  InheritedElement? _getInheritedOfExactType<T extends InheritedElement>() {
    // 从当前 Element 开始向上遍历
    Element? ancestor = _parent;
    while (ancestor != null) {
      if (ancestor is InheritedElement && ancestor.widget is T) {
        return ancestor;  // 找到了!
      }
      ancestor = ancestor._parent;
    }
    return null;
  }
  
  // 建立依赖关系
  @override
  T dependOnInheritedElement<T extends InheritedElement>(InheritedElement ancestor, {Object? aspect}) {
    // 注册依赖:将当前 Element 添加到 ancestor 的依赖表中
    ancestor.updateDependencies(this, aspect);
    
    // 返回 InheritedWidget 实例
    return ancestor.widget as T;
  }
}

2. InheritedElement 中的依赖管理

复制代码
class InheritedElement extends ProxyElement {
  // 依赖表:key 是依赖的 Element,value 是依赖的方面
  final Map<Element, Object?> _dependents = HashMap<Element, Object?>();
  
  // 更新依赖关系
  void updateDependencies(Element dependent, Object? aspect) {
    // 将当前 Element 加入依赖表
    _dependents[dependent] = aspect;
  }
  
  // 通知所有依赖者
  void notifyClients(InheritedWidget oldWidget) {
    for (final Element dependent in _dependents.keys) {
      notifyDependent(oldWidget, dependent);
    }
  }
  
  // 通知单个依赖者
  void notifyDependent(InheritedWidget oldWidget, Element dependent) {
    dependent.didChangeDependencies();
  }
}

六、完整流程图

七、源码实现

1. 向上查找的完整过程

复制代码
// Element 的 _parent 是如何建立的
class Element {
  Element? _parent;
  
  void mount(Element? parent, dynamic newSlot) {
    _parent = parent;  // 挂载时设置父节点
    _depth = _parent != null ? _parent!.depth + 1 : 1;
    // ...
  }
}

// 查找过程的详细实现
InheritedElement? _getInheritedOfExactType<T extends InheritedElement>() {
  // 从当前 Element 开始
  Element? ancestor = this;
  
  // 向上遍历直到根节点
  while (ancestor != null) {
    // 检查是否是指定类型
    if (ancestor is InheritedElement && ancestor.widget is T) {
      return ancestor;
    }
    // 继续向上
    ancestor = ancestor._parent;
  }
  
  return null;
}

关键点

  • 匹配的是 ancestor.widget is T(类型检查)

  • 不比较实例是否相同

  • 不比较数据内容

2. 依赖表的内部结构

复制代码
// 模拟依赖表的结构
class InheritedElement {
  // 依赖表示例
  // _dependents = {
  //   element1: null,        // 依赖整个 Widget
  //   element2: 'count',     // 依赖特定方面
  //   element3: null,        // 依赖整个 Widget
  // }
  final Map<Element, Object?> _dependents = {};
  
  void updateDependencies(Element dependent, Object? aspect) {
    // 如果已存在,更新 aspect
    // 如果不存在,添加新条目
    _dependents[dependent] = aspect;
  }
  
  // 选择性通知(根据 aspect)
  void notifyClients(InheritedWidget oldWidget) {
    for (final entry in _dependents.entries) {
      if (shouldNotify(entry.key, entry.value)) {
        entry.key.didChangeDependencies();
      }
    }
  }
}
相关推荐
Lanren的编程日记1 小时前
Flutter 鸿蒙应用智能提示系统实战:上下文感知+新手引导+操作提示,打造极致用户体验
flutter·华为·harmonyos·ux
Hello__77771 小时前
开源鸿蒙 Flutter 实战|文章阅读统计功能全流程实现
flutter·开源·harmonyos
Lanren的编程日记1 小时前
Flutter 鸿蒙应用智能搜索功能实战:模糊搜索+搜索建议+搜索历史,打造极致搜索体验
flutter·华为·harmonyos
maaath2 小时前
【maaath】Flutter for OpenHarmony 文件下载能力集成实战
flutter·华为·harmonyos
Hello__777711 小时前
开源鸿蒙 Flutter 实战|页面加载进度条全流程实现
flutter·开源·harmonyos
hamber12 小时前
用 Flutter 造一台掌机
flutter·ai编程·全栈
程序员老刘·16 小时前
Flutter版本选择指南:3.41.7进入稳态,生产环境升级窗口开启 | 2026年4月
flutter·跨平台开发·客户端开发
IntMainJhy18 小时前
Flutter 三方库 shimmer 的鸿蒙化适配与实战指南
flutter·华为·harmonyos