Flutter InheritedWidget及扩展类InheritedNotifier、InheritedModel应用场景

在 Flutter 中,InheritedWidget 是实现 Widget 树中跨层级数据共享的核心机制,它解决了传统通过构造函数传递数据("prop drilling")的繁琐问题,尤其适合深层级 Widget 间的数据传递。其扩展类(InheritedNotifierInheritedModel 等)则在基础功能上增加了更精细的状态管理能力。

一、核心概念与关系

  • InheritedWidget:基类,用于在 Widget 树中高效共享数据。当数据变化时,依赖它的子 Widget 会被选择性重建。
  • Listenable:抽象类(类似观察者模式中的 "被观察者"),定义了添加 / 移除监听器的接口,ChangeNotifier 是其常用实现。
  • InheritedNotifier<T extends Listenable>InheritedWidget 的子类,内部持有一个 Listenable 对象。当 Listenable 触发通知时,会自动调用 updateShouldNotify 触发依赖 Widget 重建,简化了状态更新逻辑。
  • InheritedModel<T>InheritedWidget 的子类,支持按 "维度(aspect)" 精确控制更新。普通 InheritedWidget 数据变化时所有依赖者都会重建,而 InheritedModel 可指定只有关注特定 "aspect" 的 Widget 才重建,优化性能。

二、使用场景

1. ✅InheritedWidget:基础跨层级数据共享

适用场景:共享静态或低频变化的数据(如主题配置、用户基本信息)。

Demo:共享应用主题配置

dart 复制代码
import 'package:flutter/material.dart';

// 1. 定义需要共享的数据模型
class AppTheme {
  final Color primaryColor;
  final TextStyle textStyle;

  AppTheme({required this.primaryColor, required this.textStyle});
}

// 2. 实现 InheritedWidget 子类
class ThemeInheritedWidget extends InheritedWidget {
  final AppTheme theme;
  // 提供一个便捷方法供子Widget获取实例
  static ThemeInheritedWidget? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<ThemeInheritedWidget>();
  }

  const ThemeInheritedWidget({
    super.key,
    required this.theme,
    required super.child,
  });

  // 决定数据变化时是否通知依赖的Widget
  @override
  bool updateShouldNotify(covariant ThemeInheritedWidget oldWidget) {
    // 当主题的primaryColor或textStyle变化时,通知更新
    return theme.primaryColor != oldWidget.theme.primaryColor ||
        theme.textStyle != oldWidget.theme.textStyle;
  }
}

// 3. 子Widget使用共享数据
class ThemedText extends StatelessWidget {
  final String text;

  const ThemedText(this.text, {super.key});

  @override
  Widget build(BuildContext context) {
    // 获取共享的主题数据
    final theme = ThemeInheritedWidget.of(context)?.theme;
    return Text(
      text,
      style: theme?.textStyle ?? const TextStyle(),
    );
  }
}

// 4. 用法示例
class InheritedWidgetDemo extends StatefulWidget {
  const InheritedWidgetDemo({super.key});

  @override
  State<InheritedWidgetDemo> createState() => _InheritedWidgetDemoState();
}

class _InheritedWidgetDemoState extends State<InheritedWidgetDemo> {
  late AppTheme currentTheme;

  @override
  void initState() {
    super.initState();
    currentTheme = AppTheme(
      primaryColor: Colors.blue,
      textStyle: const TextStyle(color: Colors.black, fontSize: 16),
    );
  }

  // 切换主题
  void _toggleTheme() {
    setState(() {
      currentTheme = AppTheme(
        primaryColor: currentTheme.primaryColor == Colors.blue ? Colors.red : Colors.blue,
        textStyle: currentTheme.textStyle.color == Colors.black
            ? const TextStyle(color: Colors.white, fontSize: 18)
            : const TextStyle(color: Colors.black, fontSize: 16),
      );
    });

    ThemeInheritedWidget.of(contetxt).apptheme = cu
  }

  @override
  Widget build(BuildContext context) {
    return ThemeInheritedWidget(
      theme: currentTheme,
      child: Scaffold(
        appBar: AppBar(
          title: const Text('InheritedWidget Demo'),
          backgroundColor: currentTheme.primaryColor,
        ),
        body: Center(
          child: ThemedText('共享的主题文本'),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: _toggleTheme,
          child: const Icon(Icons.color_lens),
        ),
      ),
    );
  }
}

