InheritedWidget简介
Flutter中的InheritedWidget是一个抽象类,是一个非常重要的一个功能型组件,它允许开发者高效地沿 Widget树向下传播数据,而不需要在父Widget把数据通过子Widget的构造方法逐个传递给子Widget。当然我们还可以创建一个父类,同时把数据保存到父类中,让所有的需要获取此数据的Widget都继承上面定义的父类;但是我们必须编写大量样板代码,我们还为每个Widget创建了依赖关系(因为Dart是单继承,就不能继承其他的类了),这不是一个好的做法。比如我们在应用的根Widget中通过InheritedWidget共享了一个数据,那么我们便可以在任意子Widget中来获取该共享的数据!这个特性在一些需要在整个 widget 树中共享数据的场景中非常方便!如Flutter SDK中正是通过InheritedWidget来共享应用主题(Theme)和 Locale (当前语言环境)信息的。
InheritedWidget和React中的context功能类似,逐级传递数据的同时,它们也能实现组件跨级传递数据。InheritedWidget在Flutter的作用在Android的UI开发中没有与之对应的类。 任何子Widget都可以获取到父InheritedWidget的数据。如下图所示:
InheritedWidget定义
实现InheritedWidget
scala
class MyInheritedWidget extends InheritedWidget {
final int data;
const MyInheritedWidget({
super.key,
required this.data,
required super.child,
});
@override
bool updateShouldNotify(covariant MyInheritedWidget oldWidget) {
return oldWidget.data != data;
}
static MyInheritedWidget? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
}
}
- 首先,创建一个继承InheritedWidget的子类(如MyInheritedWidget),类里面的数据data限定为final,表示不可变每次更新数据每次都需要重新创建,构造函数中需要传递子Widget。
- 再次,重写updateShouldNotify方法。每当InheritedWidget中的数据发生变化时,都会调用此方法。如果此方法返回 true,则意味着将重建依赖于此InheritedWidget的子Widget。
- 最后,提供of的静态方法,返回MyInheritedWidget的对象。无论什么时候你想从MyInheritedWidget 获取数据,你都可以调用这个方法来获取这个MyInheritedWidget里面的数据。 BLoC 和Provider包都在幕后使用了InheritedWidgets。
InheritedWidget的使用
scala
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
//可以debug
debugger(when: true);
final inheritedWidget = MyInheritedWidget.of(context);
final counter = inheritedWidget?.data ?? 0;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text(
'$counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
);
}
}
- 注意增加debugger(when: true)逻辑代码,可以进行单步调试,能够快速了解相应的逻辑。
- 当在子Widget(MyHomePage)的build方法中调用MyInheritedWidget的of的静态方法的时候,就相当于注册了一个MyInheritedWidget的监听器。当MyInheritedWidget数据变化的时候,此Widget(MyHomePage)会重新构建。 MyInheritedWidget.of()方法如何单步调试获取到MyInheritedWidget实例,如下图所示:
InheritedWidget适合使用场景
- 状态管理。管理全局或者分享的数据,比如用户身份认证状态,设置数据等等。
- 依赖注入:向Widget子树提供依赖项(例如服务或存储库)。
- 配置数据:将配置数据(例如本地化或应用程序范围的设置)传播到所有相关Widget。
- 主题或样式信息:自定义主题解决方案,其中应用程序的不同部分需要对主题更改做出反应。
InheritedWidget误用和滥用使用场景
- 过度重建:过度使用InheritedWidget或未正确实现updateShouldNotify会导致过度的Widget重建,从而导致性能问题。
- 过度复杂化简单状态:当更简单的解决方案(如StatefulWidget或ValueNotifier)就足够时,使用 InheritedWidget进行简单状态管理。
- 深度Widget树查找:严重依赖Widget树中的深度查找,这会使代码更难理解和维护。
- 全局状态管理:使用InheritedWidget进行复杂的全局状态管理,而不是使用更合适的解决方案,如 Provider、Riverpod(Google推荐使用) 或Bloc。
- context.dependOnInheritedWidgetOfExactType的不当使用:误用上下文查找方法,导致Widget树中出现意外的依赖关系和副作用。
总结
InheritedWidget是一个设计优良的功能型组件,作用是从父Widget高效地传递数据到子Widget。任何技术都有它使用场景的限制,有它能支持的场景,也有它所不及的场景。我们需要多试多练,多单步调试,多总结,多看它的源码和文档,定能解决现实中的技术问题。
致谢
希望文章对大家有所帮助,如果文章有所纰漏请不吝指教,大家共同进步。欢迎关注"技术蔡"的公众号,此公众号也是本作者的技术相关的公众号。