InheritedWidget(数据共享)

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>();
  }
}
  1. 首先,创建一个继承InheritedWidget的子类(如MyInheritedWidget),类里面的数据data限定为final,表示不可变每次更新数据每次都需要重新创建,构造函数中需要传递子Widget。
  2. 再次,重写updateShouldNotify方法。每当InheritedWidget中的数据发生变化时,都会调用此方法。如果此方法返回 true,则意味着将重建依赖于此InheritedWidget的子Widget。
  3. 最后,提供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,
          ),
        ],
      ),
    );
  }
}
  1. 注意增加debugger(when: true)逻辑代码,可以进行单步调试,能够快速了解相应的逻辑。
  2. 当在子Widget(MyHomePage)的build方法中调用MyInheritedWidget的of的静态方法的时候,就相当于注册了一个MyInheritedWidget的监听器。当MyInheritedWidget数据变化的时候,此Widget(MyHomePage)会重新构建。 MyInheritedWidget.of()方法如何单步调试获取到MyInheritedWidget实例,如下图所示:

InheritedWidget适合使用场景

  1. 状态管理。管理全局或者分享的数据,比如用户身份认证状态,设置数据等等。
  2. 依赖注入:向Widget子树提供依赖项(例如服务或存储库)。
  3. 配置数据:将配置数据(例如本地化或应用程序范围的设置)传播到所有相关Widget。
  4. 主题或样式信息:自定义主题解决方案,其中应用程序的不同部分需要对主题更改做出反应。

InheritedWidget误用和滥用使用场景

  1. 过度重建:过度使用InheritedWidget或未正确实现updateShouldNotify会导致过度的Widget重建,从而导致性能问题。
  2. 过度复杂化简单状态:当更简单的解决方案(如StatefulWidget或ValueNotifier)就足够时,使用 InheritedWidget进行简单状态管理。
  3. 深度Widget树查找:严重依赖Widget树中的深度查找,这会使代码更难理解和维护。
  4. 全局状态管理:使用InheritedWidget进行复杂的全局状态管理,而不是使用更合适的解决方案,如 Provider、Riverpod(Google推荐使用) 或Bloc。
  5. context.dependOnInheritedWidgetOfExactType的不当使用:误用上下文查找方法,导致Widget树中出现意外的依赖关系和副作用。

总结

InheritedWidget是一个设计优良的功能型组件,作用是从父Widget高效地传递数据到子Widget。任何技术都有它使用场景的限制,有它能支持的场景,也有它所不及的场景。我们需要多试多练,多单步调试,多总结,多看它的源码和文档,定能解决现实中的技术问题。

致谢

希望文章对大家有所帮助,如果文章有所纰漏请不吝指教,大家共同进步。欢迎关注"技术蔡"的公众号,此公众号也是本作者的技术相关的公众号。

相关推荐
恋猫de小郭9 分钟前
Kotlin Toolchain 0.13,JetBrains 推荐 KMP 都切换到到全新工具链
android·前端·flutter
m0_7381858214 分钟前
Flutter 鸿蒙化实战:rive 适配 OpenHarmony,Rive 动画
flutter·华为·harmonyos·鸿蒙
Geek_Vison12 小时前
保险APP已经用了Flutter,还能接入小程序吗?
flutter·小程序·uni-app·mpaas·小程序容器
FungLeo12 小时前
成为全栈·Flutter App 篇·go_router 与四 Tab App Shell:保留导航状态、深链和登录回跳
flutter·移动开发·成为全栈·go_router·路由设计·深链接
m0_7381858213 小时前
Flutter 鸿蒙化实战:screen_protector 适配 OpenHarmony,防截屏/防泄露
flutter·华为·harmonyos·鸿蒙
梦想的颜色18 小时前
【编程实战】AI 时代 APP 开发全栈硬核指南:技术选型 + AI 架构 + 模型落地全维度决策
python·flutter·react native·react.js·ai·桌面应用·milvus
yuanlaile2 天前
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
陆断枫3 天前
Flutter 3.47 动画引擎盘点:Impeller 全平台,和那些"新特性"
flutter