Widget的概念
widget意为”小部件“,在Flutter中widget的地位和作用和原生应用中的控件是一样的。 但是有区别的地方是,Flutter中万物皆widget。
Widget接口
widget是一个不可变的接口,这是为了限制widget中的属性不可变,因为如果属性发现变化就会重新构建widget,就会创建新的widget实例来替换旧的widget实例。因此设置widget是可变的没有任何意义。 在实际使用中我们会间接继承widget接口的子类:StatelessWidget
和StatefulWidget
来实现新部件。
Flutter中的四棵树
https://juejin.cn/post/7044783155051495438 Widget只负责描述一个UI元素的信息,并不负责UI的布局和绘制。我们知道在原生控件中一个View需要经过测量、布局、绘制才能显示到屏幕上,那么Flutter中是如何实现的呢?
- 根据 Widget 树生成一个 Element 树,Element 树中的节点都继承自
Element
类。 - 根据 Element 树生成 Render 树(渲染树),渲染树中的节点都继承自
RenderObject
类。 - 根据渲染树生成 Layer 树,然后上屏显示,Layer 树中的节点都继承自
Layer
类。
StatelessWidget
无状态部件简介
StatelessWidget
无状态部件,它继承自Widget,我们需要实现新的控件可以继承StatelessWidget
,并会强制重写它的build()
方法
@override
Widget build(BuildContext context) { }
StatelessWidget
用于不需要维护状态的场景,它通常在build()
方法中通过嵌套其他 widget 来构建UI,在构建过程中会递归的构建其嵌套的 widget 。
Context
build方法中有一个参数是context,它是BuildContext类的一个实例,表示当前widget在widget树中的上下文,每一个widget都会对应一个context对象。(因为每一个widget都是widget树上的一个节点) 通过当前widget的上下文context可以向上遍历widget树查找父级widget的方法
class ContextRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Context测试"),
),
body: Container(
child: Builder(builder: (context) {
// 在 widget 树中向上查找最近的父级`Scaffold` widget
Scaffold scaffold = context.findAncestorWidgetOfExactType<Scaffold>();
// 直接返回 AppBar的title, 此处实际上是Text("Context测试")
return (scaffold.appBar as AppBar).title;
}),
),
);
}
}
StatefulWidget
和StatelessWidget
一样,StatefulWidget
也是继承自Widget
接口,并重写createElement()
方法。 createState() 用于创建和 StatefulWidget 相关的状态,它在StatefulWidget 的生命周期中可能会被多次调用。
State
一个StatefulWidget类会对应一个State类,State表示与其对应的 StatefulWidget 要维护的状态,State 中的保存的状态信息可以:
- 在 widget 构建时可以被同步读取。
- 在 widget 生命周期中可以被改变,当State被改变时,可以手动调用其setState()方法通知Flutter 框架状态发生改变,Flutter 框架在收到消息后,会重新调用其build方法重新构建 widget 树,从而达到更新UI的目的。
State中的两个常用属性:
- widget:它表示与该 State 实例关联的 widget 实例,由Flutter 框架动态设置。
- context:StatefulWidget对应的 BuildContext,widget的上下文。
State的生命周期
State的回调函数:
- initState:当widget第一次被插入到widget树时会被调用,对于每一个State对象,Flutter框架只会调用initState一次。通常在改会调用做一些初始化工作。
- didChangeDependencies:当State的状态依赖发生变化时会被调用。例如在AWidget中包含了一个BWidget,当BWidget发生了变化,那么AWidget的改回调就会被调用。
- build():用于构建widget子树。该方法会在以下场景被调用:
- 在调用initState()之后。
- 在调用didUpdateWidget()之后
- 在调用setState()之后。
- 在调用didChangeDependencies()之后。
- 在State对象从树中一个位置移除后(会调用deactivate)又重新插入到树的其他位置之后。
- reassemble():此回调是专门为了开发调试而提供的,在热重载(hot reload)时会被调用,此回调在Release模式下永远不会被调用。
- didUpdateWidget():在 widget 重新构建时,Flutter 框架会调用widget.canUpdate来检测 widget 树中同一位置的新旧节点,然后决定是否需要更新,如果widget.canUpdate返回true则会调用此回调。正如之前所述,widget.canUpdate会在新旧 widget 的 key 和 runtimeType 同时相等时会返回true,也就是说在在新旧 widget 的key和runtimeType同时相等时didUpdateWidget()就会被调用。
- deactivate():当 State 对象从树中被移除时,会调用此回调。
- dispose():当 State 对象从树中被永久移除时调用;通常在此回调中释放资源。