关键点:

  • 通过 context.dependOnInheritedWidgetOfExactType 建立依赖关系。
  • updateShouldNotify 决定数据变化时是否触发子 Widget 重建。

2. ✅InheritedNotifier:结合监听者模式的状态管理

适用场景:共享高频变化且需要自动通知更新的数据(如计数器、表单状态)。

Demo:计数器状态管理

dart 复制代码
import 'package:flutter/material.dart';

// 1. 定义可监听的状态(继承ChangeNotifier,实现Listenable)
class CounterNotifier extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners(); // 通知监听者(触发更新)
  }
}

// 2. 实现InheritedNotifier子类
class CounterInheritedNotifier extends InheritedNotifier<CounterNotifier> {
  // 提供便捷获取方法
  static CounterInheritedNotifier? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<CounterInheritedNotifier>();
  }

  const CounterInheritedNotifier({
    super.key,
    required CounterNotifier notifier,
    required super.child,
  }) : super(notifier: notifier);

  // 无需手动实现updateShouldNotify,InheritedNotifier会自动根据notifier的变化处理
}

// 3. 子Widget使用计数器
class CounterDisplay extends StatelessWidget {
  const CounterDisplay({super.key});

  @override
  Widget build(BuildContext context) {
    final notifier = CounterInheritedNotifier.of(context)?.notifier;
    return Text(
      '当前计数: ${notifier?.count ?? 0}',
      style: const TextStyle(fontSize: 20),
    );
  }
}

// 4. 用法示例
class InheritedNotifierDemo extends StatelessWidget {
  const InheritedNotifierDemo({super.key});

