1、概念
Widget 是 UI 控件的基本抽象,它负责描述 UI 的一部分应该如何构建。每个Widget 都有一个对应的 RenderObject,负责实际的布局和绘制工作。Widget 不直接参与布局或绘制过程;它们只是描述了如何构建用户界面的一部分,并且可以被组合起来创建更复杂的 UI。
2、Widget接口
Wiget类本身是一个抽象类,最核心的部分是定义了
createElement()
接口,在Flutter实际开发中,我们使用StatelessWidget和StatefulWidget间接继承Widget来实现新组件,比如创建Flutter项目时生成的示例代码。
abstract class Widget extends DiagnosticableTree {
const Widget({required this.key});
final Key key;
Element createElement();
String toStringShort() {
return '$runtimeType';
}
void debugFillproperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
}
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType &&
oldWidget.key == newWidget.key;
}
}
-
DiagnosticableTree:诊断树,它在Flutter框架中主要用于生成诊断信息,帮助我们更好的理解程序的运行状态;
-
key:决定是否在下一次创建时复用旧的Widget,条件在
canUpdate()
中; -
createElement()
:Flutter Framework构建UI树时,优先调用此方法生成对应节点的Element对象,Flutter Framework在调用时为隐式调用; -
DiagnosticableTree 方法:
方法 作用 toString()
返回对象的字符串表示形式。 toStringShort()
返回对象的简短字符串表示形式。 toStringDeep()
返回对象及其子树的详细字符串表示形式。 toStringShallow()
返回对象本身的详细字符串表示形式。 toStringProperties()
返回对象的属性信息。 debugFillProperties()
填充诊断属性构建器,用于生成详细的诊断信息。 -
canUpdate()
是否使用旧的Widget对象更新旧UI树上对应Element配置,如果两个Widget的runtimeType和Key同时相等,则认为它们可以更新。
3、StatelessWidget
StatelessWidget继承自Widget类重写了
createElement()
方法:class Echo extends StatelessWidget { const Echo({super.key}); StatelessElement createElement() => StatelessElement(this); Widget build(BuildContext context) { // TODO: implement build throw UnimplementedError(); } }
- 显示文本
class Echo extends StatelessWidget {
final String text;
final Color backgroundColor;
const Echo(
{super.key,
required this.text,
this.backgroundColor = Colors.grey,
required String title});
StatelessElement createElement() => StatelessElement(this);
Widget build(BuildContext context) {
return Center(
child: Container(
color: backgroundColor,
child: Text(text),
),
);
}
}
- 调用
class _MyHomePageState extends State<MyHomePage> {
Widget build(BuildContext context) {
return const Echo(text: "示例文本", title: "StatelessWidget");
}
}
4、StatefulWidget
StatefulWidget和StatelessWidget一样也继承自Widget类 ,StatefulWidget也重写了
createElement()
方法,返回的Element对象不同;而且StatefulWidget类中添加了一个新的接口createState()
。
- StatefulWidget源码
abstract class StatefulWidget extends Widget {
const StatefulWidget({ super.key });
StatefulElement createElement() => StatefulElement(this);
State createState();
}
- StatefulElement:间接调用Element类,作为StatefulWidget的配置数据相对应,StatefulElement是Element的子类,它可以多次调用
createState()
来创建状态对象; createState()
:抽象方法,必须在具体的StatefulWidget子类中实现,用于创建Stateful Widget相关的状态,State对象负责维持Widget的状态,并提供一种机制触发Widget的重新构建。
5、State
State类是与StatefulWidget一起工作的核心部分之一,当创建一个createState方法,该方法返回一个State类的实例,这个State类负责管理Widget的状态,并提供了更新UI的方法。
State中保存的状态信息可以进行如下操作:
- Widget构建时可以被同步读取;
- Widget生命周期可以被改变,当State发生改变时,可以手动调用build重构Widget树,以达到更新UI的目的。
5.1 State生命周期
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
// return const CounterWidget();
return const Text("测试文本");
}
}
class CounterWidget extends StatefulWidget {
final int initValue;
const CounterWidget({super.key, required this.initValue});
State<CounterWidget> createState() => _CounterWidgetStates();
}
class _CounterWidgetStates extends State<CounterWidget> {
late int _counter;
bool _hotReloadTrigger = false;
void initState() {
super.initState();
_counter = widget.initValue;
if (kDebugMode) {
print("initState");
}
}
Widget build(BuildContext context) {
if (kDebugMode) {
print("build");
}
// 使用 _hotReloadTrigger 来触发构建变化
return Scaffold(
appBar: AppBar(
title: Text(_hotReloadTrigger ? 'Hot Reload Triggered' : 'Counter'),
),
body: Center(
child: ElevatedButton(
onPressed: () => setState(() => ++_counter),
child: Text("$_counter"),
),
),
);
}
void didUpdateWidget(covariant CounterWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (kDebugMode) {
print("didUpdateWidget");
}
}
void reassemble() {
super.reassemble();
if (kDebugMode) {
print("reassemble");
}
// 改变属性以触发 didUpdateWidget
setState(() {
_hotReloadTrigger = !_hotReloadTrigger;
// 创建新的 CounterWidget 实例以触发 didUpdateWidget
final newWidget = CounterWidget(initValue: widget.initValue);
if (newWidget != widget) {
if (kDebugMode) {
print("$newWidget");
}
}
});
}
void didChangeDependencies() {
super.didChangeDependencies();
if (kDebugMode) {
print("didChangeDependencies");
}
}
}
-
修改MyHomePage类,添加跳转按钮,控制台的输出日志如下:
I/flutter ( 5101): initState I/flutter ( 5101): didChangeDependencies I/flutter ( 5101): build
-
点击热重载,控制台的输出日志如下:
I/flutter ( 5101): reassemble I/flutter ( 5101): build
-
修改build,返回文本:
class MyHomePage extends StatelessWidget { const MyHomePage({super.key}); Widget build(BuildContext context) { // return const CounterWidget(); return const Text("测试文本"); } }
-
输出日志为
I/flutter ( 5101): reassemble I/flutter ( 5101): deactivate I/flutter ( 5101): dispose
-
5.2 回调函数说明
-
build()
:负责构建组件的实际UI,它在以下几种情况下调用:- 在调用
initState()
、didUpdateWidget()
、setState()
、didChangeDependencies()
这几种方法之后; - 在State对象从树中一个位置移除又插入到树的其他位置之后。
- 在调用
-
initState()
:当Widget第一次插入到树中调用,执行初始化工作的最佳时机,此方法只会调用一次,之后即使组件重建也不会再次调用; -
didChangeDependencies()
:当State对象的依赖发生变化时会被调用,例如当应用的主题或者语言发生变化,Flutter framework会通知Widget调用此回调; -
reassemble()
:为开发调试使用,点击热重载后会调用此方法; -
didUpdateWidget()
:检测Widget树某一节点是否需要更新,在新旧Widget的Key和runtimeType同时相等时就调用didUpdateWidget()
; -
deactivate()
:当State对象暂时从树中移除但未来可能重新插入的时候调用; -
dispose()
:当State对象永久从树中移除时调用,只调用一次,调用后该State对象不能再用于构建UI。
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » Widget结构(一)
发表评论 取消回复