一. 介绍
Flutter Widget采用现代响应式框架构建,这是从 React 中获得的灵感,中心思想是用widget构建你的UI。 Widget描述了他们的视图在给定其当前配置和状态时应该看起来像什么。当widget的状态发生变化时,widget会重新构建UI,Flutter会对比前后变化的不同, 以确定底层渲染树从一个状态转换到下一个状态所需的最小更改(译者语:类似于React/Vue中虚拟DOM的diff算法)。
先来简单分析一个例子
import 'package:flutter/material.dart';
void main() {
runApp(
new Center(
child: new Text(
'Hello, world!',
textDirection: TextDirection.ltr,
),
),
);
}
在上面的代码里,好像并没有用到Widget类,但我们说了flutter渲染的ui是根据widget构建的,那Widget类型在哪呢?其实Center就是一个Widget类型,甚至里面的Tetx也是一个Widget类型。他们算是Widget的实例。可以在vscode按ctrl
点击Center,会发现class Center extends Align
,同理继续点击class Align extends SingleChildRenderObjectWidget
,abstract class SingleChildRenderObjectWidget extends RenderObjectWidget
,abstract class RenderObjectWidget extends Widget
一层层点击后发现终于见到了Widget类(当然上面还有)。widget的主要工作是提供一个build()方法来描述如何构建UI界面(通常是通过组合、拼装其它基础widget)
以上代码就表示该runApp
函数接受给定的Widget
并使其成为widget树的根(也就是Center实例)。 在此示例中,widget树由两个widget:Center(及其子widget)和Text组成。框架强制根widget覆盖整个屏幕,这意味着文本“Hello, world”会居中显示在屏幕上。
总结Widget
- Widget实际上就是Element的配置数据,Widget树实际上是一个配置树,而真正的UI渲染树是由Element构成;不过,由于Element是通过Widget生成的,所以它们之间有对应关系,在大多数场景,我们可以宽泛地认为Widget树就是指UI控件树或UI渲染树。
- 一个Widget对象可以对应多个Element对象。这很好理解,根据同一份配置(Widget),可以创建多个实例(Element)。
二.Widget主要接口
聊了这么久的Widget,让我们来看看这个类的具体构造吧。
abstract class Widget extends DiagnosticableTree {
/// Initializes [key] for subclasses.
const Widget({ this.key });
final Key? key;
@protected
@factory
Element createElement();
/// A short, textual description of this widget.
@override
String toStringShort() {
final String type = objectRuntimeType(this, 'Widget');
return key == null ? type : '$type-$key';
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
}
@override
@nonVirtual
bool operator ==(Object other) => super == other;
@override
@nonVirtual
int get hashCode => super.hashCode;
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
static int _debugConcreteSubtype(Widget widget) {
return widget is StatefulWidget ? 1 :
widget is StatelessWidget ? 2 :
0;
}
}
- 可以看出Widget继承于
DiagnosticableTree
,这个DiagnosticableTree就是"诊断树",主要作用是提供调试信息。 - 这里的
key
类似于react或vue里面的key,用来决定是否调用createElement
方法,主要在canUpdate
方法里面使用。 -
createElement()
:正如前文所述“一个Widget可以对应多个Element”;Flutter Framework在构建UI树时,会先调用此方法生成对应节点的Element对象。此方法是Flutter Framework隐式调用的,在我们开发过程中基本不会调用到。 -
debugFillProperties(...)
复写父类的方法,主要是设置诊断树的一些特性。 - canUpdate(...)是一个静态方法,它主要用于在Widget树重新build时复用旧的widget,其实具体来说,应该是:是否用新的Widget对象去更新旧UI树上所对应的Element对象的配置;通过其源码我们可以看到,只要newWidget与oldWidget的runtimeType和key同时相等时就会用newWidget去更新Element对象的配置,否则就会创建新的Element。
有无状态的widget
另外Widget类本身是一个抽象类,其中最核心的就是定义了createElement()接口,在Flutter开发中,我们一般都不用直接继承Widget类来实现一个新组件,相反,我们通常会通过继承StatelessWidget
或StatefulWidget
来间接继承Widget类来实现。StatelessWidget和StatefulWidget都是直接继承自Widget类,而这两个类也正是Flutter中非常重要的两个抽象类,它们引入了两种Widget模型,接下来我们将重点介绍一下这两个类。
将上文的代码自己封装(继承的StatelessWidget类是一个抽象类,需要复写build方法,该方法需要返回一个widget类型)
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Center(
child: new Text(
'Hello, world!',
textDirection: TextDirection.ltr,
),
);
}
}
基础 Widget
Flutter有一套丰富、强大的基础widget,其中以下是很常用的:
-
Text
:该 widget 可让创建一个带格式的文本。 -
Row
、Column
: 这些具有弹性空间的布局类Widget可让您在水平(Row)和垂直(Column)方向上创建灵活的布局。其设计是基于web开发中的Flexbox布局模型。 -
Stack
: 取代线性布局 (译者语:和Android中的LinearLayout相似),Stack
允许子 widget 堆叠, 你可以使用Positioned
来定位他们相对于Stack
的上下左右四条边的位置。Stacks是基于Web开发中的绝度定位(absolute positioning )布局模型设计的。 -
Container
:Container
可让您创建矩形视觉元素。container 可以装饰为一个BoxDecoration
, 如 background、一个边框、或者一个阴影。Container
也可以具有边距(margins)、填充(padding)和应用于其大小的约束(constraints)。另外,Container
可以使用矩阵在三维空间中对其进行变换。
网友评论