Flutter 之 InheritedWidget

[复制链接]
发表于 2025-12-19 05:58:51 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
InheritedWidget 是 Flutter 框架中的一个紧张类,用于在 Widget 树中共享数据。它是 Flutter 中数据通报和状态管理的根本之一。通过 InheritedWidget,你可以让子 Widget 在不须要显式通报数据的环境下,访问先人 Widget 中的数据。这种机制对于跨多个条理的 Widget 共享状态非常有效。


一、 为什么会有 InheritedWidget?

在 Flutter 中,Widget 树是单向的,意味着数据从上层(父 Widget)流向下层(子 Widget)。而 Flutter 中的 Widget 是不可变的,也就是说,你不能直接修改它们的状态。因此,当须要跨多个 Widget 条理通报状态或共享数据时,通常会遇到两类标题:


  • **属性通报:**假如须要共享的数据通报层级过多,父 Widget 必须将数据一层一层地通报给子 Widget,直到最须要这个数据的 Widget。这个过程会导致代码变得冗长,维护困难。
  • 数据共享: 在某些环境下,多个子 Widget 大概须要访问同一个数据源,这种环境下,直接通过构造函数通报数据显得非常不方便,尤其是在较深的 Widget 树中。
InheritedWidget 的出现正是为了办理这两个标题,它让 Widget 树中的任何子 Widget 可以便捷地访问到先人 Widget 提供的数据,而不必显式地通报给每一个子 Widget。
二、InheritedWidget 的根本工作原理

InheritedWidget 是一个可以在 Widget 树中流传数据的 Widget。它通过实现 updateShouldNotify 方法来关照下层的 Widget,当数据发生厘革时,这些 Widget 会重新构建(rebuild),从而获取新的数据。


  • 当 InheritedWidget 发生厘革时,它会通过 notifyClients 方法关照全部依靠于它的 Widget。
  • 在 Widget 树中的下层 Widget 可以通过 of 方法获取到先人 Widget 提供的状态(数据)。
三、InheritedWidget 的办理标题



  • 凌驾多层 Widget 树通报数据: 在 Widget 树的上层将数据放入 InheritedWidget 中后,全部的子 Widget 可以访问到该数据,而不须要通过构造函数一层一层地通报。
  • 制止重复构建: 当 InheritedWidget 的数据发生厘革时,只有依靠该数据的 Widget 会重新构建。其他不依靠该数据的 Widget 会保持稳固,优化了性能。
  • 简化状态管理: InheritedWidget 是许多状态管明白决方案(如 Provider、Riverpod 等)的根本,提供了一个简单且高效的方式来管理和共享状态。
四、 InheritedWidget 的使用例子

  1. import 'package:flutter/material.dart';
  2. // 创建一个自定义的 InheritedWidget
  3. class MyInheritedWidget extends InheritedWidget {
  4.   final int counter;
  5.   MyInheritedWidget({required Widget child, required this.counter}) : super(child: child);
  6.   // 判断是否需要通知下层 Widget 更新
  7.   @override
  8.   bool updateShouldNotify(MyInheritedWidget oldWidget) {
  9.     return counter != oldWidget.counter;
  10.   }
  11.   // 提供获取数据的方法
  12.   static MyInheritedWidget? of(BuildContext context) {
  13.     return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
  14.   }
  15. }
  16. // 一个使用 InheritedWidget 的子 Widget
  17. class CounterDisplay extends StatelessWidget {
  18.   @override
  19.   Widget build(BuildContext context) {
  20.     final inheritedWidget = MyInheritedWidget.of(context);
  21.     return Text('Counter: ${inheritedWidget?.counter ?? 0}');
  22.   }
  23. }
  24. void main() {
  25.   runApp(MyApp());
  26. }
  27. class MyApp extends StatelessWidget {
  28.   int counter = 0;
  29.   @override
  30.   Widget build(BuildContext context) {
  31.     return MaterialApp(
  32.       home: Scaffold(
  33.         appBar: AppBar(title: Text('InheritedWidget Example')),
  34.         body: MyInheritedWidget(
  35.           counter: counter,
  36.           child: Column(
  37.             mainAxisAlignment: MainAxisAlignment.center,
  38.             children: [
  39.               CounterDisplay(),
  40.               ElevatedButton(
  41.                 onPressed: () {
  42.                   counter++;
  43.                   // 由于 counter 变化,MyInheritedWidget 会通知子 Widget 更新
  44.                 },
  45.                 child: Text('Increment Counter'),
  46.               ),
  47.             ],
  48.           ),
  49.         ),
  50.       ),
  51.     );
  52.   }
  53. }
