Flutter中Widget简介
Widget 概念
Flutter中几乎所有的对象都是一个 widget 。与原生开发中“控件”不同的是,Flutter 中的 widget 的概念更广泛,它不仅可以表示UI元素,也可以表示一些功能性的组件如:用于手势检测的 GestureDetector 、用于APP主题数据传递的 Theme 等等
Widget 接口
- Widget 中定义的属性(即配置信息)必须是不可变的(final),为什么不允许 Widget 中定义的属性变化呢?这是因为,Flutter 中如果属性发生变化则会重新构建Widget树,即重新创建新的 Widget 实例来替换旧的 Widget 实例
- key属性类似于 React/Vue 中的key,主要的作用是决定是否在下一次build时复用旧的 widget ,决定的条件在canUpdate()方法中。
- createElement() Flutter 框架在构建UI树时,会先调用此方法生成对应节点的Element对象。此方法是 Flutter 框架隐式调用的
- canUpdate(…)是一个静态方法,它主要用于在 widget 树重新build时复用旧的 widget ,其实具体来说,应该是:是否用新的 widget 对象去更新旧UI树上所对应的Element对象的配置,只要newWidget与oldWidget的runtimeType和key同时相等时就会用new widget去更新Element对象的配置,否则就会创建新的Element。
Flutter中的四棵树
- 根据 Widget 树生成一个 Element 树,Element 树中的节点都继承自 Element 类。
- 根据 Element 树生成 Render 树(渲染树),渲染树中的节点都继承自RenderObject 类。
- 根据渲染树生成 Layer 树,然后上屏显示,Layer 树中的节点都继承自 Layer 类。
1 | Container( // 一个容器 widget |
三棵树中,Widget 和 Element 是一一对应的,但并不和 RenderObject 一一对应。比如 StatelessWidget 和 StatefulWidget 都没有对应的 RenderObject。
渲染树在上屏前会生成一棵 Layer 树
StatelessWidget
- StatelessWidget用于不需要维护状态的场景,它通常在build方法中通过嵌套其他 widget 来构建UI,在构建过程中会递归的构建其嵌套的 widget
1 | class Echo extends StatelessWidget { |
Context
build方法有一个context参数,它是BuildContext类的一个实例,表示当前 widget 在 widget 树中的上下文,每一个 widget 都会对应一个 context 对象,context是当前 widget 在 widget 树中位置中执行”相关操作“的一个句柄(handle)。
在子树中获取父级 widget 的一个示例:
1 | class ContextRoute extends StatelessWidget { |
StatefulWidget
StatefulWidget类中添加了一个新的接口createState()。有状态的Widget。
State 对象和StatefulElement具有一一对应的关系
State
State表示与其对应的 StatefulWidget 要维护的状态,State 中的保存的状态信息可以:
- 在 widget 构建时可以被同步读取。
- 在 widget 生命周期中可以被改变,当State被改变时,可以手动调用其setState()方法通知Flutter 框架状态发生改变,Flutter 框架在收到消息后,会重新调用其build方法重新构建 widget 树,从而达到更新UI的目的。
- State 中有两个常用属性:
- widget,它表示与该 State 实例关联的 widget 实例,由Flutter 框架动态设置。注意,这种关联并非永久的,widget 实例在重新构建时可能会变化,但State实例只会在第一次插入到树中时被创建,当在重新构建时,如果 widget 被修改了,Flutter 框架会动态设置State. widget 为新的 widget 实例。
- context。StatefulWidget对应的 BuildContext,作用同StatelessWidget 的BuildContext。
- State生命周期
- initState:当 widget 第一次插入到 widget 树时会被调用,对于每一个State对象,Flutter 框架只会调用一次该回调,所以,通常在该回调中做一些一次性的操作,如状态初始化、订阅子树的事件通知等。
- didChangeDependencies():当State对象的依赖发生变化时会被调用。
- build():它主要是用于构建 widget 子树的,会在如下场景被调用:
a 在调用initState()之后。
b 在调用didUpdateWidget()之后。
c 在调用setState()之后。
c 在调用didChangeDependencies()之后。
d 在State对象从树中一个位置移除后(会调用deactivate)又重新插入到树的其他位置之后。 - reassemble():此回调是专门为了开发调试而提供的,在热重载(hot reload)时会被调用
- didUpdateWidget ():在 widget 重新构建时,Flutter 框架会调用widget.canUpdate来检测 widget 树中同一位置的新旧节点,然后决定是否需要更新,如果widget.canUpdate返回true则会调用此回调。
- deactivate():当 State 对象从树中被移除时,会调用此回调。
- dispose():当 State 对象从树中被永久移除时调用;通常在此回调中释放资源。
在 widget 树中获取State对象
两种方法在子 widget 树中获取父级 StatefulWidget 的State 对象。
- 通过Context获取
context对象有一个findAncestorStateOfType()方法,该方法可以从当前节点沿着 widget 树向上查找指定类型的 StatefulWidget 对应的 State 对象。
1 | class GetStateObjectRoute extends StatefulWidget { |
直接通过of静态方法来获取ScaffoldState
1 | Builder(builder: (context) { |
- 通过GlobalKey
给目标StatefulWidget添加GlobalKey。
1 | //定义一个globalKey, 由于GlobalKey要保持全局唯一性,我们使用静态变量存储 |
通过GlobalKey来获取State对象
1 | _globalKey.currentState.openDrawer() |
GlobalKey 是 Flutter 提供的一种在整个 App 中引用 element 的机制。
注意 :使用 GlobalKey 开销较大,如果有其他可选方案,应尽量避免使用它。另外,同一个 GlobalKey 在整个 widget 树中必须是唯一的,不能重复。
通过 RenderObject 自定义 Widget
1 | //如果组件不会包含子组件,则我们可以直接继承自 LeafRenderObjectWidget |
Flutter SDK内置组件库介绍
在基础组件库之上 Flutter 又提供了一套 Material 风格( Android 默认的视觉风格)和一套 Cupertino 风格(iOS视觉风格)的组件库。
1. 基础组件
Text (opens new window):该组件可让您创建一个带格式的文本。
Row (opens new window)、 Column (opens new window): 这些具有弹性空间的布局类 widget 可让您在水平(Row)和垂直(Column)方向上创建灵活的布局。其设计是基于 Web 开发中的 Flexbox 布局模型。
Stack (opens new window): 取代线性布局 (译者语:和 Android 中的FrameLayout相似),[Stack](https://docs.flutter.dev/flutter/ widgets/Stack-class.html)允许子 widget 堆叠, 你可以使用 Positioned (opens new window)来定位他们相对于Stack的上下左右四条边的位置。Stacks是基于Web开发中的绝对定位(absolute positioning )布局模型设计的。
Container (opens new window): Container (opens new window)可让您创建矩形视觉元素。Container 可以装饰一个BoxDecoration (opens new window), 如 background、一个边框、或者一个阴影。 Container (opens new window)也可以具有边距(margins)、填充(padding)和应用于其大小的约束(constraints)。另外, Container (opens new window)可以使用矩阵在三维空间中对其进行变换。