Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >从零开始的Flutter之旅: InheritedWidget

从零开始的Flutter之旅: InheritedWidget

作者头像
Rouse
发布于 2020-06-11 13:06:56
发布于 2020-06-11 13:06:56
66300
代码可运行
举报
文章被收录于专栏:Android补给站Android补给站
运行总次数:0
代码可运行

Rouse

读完需要

11

分钟

速读仅需 4 分钟

1

往期回顾

从零开始的 Flutter 之旅: StatelessWidget

从零开始的 Flutter 之旅: StatefulWidget

在之前的文章中,介绍了 StatelessWidget 与 StatefulWidget 的特性与它们的呈现原理。

这期要聊的是它们的另一个兄弟 InheritedWidget。

2

特性

InheritedWidget 是 Flutter 中的一个非常重要的功能组件,它能够提供数据在 widget 树中从上到下进行传递。保证数据在不同子 widget 中进行共享。这对于一些需要使用共享数据的场景非常有效,例如,在 Flutter SDK 中就是通过 InheritedWidget 来共享应用的主题与语言信息。

可能你还有点模糊,别急,下面我们通过一个简单的示例来了解 InheritedWidget。

3

示例

相信开始学 Flutter 时都看过官方的计数器示例。我们将官方提供的计数器示例使用 InheritedWidget 进行改造。

首先我们需要一个 CountInheritedWidget,它继承于 InheritedWidget。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1class CountInheritedWidget extends InheritedWidget {
 2  CountInheritedWidget({@required this.count, Widget child})
 3      : super(child: child);
 4
 5  // 共享数据,计数的数量
 6  final int count;
 7
 8  // 统一的获取CountInheritedWidget实例, 方便树中子widget的获取共享数据
 9  // 必须在State中调用才会有效
10  static CountInheritedWidget of(BuildContext context) {
11    // 调用共享数据的子widget将不会回调didChangeDependencies方法,即子widget将不会更新
12    // return context.getElementForInheritedWidgetOfExactType<CountInheritedWidget>().widget;
13    return context.dependOnInheritedWidgetOfExactType<CountInheritedWidget>();
14  }
15
16  // true -> 通知树中依赖改共享数据的子widget
17  @override
18  bool updateShouldNotify(CountInheritedWidget oldWidget) {
19    return oldWidget.count != count;
20  }
21}

  1. 在 CountInheritedWidget 中提供共享计数的数量 count
  2. 同时为外部提供统一的获取 CountInheritedWidget 实例的 of 方法
  3. 最后再重写 updateShouldNotify 方法,来通知依赖该共享 count 的子 widget 进行更新

现在已经有了共享数据 count 的提供,接下来是在具体的子 widget 中进行使用。

我们抽离出一个 CountText 子 widget

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1class CountText extends StatefulWidget {
 2  @override
 3  _CountTextState createState() {
 4    return _CountTextState();
 5  }
 6}
 7
 8class _CountTextState extends State<CountText> {
 9  @override
10  Widget build(BuildContext context) {
11    return Text("count: ${CountInheritedWidget.of(context).count.toString()}");
12  }
13
14  @override
15  void didChangeDependencies() {
16    super.didChangeDependencies();
17    print("didChangeDependencies");
18  }
19}

  1. 内部引用了 CountInheritedWidget 中的共享数据 count,通过 of 方法获取 CountInheritedWidget 实例
  2. didChangeDependencies 可以用来监听子 widget 依赖是否反生改变

最后,我们再将 CountInheritedWidget 与 CountText 结合起来,通过简单的点击自增事件来看下效果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1class CountWidget extends StatefulWidget {
 2  @override
 3  _CountState createState() {
 4    return _CountState();
 5  }
 6}
 7
 8class _CountState extends State<CountWidget> {
 9  int count = 0;
10
11  @override
12  Widget build(BuildContext context) {
13    return MaterialApp(
14      title: 'Count App',
15      theme: new ThemeData(primarySwatch: Colors.blue),
16      home: Scaffold(
17        appBar: AppBar(
18          title: Text("Count"),
19        ),
20        body: Center(
21          child: CountInheritedWidget(
22            count: count,
23            child: Column(
24              mainAxisAlignment: MainAxisAlignment.center,
25              children: <Widget>[
26                CountText(),
27                RaisedButton(
28                  onPressed: () => setState(() => count++),
29                  child: Text("Increment"),
30                )
31              ],
32            ),
33          ),
34        ),
35      ),
36    );
37  }
38}

上面的层级关系是 CountText 刚好是 CountInheritedWidget 的子 widget。

现在我们通过点击事件直接改变外部的 count 值,如果 InheritedWidget 从上到下数据传到的效果能够生效,那么在 CountText 中引用的 count 将会与外部 count 同步,程序呈现的效果将会是自增的,同时由于依赖的 count 发生改变 CountText 中的 didChangeDependencies 也会回调。

我们直接运行一下

点击后的输出日志

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1I/flutter: didChangeDependencies

说明 InheritedWidget 的效果已经生效,通过 InheritedWidget 的使用,我们 可以很方便的在嵌套下层的子 widget 中拿到上层的数据,或者说整个 widget 的共享数据。

4

分析

在依赖方式改变时子 widget 的 didChangeDependencies 会回调,但由于你可能会在该方法中做一些特殊的操作,例如网络请求。只是需要一次就可以。如果是套用我们上面的示例,将会在 count 子增时反复调用。

为了防止 didChangeDependencies 的调用,我们再来看 CountInheritedWidget 的 of 方法中注释的那部分

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1  static CountInheritedWidget of(BuildContext context) {
2    // 调用共享数据的子widget将不会回调didChangeDependencies方法,即子widget将不会更新
3    // return context.getElementForInheritedWidgetOfExactType<CountInheritedWidget>().widget;
4    return context.dependOnInheritedWidgetOfExactType<CountInheritedWidget>();
5  }  

我们使用的是 dependOnInheritedWidgetOfExactType 方法,依赖的共享数据发生改变时会回调子 widget 中的 didChangeDependencies 方法,如果我们不想要子 widget 调用该方法,可以使用注释的代码,通过 getElementForInheritedWidgetOfExactType 方法来获取共享数据。

如果此时我们再运行一下项目,点击 count 自增,控制台将不会再输出日志。这样就可以解决 didChangeDependencies 的反复调用。

而这两个方法的主要区别是在 dependOnInheritedWidgetOfExactType 调用的过程中会进行注册依赖关系

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1  @override
2  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
3    assert(ancestor != null);
4    _dependencies ??= HashSet<InheritedElement>();
5    _dependencies.add(ancestor);
6    ancestor.updateDependencies(this, aspect);
7    return ancestor.widget;
8  }

所以 dependOnInheritedWidgetOfExactType 更新依赖的子 widget 中的 didChangeDependencies 方法。

思考下一个问题,虽然现在 didChangeDependencies 方法不会调用,但是 CountText 的 build 方法还是会执行。原因是在 CountWidget 中通过 setState 来改变 count 值,会重新 build 所用的子 widget。但我们真正想要的只是更新子 widget 中依赖的 CountInheritedWidget 的组件值。

那么如何解决呢?这里提供一个解决方案是为子 widget 提供缓存。可以通过封装一个简单的 StatefulWidget,将子 widget 缓存起来。如果对这块感兴趣的,可以期待我的后续文章。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-06-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android补给站 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验