复制代码
在上面的代码中:


  • MyInheritedWidget 包罗了一个 counter 变量,它可以被下层的子 Widget 访问。
  • CounterDisplay 使用 MyInheritedWidget.of(context) 来获取并表现 counter 的值。
  • 当按钮点击时,counter 值增长并重新构建整个 Widget 树。
五、 InheritedWidget 和性能

固然 InheritedWidget 可以资助我们轻松共享数据,但它也有一些限定,尤其是在性能上:


  • 仅依靠 InheritedWidget 的子 Widget 会重新构建,但这个重新构建的范围大概是你未预推测的。因此,要审慎使用,制止不须要的性能斲丧。
  • 对于复杂的应用,大概会更倾向于使用一些更强盛的状态管理工具(如 Provider、Riverpod)来简化使用和提拔性能。
六、疑问

1、InheritedWidget只能是父组件状态通报到子组件不能子组件通报到父组件吗

是的,InheritedWidget 的操持原则是从父组件向下通报数据,而不是从子组件向父组件通报数据。它重要用于共享 不可变的数据,大概是一些全局状态(如主题、语言、设置等),并使得在 InheritedWidget 树下的子 Widget 可以访问这些数据。
1、父组件到子组件的数据通报

InheritedWidget 答应父组件通过它通报数据到子组件。子组件通过 of 方法获取父组件(或先人组件)中提供的状态或数据。比方,你可以通过一个 InheritedWidget 在树的上层通报一些数据(如计数器值),然后让树下的任何子组件获取该数据。
2、为什么不支持子组件通报到父组件?

InheritedWidget 是单向数据流的模子,它不支持反向通报(即子组件直接关照父组件更新)。这是由于 InheritedWidget 重要用于 数据共享,而不是 数据修改。其重要目标是提供一种高效的方式来向下共享数据,而不涉及子组件控制父组件的举动。
2、子组件的数据怎样通报给父组件

要从子组件通报数据回父组件,通常有两种方式:
1. 回调函数(Callback)

子组件通过调用父组件传入的回调函数将数据或变乱通报给父组件。父组件可以在回调函数中处理处罚数据,并更新状态。
  1. import 'package:flutter/material.dart';
  2. class ParentWidget extends StatefulWidget {
  3.   @override
  4.   _ParentWidgetState createState() => _ParentWidgetState();
  5. }
  6. class _ParentWidgetState extends State<ParentWidget> {
  7.   int counter = 0;
  8.   void _incrementCounter() {
  9.     setState(() {
  10.       counter++;
  11.     });
  12.   }
  13.   @override
  14.   Widget build(BuildContext context) {
  15.     return Scaffold(
  16.       appBar: AppBar(title: Text('Parent to Child Example')),
  17.       body: Column(
  18.         children: [
  19.           Text('Counter: $counter'),
  20.           ChildWidget(onPressed: _incrementCounter), // 传递回调函数
  21.         ],
  22.       ),
  23.     );
  24.   }
  25. }
  26. class ChildWidget extends StatelessWidget {
  27.   final VoidCallback onPressed;  // 回调函数
  28.   ChildWidget({required this.onPressed});
  29.   @override
  30.   Widget build(BuildContext context) {
  31.     return ElevatedButton(
  32.       onPressed: onPressed,  // 触发父组件传递的回调
  33.       child: Text('Increment Counter'),
  34.     );
  35.   }
  36. }
  37. void main() {
  38.   runApp(MaterialApp(home: ParentWidget()));
  39. }
