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。任何技术都有它使用场景的限制,有它能支持的场景,也有它所不及的场景。我们需要多试多练,多单步调试,多总结,多看它的源码和文档,定能解决现实中的技术问题。

致谢

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

相关推荐
FungLeo3 分钟前
成为全栈·Flutter App 篇·Flutter Markdown 阅读器:支持范围与渲染边界
flutter·markdown·dart·代码高亮·成为全栈·移动阅读
m0_7381858213 分钟前
Flutter 鸿蒙化实战:webview_cookie_manager 适配 OpenHarmony,WebView Cookie 管理
flutter·华为·harmonyos·鸿蒙
FungLeo1 小时前
成为全栈·Flutter App 篇·服务端目录与文章辅助阅读:锚点、上下篇、浏览量和阅读历史
flutter·锚点·阅读体验·成为全栈·markdown目录·api契约
恋猫de小郭2 小时前
Flutter Android 改动:APK 不再允许通过 Intent 动态修改 Engine 参数
android·前端·flutter
小 黄 鸡12 小时前
Flutter 面试必会知识点详解(从基础到进阶)
flutter·面试·职场和发展
大龄秃头程序员19 小时前
Flutter 最少代码验证:为什么子 Isolate 调原生,一定要先回到主 Isolate?
flutter
FungLeo20 小时前
成为全栈·Flutter App 篇·分类、标签与搜索:移动端内容发现
flutter·移动开发·标签·搜索·分类管理·成为全栈
m0_7381858221 小时前
Flutter 鸿蒙化实战:the_apple_sign_in 适配 OpenHarmony,Apple 登录
flutter·华为·harmonyos·鸿蒙
m0_738185821 天前
Flutter 鸿蒙化实战:umeng_analytics_plugin 适配 OpenHarmony,友盟统计
flutter·华为·harmonyos·鸿蒙
FungLeo1 天前
成为全栈·Flutter App 篇·列表分页与下拉刷新:去重、失败保留和返回位置
flutter·移动开发·下拉刷新·分页加载·列表优化·成为全栈