一、前言
在 Flutter 开发中,如果我们想要跨层级共享数据,最原始的方式是「逐层传参」,层级深了代码会极其臃肿、维护困难。
而InheritedWidget是Flutter 框架原生、最底层、无第三方依赖的跨层级状态共享方案。
我们日常使用的:
-
Theme.of(context) -
MediaQuery.of(context) -
Localizations.of(context)
全部都是基于 InheritedWidget 实现的。
它是Provider/Bloc/GetX 等所有状态管理库的底层基石。
二、InheritedWidget 核心定位
1. 是什么?
InheritedWidget 是一种特殊的不可变 Widget ,可以在 Widget 树中从上到下跨层级共享数据 ,并且支持精准局部刷新。
2. 解决了什么问题?
-
避免层层传参(props 穿透)
-
任意子节点可直接获取上层共享数据
-
精准通知依赖组件刷新,不整树重建
3. 核心特性
-
自上而下数据分发:数据从祖先节点共享,子节点读取
-
依赖收集机制:只刷新真正依赖数据的组件
-
不可变:自身是 immutable,数据更新需要重建自身
-
精准刷新:通过
updateShouldNotify控制是否通知子组件
三、InheritedWidget的定义
我们先看一下InheritedWidget类的继承关系:
DiagnosticableTree → Widget → ProxyWidget → InheritedWidget
完整层级拆解:
-
DiagnosticableTree:调试树基类,提供日志、调试信息能力
-
Widget(根抽象类):所有UI组件的父类,不可变配置对象
-
ProxyWidget:中间代理组件,用于包裹单个子组件,无渲染能力
-
InheritedWidget:跨层级数据共享专属抽象组件
下面我们看一下各个类的内部做了什么
1.DiagnosticableTree
这个类主要调试树基类,提供日志、调试信息能力。
2.Widget
Widget是StatefulWidget、StatelessWidget的父类,提供了key属性,用来初始话子类,以及createElement方法创建Elment,除此之外提供了一些调试方法。有一个child属性。这里它自己不它不生产 UI,原样把 child 交出去。
3.ProxyWidget
ProxyWidget是InheritedWidget的父类。
4.InheritedWidget
我们看一下的定义:
abstract class InheritedWidget extends ProxyWidget {
const InheritedWidget({super.key, required super.child});
@override
InheritedElement createElement() => InheritedElement(this);
@protected
bool updateShouldNotify(covariant InheritedWidget oldWidget);
}
三、最简可运行 Demo(标准模板)
我们实现一个「全局计数器共享」,任意子组件读取、更新、局部刷新。
我们看一下这个Widget的实现步骤。
1.定义InheritedWidget
Dart
class CounterInherited extends InheritedWidget {
const CounterInherited({
super.key,
required this.count,
required this.increment,
required super.child,
});
final int count;
final VoidCallback increment;
// 核心:数据没变就不通知依赖者重建
@override
bool updateShouldNotify(CounterInherited old) {
return old.count != count;
}
}
2.定义便捷静态方法
Dart
class CounterInherited extends InheritedWidget {
// ... 上面那些代码不变 ...
// 静态方法:让后代用 CounterInherited.of(context) 一行读取
static CounterInherited of(BuildContext context) {
final inherited = context
.dependOnInheritedWidgetOfExactType<CounterInherited>();
assert(inherited != null, '未找到 CounterInherited,请检查 Widget 树');
return inherited!;
}
}
3.在顶层注入(提供数据)
Dart
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int _count = 0;
void _increment() => setState(() => _count++);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: CounterInherited(
count: _count,
increment: _increment,
child: const Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ShowCount(), // 读数据
SizedBox(height: 20),
IncrementButton(), // 调方法
],
),
),
),
);
}
}
3.后代读取数据(消费方)
Dart
// 只读数据
class ShowCount extends StatelessWidget {
const ShowCount({super.key});
@override
Widget build(BuildContext context) {
final count = CounterInherited.of(context).count;
return Text('计数: $count', style: const TextStyle(fontSize: 32));
}
}
// 调方法
class IncrementButton extends StatelessWidget {
const IncrementButton({super.key});
@override
Widget build(BuildContext context) {
final increment = CounterInherited.of(context).increment;
return ElevatedButton(
onPressed: increment,
child: const Text('+1'),
);
}
}
效果:点按钮 → _count++ → CounterInherited 数据变了 → updateShouldNotify 返回 true → ShowCount 自动重建 → 数字更新。IncrementButton 没用到 count,但它也依赖了同一个 InheritedWidget------等等,这里有个坑,下面讲。
四、两大核心机制
1. 依赖收集机制
当我们调用 XXX.of(context):
-
Flutter 会向上遍历 Element 树找到对应 InheritedElement
-
将当前组件 Element 注册为依赖者
-
后续数据更新,只刷新注册过的组件,实现精准局部刷新
2. 刷新拦截机制
updateShouldNotify函数返回的是数据发生变化的时候,通知是否重建。
· 数据更新、InheritedWidget 重建后:
-
返回 true:通知所有依赖组件刷新(执行 didChangeDependencies + build)
-
返回 false:不通知,子组件不刷新(性能优化)
最佳实践:只对比核心业务数据,避免无效刷新。
五、of 与 get 的区别(高频面试坑)
很多人不知道这两种写法的巨大差异:
1. of 写法(注册依赖)
Dart
static CounterInherited of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<CounterInherited>()!;
}
会注册依赖 → 数据变化自动刷新(适合UI展示)
2. get 写法(不注册依赖)
Dart
static CounterInherited? get(BuildContext context) {
return context.getElementForInheritedWidgetOfExactType<CounterInherited>()?.widget as CounterInherited;
}
只取值、不注册依赖 → 数据变化不会刷新UI(适合点击事件、方法调用)
六、InheritedWidget触发的生命周期
依赖 InheritedWidget 的 Stateful 组件:
数据更新 → 触发 didChangeDependencies() → 执行 build()
❗ 不会触发 didUpdateWidget(这是最大误区)
didUpdateWidget 是「父组件传参变化」触发; didChangeDependencies 是「Inherited 依赖变化」触发。
七、优缺点总结
✅ 优点
-
原生支持、零依赖、轻量
-
解决跨层级传参问题
-
精准局部刷新,性能优异
-
所有全局状态库底层原理
❌ 缺点
-
自身不可变,必须外层嵌套 Stateful 管理数据
-
无法单独做细粒度状态监听
-
原生不支持异步、缓存、中间件(需要二次封装)
八、适用场景
-
全局主题、配色切换
-
全局语言、本地化配置
-
用户登录态、全局简单配置
-
中低频更新的全局共享数据
九、总结
InheritedWidget 是 Flutter 原生跨层级数据共享组件,通过依赖收集实现精准局部刷新,通过 updateShouldNotify 控制刷新时机,是 Theme、MediaQuery 及所有状态管理库的底层原理;通过 of 方法注册依赖自动刷新UI,get 方法仅取值不刷新。