复制代码
在这个例子中:


  • ParentWidget 通过 onPressed 回调函数通报给 ChildWidget。
  • ChildWidget 点击按钮时会调用 onPressed 回调,关照父组件更新 counter。
2. 状态提拔(Lift State Up)

假如须要共享和修改状态,可以将状态提拔到迩来的共同先人组件,父组件和子组件都可以通过该先人组件来管理和更新状态。通常,这种方式也与回调函数一起使用。
  1. import 'package:flutter/material.dart';
  2. class ParentWidget extends StatefulWidget {
  3.   @override
  4.   _ParentWidgetState createState() => _ParentWidgetState();
  5. }
  6. class _ParentWidgetState extends State<ParentWidget> {
  7.   int counter = 0;
  8.   void _updateCounter(int value) {
  9.     setState(() {
  10.       counter = value;
  11.     });
  12.   }
  13.   @override
  14.   Widget build(BuildContext context) {
  15.     return Scaffold(
  16.       appBar: AppBar(title: Text('State Lifting Example')),
  17.       body: Column(
  18.         children: [
  19.           Text('Counter: $counter'),
  20.           ChildWidget(onCounterChanged: _updateCounter),  // 状态提升
  21.         ],
  22.       ),
  23.     );
  24.   }
  25. }
  26. class ChildWidget extends StatelessWidget {
  27.   final Function(int) onCounterChanged; // 传递回调
  28.   ChildWidget({required this.onCounterChanged});
  29.   @override
  30.   Widget build(BuildContext context) {
  31.     return ElevatedButton(
  32.       onPressed: () {
  33.         onCounterChanged(5);  // 修改父组件的状态
  34.       },
  35.       child: Text('Update Counter to 5'),
  36.     );
  37.   }
  38. }
  39. void main() {
  40.   runApp(MaterialApp(home: ParentWidget()));
  41. }
复制代码
总结



  • InheritedWidget:重要用于将数据从父组件通报到子组件,子组件无法直接修改父组件的状态。
  • 回调函数和状态提拔:是从子组件向父组件通报数据或修改父组件状态的常用方法。
3、兄弟组件怎样共享数据

在 Flutter 中,兄弟组件(即同一个父组件下的差别子组件)之间共享数据通常有几种方式。以下是几种常见的实现方法:
1. 通过父组件通报数据

最简单的方式是通过父组件来和谐兄弟组件之间的通讯。父组件将共享数据通报给它的子组件,然后子组件通过回调函数将数据发送回父组件,父组件再将数据通报给另一个子组件。如许可以保持数据流的单向性。
示例代码:

  1. import 'package:flutter/material.dart';
  2. class ParentWidget extends StatefulWidget {
  3.   @override
  4.   _ParentWidgetState createState() => _ParentWidgetState();
  5. }
  6. class _ParentWidgetState extends State<ParentWidget> {
  7.   int sharedData = 0;
  8.   // 更新数据并通知所有子组件
  9.   void updateSharedData(int newValue) {
  10.     setState(() {
  11.       sharedData = newValue;
  12.     });
  13.   }
  14.   @override
  15.   Widget build(BuildContext context) {
  16.     return Scaffold(
  17.       appBar: AppBar(title: Text('Shared Data Between Siblings')),
  18.       body: Column(
  19.         children: [
  20.           Text('Shared Data: $sharedData'),
  21.           // 传递数据到子组件
  22.           ChildWidget1(onDataChanged: updateSharedData),
  23.           ChildWidget2(sharedData: sharedData),
  24.         ],
  25.       ),
  26.     );
  27.   }
  28. }
  29. class ChildWidget1 extends StatelessWidget {
  30.   final Function(int) onDataChanged;
  31.   ChildWidget1({required this.onDataChanged});
  32.   @override
  33.   Widget build(BuildContext context) {
  34.     return ElevatedButton(
  35.       onPressed: () => onDataChanged(42), // 通过回调通知父组件更新数据
  36.       child: Text('Update Data to 42'),
  37.     );
  38.   }
  39. }
  40. class ChildWidget2 extends StatelessWidget {
  41.   final int sharedData;
  42.   ChildWidget2({required this.sharedData});
  43.   @override
  44.   Widget build(BuildContext context) {
  45.     return Text('Child 2 sees Shared Data: $sharedData');
  46.   }
  47. }
  48. void main() {
  49.   runApp(MaterialApp(home: ParentWidget()));
  50. }
