啊哈,原来 InheritedWidget 是这个意思
在开发 Flutter
应用过程中,状态管理是一个至关重要且经常讨论的话题。随着应用规模的扩大和复杂度的增加,有效地管理应用的状态也逐渐变得尤为关键。而 InheritedWidget
作为 Flutter
中最基础、最底层的状态管理工具之一,承担着传递数据、共享状态的重要责任,本篇将深入探讨 InheritedWidget
的原理、使用方法、在实际开发中的应用场景及它的优缺点。希望能帮助你更好的理解及使用InheritedWidget
。
InheritedWidget
在日常开发中使用的还是挺多的,或者说没有直接使用它,但MediaQuery.of(context)
、Theme.of(context)
这些经常用吧,还有 Provider
、Bloc
这些状态管理的插件总用过吧,这些可以做到共享数据或者更新数据的原因,就是因为它们背后是 InheritedWidget
来实现的。
简单的来讲,InheritedWidget
其实是就是一种跨 widget
共享数据的机制,比如在名A
的 widget
中调用了 .of(context)
来获取数据,这个时候会将 A
注册为 InheritedWidget
的监听器👂,所以每当 InheritedWidget
中的值发生变化时,这个 A
就会被重建。
工作原理
其工作原理就是当一个 Widget
需要访问共享的数据时,它会通过 context
向上查找最近的父的 InheritedWidget
。如果找到了父的 InheritedWidget
,就会将从中获取共享数据。当共享数据发生变化时,InheritedWidget
会通知其所有子 Widget
,并触发它们重建。子 Widget
在重建时会获取最新的共享数据,并根据新的数据状态更新自己的内容。下面一段代码看看 InheritedWidget
的用法。
class CustomColorWidget extends InheritedWidget {
const CustomColorWidget({
super.key,
required super.child,
required this.color,
required this.onColorChange,
});
final Color color;
final void Function() onColorChange;
static CustomColorWidget? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<CustomColorWidget>();
}
@override
bool updateShouldNotify(CustomColorWidget oldWidget) {
return oldWidget.color != color;
}
}
of
方法
of
方法是static
,意味着不需要类的示例,可以直接使用类来调用。参数BuildContext
则表示widget
在widget tree
中的位置。of
方法内部调用了context.dependOnInheritedWidgetOfExactType<T>()
方法。 此方法由Flutter
框架提供,并从给定的BuildContext
开始搜索CustomColorWidget
类型的最近父widget
,。dependOnInheritedWidgetOfExactType<T>()
函数返回的是找到的CustomColorWidget
,所以访问其属性或方法,如果没有找到匹配的小部件,则返回null
。
通过使用 of
方法,可以很方便地从 widget tree
中的任何点检索特定类型的最近父 widget
,而不需要手动遍历树。
updateShouldNotify
方法
这是 InheritedWidget
文档的一段话,翻译过来:框架是否应该通知继承此小部件的小部件。当重建此小部件时,有时我们需要重建从此小部件继承的小部件,但有时则不需要。 例如,如果这个小部件保存的数据与 oldWidget
保存的数据相同,那么我们不需要重建继承 oldWidget
保存数据的小部件。框架通过使用先前占据树中此位置的小部件作为参数来调用此函数来区分这些情况。保证给定的小部件具有与此对象的 runtimeType
相同。
结合上面的代码例子来说,updateShouldNotify
方法是重写父类的方法,用于是否需要向它的子类 widget
通知更新,其由 Flutter
框架自动调用,当前的实例的属性与先前(旧)实例的属性进行比较。如果比较结果为 true
,则意味着 color
属性已更改,并且将更改通知 InheritedWidget
的子类 widget
。
如何使用
这里实现一个当点击按钮改变InheritedWidget
下子 widget
(也就是MyCardWidget
)的背景颜色的 demo
。
class InheritedWidgetTestPage extends StatefulWidget {
const InheritedWidgetTestPage({Key? key}) : super(key: key);
@override
State<InheritedWidgetTestPage> createState() =>
_InheritedWidgetTestPageState();
}
class _InheritedWidgetTestPageState extends State<InheritedWidgetTestPage> {
Color color = Colors.blue;
@override
Widget build(BuildContext context) {
return CustomColorWidget(
color: color,
onColorChange: onColorChange,
child: Scaffold(
backgroundColor: Colors.white,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MyCardWidget(key: UniqueKey()),
FilledButton(
onPressed: () => onColorChange(),
child: const Text("Change Color"),
),
],
),
),
),
);
}
void onColorChange() {
setState(() {
color = Color.fromRGBO(Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1);
});
}
}
class MyCardWidget extends StatelessWidget {
const MyCardWidget({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: CustomColorWidget.of(context)?.color ?? Colors.white,
height: 200,
width: 200,
);
}
}
这里可以看到,CustomColorWidget
被作为父部件包裹着 Scaffold
,这样它的子 widget
也就是 MyCardWidget
就能够通过 CustomColorWidget.of(context)?.color
来访问 color
属性。当点击按钮时调用 setState
触发重新构建。由于 CustomColorWidget
是一个 InheritedWidget
,它的父 widget
会重建,将新的 color
值传递给 MyCardWidget
进行重新渲染。
来看看运行效果:
优点
- 更加高效的数据共享:
InheritedWidget
提供了一种在widget tree
中的多个widget
之间共享数据的有效方法,而不需要显式的数据传递。 它避免了手动通过多个级别向小部件传递参数。 - 自动更新
InheritedWidget
内部的widget
:当InheritedWidget
内的数据发生更改时,widget tree
中的所有依赖这个InheritedWidget
的都会自动更新。 也避免了跨widget
手动跟踪和更新数据。 - 全局可访问性:一旦将
InheritedWidget
放置在widget tree
的根部,就可以从tree
中的任何子的widget
访问它的数据。 这提供了对共享数据的全局访问,不再需要显式传递数据。
缺点
- 共享数据的范围有限:
InheritedWidget
共享的数据仅限于其下方的widget
子树。 如果需要跨多个独立的widget
树或跨不同的页面共享数据,仅InheritedWidget
可能还不够,需要使用其他状态管理解决方案。 - 有一定潜在的性能影响:将
InheritedWidget
用于大型且频繁更改的数据可能会影响性能。 当数据发生变化时,widget
树中依赖于InheritedWidget
的每个widget
都会被重建,这可能会导致不必要的重建并影响应用程序性能。 因此,我们需要更加优化的状态管理方法。 - 容易造成数据耦合:由于
InheritedWidget
共享的数据可由任何后代widget
访问,因此不相关的widget
之间可能存在耦合,意思就是一个子widget
需要color
,而另外一个需要text
,但是color
和text
都存放在这两个widget
父的InheritedWidget
里面。
小结
InheritedWidget
是了解其它状态管理框架的基础,但它并不是解决所有状态管理问题的唯一方法。在实际开发中,根据具体情况选择合适的状态管理工具和模式是至关重要的。除了 InheritedWidget
之外,还有许多其他状态管理解决方案,如 Provider
、Bloc
等,每种解决方案都有其适用的场景和优势。好了今天就分享到这里,感谢您的阅读!
转载自:https://juejin.cn/post/7363175643230502938