理解InheritedWidget及应用

语言: CN / TW / HK

本篇文章主要介绍Flutter中的InheritedWidget小部件,以及其官方小部件库中的应用。

官方介绍

关于InheritedWidget,在官方文档中是这样描述的: * 1,可以沿着树向下传递信息。 * 2,可以使用 BuildContext.dependOnInheritedWidgetOfExactType方法,获取最近的指定类型的inherited widget。 * 3,当inherited widget状态发生改变时,所有依赖其状态的子部件都会进行rebuild

我们该如何理解InheritedWidget呢,接下来,通过几个示例来阐述InheritedWidget

ThemeData

我们经常使用的ThemeData就是一个inheritedWidget,通常我们ApprootMaterialApp,这样我们的ThemeDataWidget树的最顶端。

我们可以使用 final themeData = Theme.of(context); 来获取当前的主题,当主题改变时,会从Widget树的最顶端,沿着树,从上到下,依次刷新每个依赖themeData的子部件。

static ThemeData of(BuildContext context) { final _InheritedTheme? inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedTheme>(); final MaterialLocalizations? localizations = Localizations.of<MaterialLocalizations>(context, MaterialLocalizations); final ScriptCategory category = localizations?.scriptCategory ?? ScriptCategory.englishLike; final ThemeData theme = inheritedTheme?.theme.data ?? _kFallbackTheme; return ThemeData.localize(theme, theme.typography.geometryThemeFor(category)); }static ThemeData of(BuildContext context)的实现我们可以看出其是一个InheritedWidget

示例

无状态InheritedWidget

对于无状态的InheritedWidget,我们以读取其共享的数据为例展开,接下来,我们新建Counter

```dart class Counter extends InheritedWidget { // 1 const Counter( {Key? key, required this.child, required this.counter}) : super(key: key, child: child); // 2

final int counter;

final Widget child;

static Counter? of(BuildContext context) { // 3 return context.dependOnInheritedWidgetOfExactType(); }

@override bool updateShouldNotify(covariant Counter oldWidget) { // 4 return oldWidget.counter != counter; } } `` * 1,Counter继承InheritedWidget。 * 2,在构建时,需要提供counter值和子Widget`。 * 3,向子部件提供获取状态的接口。 * 4,是否通知子部件进行状态刷新。

我们可以这样使用该InheritedWidget

``` Widget buildInheritedWidget(BuildContext context) { return Counter( counter: 5, child: Scaffold( appBar: AppBar(), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( 'You have pushed the button this many times:', ), WidgetE(), ], ), ), ));

}

@override Widget build(BuildContext context) {

return buildInheritedWidget(context); } ``` 运行结果如下

Simulator Screen Shot - iPhone 13 - 2022-05-14 at 11.49.04.png

接下来,我们创建一个有状态的InheritedWidget

有状态的InheritedWidget

1,创建一个持有状态的InheritedWidget

```dart class CounterWidget extends InheritedWidget { const CounterWidget( {Key? key, required this.counter, required this.child, required this.data}) : super(child: child, key: key);

final int counter;

final Widget child;

final CounterWrapperState data; // 1

/// 获取 CounterWidget 实例 static CounterWidget? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType(); }

@override bool updateShouldNotify(covariant CounterWidget oldWidget) { // TODO: implement updateShouldNotify return oldWidget.counter != counter; } } ```

  • 1,相比于无状态的,这里新增了一个CounterWrapperState