  @override
  Widget build(BuildContext context) {
    final counterNotifier = CounterNotifier();

    return CounterInheritedNotifier(
      notifier: counterNotifier,
      child: Scaffold(
        appBar: AppBar(title: const Text('InheritedNotifier Demo')),
        body: const Center(child: CounterDisplay()),
        floatingActionButton: FloatingActionButton(
          onPressed: counterNotifier.increment,
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}

关键点:

  • 无需手动管理 updateShouldNotifyInheritedNotifier 会在 Listenable 调用 notifyListeners 时自动触发更新。
  • 适合封装 ChangeNotifier 状态,简化状态管理逻辑。

3. ✅InheritedModel:按维度精确控制更新

适用场景:共享包含多个独立维度的数据(如一个页面同时展示用户信息、商品列表、购物车数量),且需要精确控制某一维度变化时只有相关 Widget 重建。

Demo:多维度用户信息更新

dart 复制代码
import 'package:flutter/material.dart';

// 1. 定义共享的数据模型(包含name和age两个维度)
class UserInfo {
  final String name;
  final int age;

  UserInfo({required this.name, required this.age});

  UserInfo copyWith({String? name, int? age}) {
    return UserInfo(
      name: name ?? this.name,
      age: age ?? this.age,
    );
  }
}

// 2. 定义维度标识(aspect)
enum UserAspect { name, age }

// 3. 实现InheritedModel子类
class UserInheritedModel extends InheritedModel<UserAspect> {
  final UserInfo userInfo;

  static UserInheritedModel? of(BuildContext context, {UserAspect? aspect}) {
    return context.dependOnInheritedWidgetOfExactType<UserInheritedModel>(
      aspect: aspect, // 指定关注的维度
    );
  }

  const UserInheritedModel({
    super.key,
    required this.userInfo,
    required super.child,
  });

  // 决定某一维度变化时是否通知依赖该维度的Widget
  @override
  bool updateShouldNotifyDependent(
    covariant UserInheritedModel oldWidget,
    Set<UserAspect> dependencies,
  ) {
    // 如果依赖name维度,且name变化,则通知
    if (dependencies.contains(UserAspect.name) &&
        userInfo.name != oldWidget.userInfo.name) {
      return true;
    }
    // 如果依赖age维度,且age变化,则通知
    if (dependencies.contains(UserAspect.age) &&
        userInfo.age != oldWidget.userInfo.age) {
      return true;
    }
    return false;
  }

  @override
  bool updateShouldNotify(covariant UserInheritedModel oldWidget) {
    // 基础更新判断(是否有任何变化)
    return userInfo.name != oldWidget.userInfo.name ||
        userInfo.age != oldWidget.userInfo.age;
  }
}

// 4. 子Widget:只依赖name维度
class NameDisplay extends StatelessWidget {
  const NameDisplay({super.key});

  @override
  Widget build(BuildContext context) {
    final userInfo = UserInheritedModel.of(context, aspect: UserAspect.name)?.userInfo;
    print('NameDisplay 重建了'); // 验证是否只在name变化时重建
    return Text('姓名: ${userInfo?.name ?? "未知"}');
  }
}

// 5. 子Widget:只依赖age维度
class AgeDisplay extends StatelessWidget {
  const AgeDisplay({super.key});

  @override
  Widget build(BuildContext context) {
    final userInfo = UserInheritedModel.of(context, aspect: UserAspect.age)?.userInfo;
    print('AgeDisplay 重建了'); // 验证是否只在age变化时重建
    return Text('年龄: ${userInfo?.age ?? 0}');
  }
}

// 6. 用法示例
class InheritedModelDemo extends StatefulWidget {
  const InheritedModelDemo({super.key});

  @override
  State<InheritedModelDemo> createState() => _InheritedModelDemoState();
}

class _InheritedModelDemoState extends State<InheritedModelDemo> {
  late UserInfo userInfo;

  @override
  void initState() {
    super.initState();
    userInfo = UserInfo(name: "张三", age: 20);
  }

  void _updateName() {
    setState(() {
      userInfo = userInfo.copyWith(name: "李四");
    });
  }

  void _updateAge() {
    setState(() {
      userInfo = userInfo.copyWith(age: 21);
    });
  }

  @override
  Widget build(BuildContext context) {
    return UserInheritedModel(
      userInfo: userInfo,
      child: Scaffold(
        appBar: AppBar(title: const Text('InheritedModel Demo')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: const [
              NameDisplay(),
              SizedBox(height: 20),
              AgeDisplay(),
            ],
          ),
        ),
        floatingActionButton: Row(
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            FloatingActionButton(
              onPressed: _updateName,
              child: const Icon(Icons.edit),
            ),
            const SizedBox(width: 10),
            FloatingActionButton(
              onPressed: _updateAge,
              child: const Icon(Icons.add),
            ),
          ],
        ),
      ),
    );
  }
}

关键点:

  • 通过 dependencies 区分 Widget 关注的维度(UserAspect)。
  • updateShouldNotifyDependent 精确控制某一维度变化时,只有依赖该维度的 Widget 才重建。

三、总结

核心能力 适用场景
InheritedWidget 基础跨层级数据共享 静态 / 低频变化数据(主题、用户信息)
InheritedNotifier 结合 Listenable 自动管理更新 高频变化数据(计数器、表单状态)
InheritedModel 按维度精确控制更新 多维度数据(需优化性能,避免无关 Widget 重建)
相关推荐
魔芋红茶几秒前
MySQL 从入门到精通 16:主从复制
android·mysql·adb
2501_915106322 小时前
移动端网页调试实战,iOS WebKit Debug Proxy 的应用与替代方案
android·前端·ios·小程序·uni-app·iphone·webkit
柯南二号3 小时前
【大前端】React Native 调用 Android、iOS 原生能力封装
android·前端·react native
可乐+冰03 小时前
Android 编写高斯模糊功能
android·人工智能·opencv
xzkyd outpaper6 小时前
Android中APK包含哪些内容?
android
蹦极的考拉6 小时前
网站日志里面老是出现{pboot:if((\x22file_put_co\x22.\x22ntents\x22)(\x22temp.php\x22.....
android·开发语言·php
程序员老刘7 小时前
Dart MCP翻车了!3.9.0版本无法运行,这个坑你踩过吗?
flutter·ai编程·客户端
安卓开发者7 小时前
Android Glide最佳实践:高效图片加载完全指南
android·glide
菠萝加点糖8 小时前
Android 使用MediaMuxer+MediaCodec编码MP4视频
android·音视频·编码
ideal树叶9 小时前
flutter 中 的 关键字
flutter