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 重建)
相关推荐
RaidenLiu15 分钟前
Flutter 状态管理:Provider 入门与实战
前端·flutter
吧嗒贝斯30 分钟前
Flutter项目中接入百度地图(2025 最新 Android版)
flutter
鹏多多.34 分钟前
flutter-使用AnimatedDefaultTextStyle实现文本动画
android·前端·css·flutter·ios·html5·web
新镜36 分钟前
Flutter报错...Unsupported class file major version 65
flutter
似霰2 小时前
安卓系统属性之androidboot.xxx转换成ro.boot.xxx
android·gitee
0wioiw02 小时前
Android-Kotlin基础(Jetpack①-ViewModel)
android
用户2018792831673 小时前
限定参数范围的注解之 "咖啡店定价" 的故事
android·java
xzkyd outpaper3 小时前
Android中视图测量、布局、绘制过程
android
泓博3 小时前
Android底部导航栏图标变黑色
android
包达叔3 小时前
使用 Tauri 开发 Android 应用:环境搭建与入门指南
android