- 原文地址:Widget - State - Context - InheritedWidget
- 原文作者:www.didierboelens.com
- 译文出自:掘金翻译计划
- 本文永久链接:github.com/xitu/gold-m…
- 译者:nanjingboy
- 校对者:Mirosalva、HearFishle
本文涵盖了 Flutter 应用中有关 Widget、State、Context 及 InheritedWidget 的重要概念。因为 InheritedWidget 是最重要且文档缺乏的部件之一,故需特别关注。
难度:初学者
前言
Flutter 中的 Widget、State 及 Context 是每个 Flutter 开发者都需要充分理解的最重要的概念之一。
虽然存在大量文档,但并没有一个能够清晰地解释它。
我将用自己的语言来解释这些概念,知道这些可能会让一些纯理论者感到不安,但本文的真正目的是试图说清以下主题:
- Stateful Widget 和 Stateless Widget 的区别
- Context 是什么
- State 是什么并且如何使用它
- context 与其 state 对象之间的关系
- InheritedWidget 及在 Widgets 树中传播信息的方式
- 重建的概念
本文同时发布于 Medium - Flutter Community。
第一部分:概念
Widget 的概念
在 Flutter 中,几乎所有的东西都是 Widget。
将一个 Widget 想象为一个可视化组件(或与应用可视化方面交互的组件)。
当你需要构建与布局直接或间接相关的任何内容时,你正在使用 Widget。
Widget 树的概念
Widget 以树结构进行组织。
包含其他 Widget 的 Widget 被称为父 Widget(或Widget 容器)。包含在父 Widget 中的 Widget 被称为子 Widget。
让我们用 Flutter 自动生成的基础应用来说明这一点。以下是简化代码,仅有 build 方法:
@override
Widget build(BuildContext){
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'You have pushed the button this many times:',
),
new Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: new Icon(Icons.add),
),
);
}
复制代码
如果我们现在观察这个基本示例,我们将获得以下 Widget 树结构(限制代码中存在的 Widget 列表):
Context 的概念
另外一个重要的概念是 Context。
Context 仅仅是已创建的所有 Widget 树结构中某个 Widget 的位置引用。
简而言之,将 context 作为 Widget 树的一部分,其中 context 所对应的 Widget 被添加到此树中。
一个 context 仅仅从属于一个 widget。
如果 widget ‘A’ 拥有子 widget,那么 widget ‘A’ 的 context 将成为其直接关联子 context 的父 context。
读到这里会很明显发现 context 是链接在一起的,并且会形成一个 context 树(父子关系)。
如果我们现在尝试在上图中说明 Context 的概念,我们得到(依旧是一个非常简化的视图)每种颜色代表一个 context(除了 MyApp,它是不同的):
Context 可见性 (简短描述):
某些东西 只能在自己的 context 或在其父 context 中可见。
通过上述描述我们可以将其从子 context 中提取出来,它很容易找到一个 祖先(= 父)Widget。
一个例子,考虑 Scaffold > Center > Column > Text:context.ancestorWidgetOfExactType(Scaffold) => 通过从 Text 的 context 得到树结构来返回第一个 Scaffold。
从父 context 中,也可以找到 后代(= 子)Widget,但不建议这样做(我们将稍后讨论)。
Widget 的类型
Widget 拥有 2 种类型:
Stateless Widget
这些可视化组件除了它们自身的配置信息外不依赖于任何其他信息,该信息在其直接父节点构建时提供。
换句话说,这些 Widget 一旦创建就不关心任何变化。
这样的 Widget 称为 Stateless Widget。
这种 Widget 的典型示例可以是 Text、Row、Column 和 Container 等。在构建时,我们只需将一些参数传递给它们。
参数可以是装饰、尺寸、甚至其他 widget 中的任何内容。需要强调的是,该配置一旦被创建,在下次构建过程之前都不会改变。
stateless widget 只有在 loaded/built 时才会绘制一次,这意味着任何事件或用户操作都无法对该 Widget 进行重绘。
Stateless Widget 生命周期
以下是与 Stateless Widget 相关的典型代码结构。
如下所示,我们可以将一些额外的参数传递给它的构造函数。但请记住,这些参数在后续阶段将不改变(变化),并且必须按照已有状态使用。
class MyStatelessWidget extends StatelessWidget {
MyStatelessWidget({
Key key,
this.parameter,
}): super(key:key);
final parameter;
@override
Widget build(BuildContext context){
return new ...
}
}
复制代码
即使有另一个方法可以被重写(createElement),后者也几乎不会被重写。唯一需要被重写的是 build 方法。
这种 Stateless Widget 的生命周期是相当简单的:
- 初始化
- 通过 build() 渲染
Stateful Widget
其他一些 Widget 将处理一些在 Widget 生命周期内会发生变化的内部数据。因此,此类数据会变为动态。
该 Widget 所持有的数据集在其生命周期内可能会发生变化,这样的数据被称为 State。
这些 Widget 被称为 Stateful Widget。
此类 Widget 的示例可能是用户可选择的复选框列表,也可以是根据条件禁用的 Button 按钮。
State 的概念
State 定义了 StatefulWidget 实例的 “行为”。
它包含了用于 交互 / 干预 Widget 信息:
- 行为
- 布局
应用于 State 的任何更改都会强制 Widget 进行重建。
State 和 Context 的关系
对于 Stateful Widget,State 与 Context 相关联。并且此关联是永久性的,State 对象将永远不会改变其 context。
即使可以在树结构周围移动 Widget Context,State 仍将与该 context 相关联。
当 State 与 Context 关联时,State 被视为已挂载。
重点:
State 对象 与 context 相关联,就意味着该 State 对象是不