解下来,创建一个StatefulWidget,并将CounterWidget作为该部件的子部件。 ```dart class CounterWrapper extends StatefulWidget { final Widget child; // 1 const CounterWrapper({Key? key, required this.child}) : super(key: key);

static CounterWrapperState of(BuildContext context, {bool build = true}) { // 2 return build ? (context.dependOnInheritedWidgetOfExactType())!.data : context.findAncestorWidgetOfExactType()!.data; }

@override CounterWrapperState createState() => CounterWrapperState(); }

class CounterWrapperState extends State { int counter = 0; void incrementCounter() { // 3 setState(() { counter++; }); }

@override Widget build(BuildContext context) { // 4 return CounterWidget(data: this, counter: counter, child: widget.child); } } `` * 1,在构建时,需提供child,并传递给CounterWidget。 * 2,提供获取状态的接口,有两种方式 : 当使用dependOnInheritedWidgetOfExactType时,当状态改变时,会重新rebuild子部件。 使用findAncestorWidgetOfExactType`时,当状态改变时,子部件不会刷新。

  • 3,计数器加1,刷新Widget。
  • 4,将CounterWidget作为CounterWrapper的子部件。

子Widget刷新、局部刷新、不刷新

接下来,创建5个不同的Widget:

```dart /// 使计数状态改变 class WidgetA extends StatefulWidget { const WidgetA({Key? key}) : super(key: key);

@override _WidgetAState createState() => _WidgetAState(); }

class _WidgetAState extends State { @override Widget build(BuildContext context) { print("A refresh"); return ElevatedButton(onPressed: onPressed, child: Text("Increment")); } onPressed() { CounterWrapperState wrapper = CounterWrapper.of(context, build: false); wrapper.incrementCounter(); } }

/// 局部刷新 class WidgetB extends StatelessWidget { const WidgetB({Key? key}) : super(key: key); @override Widget build(BuildContext context) { print("widget B 整个 刷新"); return Builder(builder: (contextTwo) { print("widget B Text 局部刷新"); final CounterWrapperState state = CounterWrapper.of(contextTwo, build: true); return Text('${state.counter}'); }); } }

/// inheritedWidget刷新时,也刷新 class WidgetC extends StatelessWidget { @override Widget build(BuildContext context) { final CounterWrapperState state = CounterWrapper.of(context, build: true); print("widget C 刷新"); return new Text('I am Widget C ${state.counter}'); } } /// inheritedWidget刷新时,不刷新 class WidgetC1 extends StatelessWidget { @override Widget build(BuildContext context) { final CounterWrapperState state = CounterWrapper.of(context, build: false); print("widget C1 刷新"); return new Text('I am Widget C1 ${state.counter}'); } } /// 不依赖inheriteWidget状态的子部件 class WidgetD extends StatelessWidget { @override Widget build(BuildContext context) { print("widget D 刷新"); return new Text('I am Widget D'); } } `` * 1,**WidgetA:** 使 **CounterWrapper** 的状态改变。 * 2,**WidgetB:** 不刷新整个页面,对依赖部分进行局部刷新`。 * 3,WidgetC: 依赖inheritedWidget的状态,状态改变时,也随之刷新。 * 4,WidgetC1: 只在初始化时,获取inheritedWidget的状态,不随之刷新。 * 5,WidgetD: inheritedWidget的子部件,但不依赖其状态

我们将这些Widget加载到视图中 Widget buildStatefulInheritedWidget(BuildContext context) { return CounterWrapper( child: Scaffold( appBar: AppBar(), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text( 'You have pushed the button this many times:', ), WidgetB(), WidgetA(), WidgetC(), WidgetC1(), WidgetD(), ], ), ), )); } 界面如下

Simulator Screen Shot - iPhone 13 - 2022-05-14 at 13.04.59.png

点击increment,只会有 WidgetCWidgetB 进行刷新。 flutter: widget C 刷新 flutter: widget B Text 局部刷新

Simulator Screen Shot - iPhone 13 - 2022-05-14 at 13.08.43.png

这样我们就可以定义我们自己的inheritedWidget,并通知其子部件进行数据刷新。

总结

我们可以使用InheritedWidget共享全局状态,子部件可以获取其状态,并控制小部件刷新

最后附上本文涉及的示例代码 inherited_widget_demo

如果觉得有收获请按如下方式给个 爱心三连:👍:点个赞鼓励一下。🌟:收藏文章,方便回看哦!。💬:评论交流,互相进步!