Widget 是什么
在 Flutter 中,Widget
是 UI 的配置(configuration)。
它本身 不是真正的显示元素(不是按钮、不是图片、不是文字)。
它只是描述了界面长什么样子、需要哪些参数。
真正显示在屏幕上的东西是
Element
(Flutter 内部类)和底层的 RenderObject。
Widget
就像 蓝图,Element
就像 工地上的工人,RenderObject
就是最终建好的 房子。
在 Flutter SDK 里的源码如下(省略):
@immutable
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });
final Key? key;
@protected
Element createElement();
}
@immutable
:Widget 必须是不可变的(Immutable)。意思是:你不能直接修改 Widget 的属性,只能通过创建一个新的 Widget 来替换。key
:用来标识 Widget,帮助框架在重建时决定是复用旧的 Element,还是销毁重建。createElement()
:每个 Widget 会创建一个对应的Element
,用来挂到 Flutter 的 Element Tree 上。举例说明:
Text("Hello", style: TextStyle(fontSize: 20))
Text
Widget 只是一个配置:要显示字符串"Hello"
,字体大小 20。- Flutter 内部会调用
Text.createElement()
,创建一个Element
节点,挂到 Element 树上。 Element
会再创建对应的RenderObject
,它才是真正在屏幕上绘制文字的对象。
所有 Flutter UI 都是由 Widget
构成的,常见有三大类:
StatelessWidget:没有内部状态,完全由构造函数参数决定。例如:
Text("Hello")
、Icon(Icons.add)
StatefulWidget:有内部可变状态,需要搭配一个
State
来维护。例如:Checkbox
、TextField
、你写的ImageWidget
InheritedWidget:用于在 Widget 树中向下传递数据,常用于全局状态共享(比如
Theme.of(context)
)。
State类中的widget
import 'package:cached_network_image/cached_network_image.dart';
import 'package:ducafe_ui_core/ducafe_ui_core.dart';
import 'package:flutter/material.dart';
import '../index.dart';
import 'package:flutter_svg/svg.dart';
/// 图片类型
enum ImageWidgetType {
img,
svg,
svgRaw,
}
/// 图片组件
class ImageWidget extends StatefulWidget {
const ImageWidget({
super.key,
required this.path,
required this.type,
this.radius,
this.width,
this.height,
this.fit,
this.placeholder,
this.errorWidget,
this.elevation,
this.color,
});
/// 文件路径
final String path;
/// 类型
final ImageWidgetType type;
/// 圆角
final double? radius;
/// 宽度
final double? width;
/// 高度
final double? height;
/// 自适应方式
final BoxFit? fit;
/// 占位图
final Widget? placeholder;
/// 错误图
final Widget? errorWidget;
/// 阴影
final double? elevation;
/// 颜色
final Color? color;
const ImageWidget.img(
this.path, {
super.key,
this.radius,
this.width,
this.height,
this.fit,
this.placeholder,
this.errorWidget,
this.elevation,
this.color,
}) : type = ImageWidgetType.img;
@override
State<ImageWidget> createState() => _ImageWidgetState();
}
class _ImageWidgetState extends State<ImageWidget> {
Widget _buildView() {
Widget ws = widget.placeholder ?? const SizedBox();
// 是否是网络图片
bool isNetwork = widget.path.startsWith('http') ||
widget.path.startsWith('https') ||
widget.path.startsWith('//');
// 1 图片
// asset 图片
if (widget.type == ImageWidgetType.img && !isNetwork) {
ws = Image.asset(
widget.path,
fit: widget.fit,
color: widget.color,
);
}
// 网络图片
else if (widget.type == ImageWidgetType.img && isNetwork) {
ws = CachedNetworkImage(
imageUrl: widget.path,
fit: widget.fit,
cacheKey: widget.path.hashCode.toString(),
color: widget.color,
placeholder: (context, url) =>
widget.placeholder ??
const CircularProgressIndicator()
.tightSize(AppSize.indicator)
.center(),
errorWidget: (context, url, error) =>
widget.errorWidget ?? const Icon(Icons.error),
);
}
return ws;
}
@override
Widget build(BuildContext context) {
return _buildView();
}
}
是什么
abstract class State<T extends StatefulWidget> with Diagnosticable {
T get widget => _widget!;
T? _widget;
}
widget
是一个 getter,返回_widget
。_widget
在框架内部由 Flutter 赋值,它保存着当前和State
绑定的 StatefulWidget 实例。换句话说:
widget
就是你在外面创建的StatefulWidget
对象。
class ImageWidget extends StatefulWidget {
//----------------------中间省略代码--------------------------
@override
State<ImageWidget> createState() => _ImageWidgetState();
}
class _ImageWidgetState extends State<ImageWidget> {
//----------------------中间省略代码--------------------------
@override
Widget build(BuildContext context) {
return _buildView();
}
}
Flutter 框架规定:每个
StatefulWidget
必须实现createState()
方法。比如上面的案例:ImageWidget
创建出来时,Flutter 框架会调用createState()
来生成一个_ImageWidgetState
实例。之后,Flutter 会把这个State
和Widget
绑定在一起。在
_ImageWidgetState
里,widget
指向的就是这个ImageWidget
实例。ImageWidget
实例里面有什么属性和方法,widget
也会拥有同样的属性和方法。// 如果 ImageWidget 创建了一个这样的实例 ImageWidget( path: "http://xxx.png", radius: 8, ) // 如果我 _ImageWidgetState 里面使用 widget.xxx,其实就是在访问xxx的属性 widget.path // "http://xxx.png" widget.radius // 8
widget
的生命周期:Flutter 框架会自动维护 widget
的赋值:
第一次创建:调用
createState()
时,Flutter 会把ImageWidget
实例赋给_ImageWidgetState._widget
。热重建 / 父组件更新时:Flutter 会调用
didUpdateWidget(oldWidget)
,然后更新_widget
的值。这样State
始终持有最新的widget
。State
是持久的(不会轻易销毁),但widget
会不断被新实例替换。