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();
      }
    }
  }
}
相关推荐
Geek_Vison2 小时前
保险APP已经用了Flutter,还能接入小程序吗?
flutter·小程序·uni-app·mpaas·小程序容器
FungLeo2 小时前
成为全栈·Flutter App 篇·go_router 与四 Tab App Shell:保留导航状态、深链和登录回跳
flutter·移动开发·成为全栈·go_router·路由设计·深链接
m0_738185822 小时前
Flutter 鸿蒙化实战:screen_protector 适配 OpenHarmony,防截屏/防泄露
flutter·华为·harmonyos·鸿蒙
梦想的颜色8 小时前
【编程实战】AI 时代 APP 开发全栈硬核指南:技术选型 + AI 架构 + 模型落地全维度决策
python·flutter·react native·react.js·ai·桌面应用·milvus
yuanlaile1 天前
2026年全新Flutter 跨平台开发全栈学习资料整理
flutter·getx·鸿蒙 next·flutter开发鸿蒙·flutter入门实战·flutter全栈
恋猫de小郭2 天前
Genkit Dart 1.0 发布,Flutter 原生的 AI Agent 终于完整了
android·前端·flutter
第10086个小白2 天前
flutter桌面端打包方式(Inno Setup Compiler)版本
前端·flutter
陆断枫2 天前
Flutter 3.47 动画引擎盘点:Impeller 全平台,和那些"新特性"
flutter
程序员老刘2 天前
Flet 1.0 思路很好,可惜来晚了
flutter·ai编程·客户端
m0_738185823 天前
Flutter 鸿蒙化实战:qr_code_scanner_plus 适配 OpenHarmony,二维码扫描
flutter·华为·harmonyos·鸿蒙