复制代码
在这个例子中:


  • ParentWidget 维护了 sharedData,并通过 updateSharedData 方法更新它。
  • ChildWidget1 通过回调向父组件发送数据变更哀求,父组件汲取到数据后更新 sharedData。
  • ChildWidget2 获取 sharedData,并表现它。
2. 使用 InheritedWidget 或 InheritedModel

假如你盼望在多个兄弟组件之间共享数据,且不盼望通过父组件直接通报数据,可以使用 InheritedWidget 或 InheritedModel 来共享数据。这两者可以大概在 Widget 树中向下通报数据,使得同一父级的多个子组件都可以访问这些数据。
使用 InheritedWidget 共享数据:

  1. import 'package:flutter/material.dart';
  2. // 创建一个 InheritedWidget
  3. class SharedData extends InheritedWidget {
  4.   final int data;
  5.   SharedData({required this.data, required Widget child}) : super(child: child);
  6.   // 获取数据的静态方法
  7.   static SharedData? of(BuildContext context) {
  8.     return context.dependOnInheritedWidgetOfExactType<SharedData>();
  9.   }
  10.   @override
  11.   bool updateShouldNotify(SharedData oldWidget) {
  12.     return oldWidget.data != data;
  13.   }
  14. }
  15. class ParentWidget extends StatelessWidget {
  16.   @override
  17.   Widget build(BuildContext context) {
  18.     return Scaffold(
  19.       appBar: AppBar(title: Text('Shared Data Using InheritedWidget')),
  20.       body: SharedData(
  21.         data: 100, // 共享数据
  22.         child: Column(
  23.           children: [
  24.             ChildWidget1(),
  25.             ChildWidget2(),
  26.           ],
  27.         ),
  28.       ),
  29.     );
  30.   }
  31. }
  32. class ChildWidget1 extends StatelessWidget {
  33.   @override
  34.   Widget build(BuildContext context) {
  35.     final sharedData = SharedData.of(context)?.data;
  36.     return Text('Child 1 sees Shared Data: $sharedData');
  37.   }
  38. }
  39. class ChildWidget2 extends StatelessWidget {
  40.   @override
  41.   Widget build(BuildContext context) {
  42.     final sharedData = SharedData.of(context)?.data;
  43.     return Text('Child 2 sees Shared Data: $sharedData');
  44.   }
  45. }
  46. void main() {
  47.   runApp(MaterialApp(home: ParentWidget()));
  48. }
复制代码
在这个例子中:


  • SharedData 继承自 InheritedWidget,用于在树下的多个子组件之间共享数据。
  • ChildWidget1 和 ChildWidget2 都可以通过 SharedData.of(context) 获取共享的 data。
3. 使用 Provider 包

假如须要跨多个页面大概复杂的共享数据,可以使用 Flutter 的状态管理框架,如 Provider。Provider 是一种比 InheritedWidget 更加简便和机动的办理方案。
七、总结

InheritedWidget 是 Flutter 中处理处罚跨条理数据共享的根本工具。它能有效制止 “prop drilling” 标题,使得子 Widget 可以轻松访问先人 Widget 的数据。它经常用作状态管理的根本,而且在 Flutter 中的许多第三方库和框架(如 Provider)中都有广泛应用。
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表