Widget的分类
1、有无状态分类
按照否是有状态的分类方式,widget可以分为无状态StatelessWidget和有状态StatefulWidget,StatelessWidget和StatefulWidget的Element都是ComponentElement,并且都不具备RenderObject。两者都是调用build方法,但是StatelessWidget只是实现简单的组件,而StatefulWidget则实现复杂的、有状态的组件,它的状态和数据都保存在State里面。
无状态组件
一个无状态部件在Flutter应用程序的运行期间不能改变其状态。这意味着无状态小组件在应用程序运行时不能被重新绘制,外观和属性在小组件的整个生命周期内保持不变。我们创建一个不需要反复重绘部件的应用程序时,我们会使用一个无状态的部件。
class StatelessElement extends StatelessWidget{
@override
Widget build(BuildContext context){
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Test Stateless'),
backgroundColor: Colors.blue,
),
body: Container(
// child: Widget(),
),
),
);
}
}
无状态组件覆盖了build 方法,build 方法接受BuildContext 作为参数,并返回一个 widget。在表面上看 每次数据更新都会执行build方法,但实际上,在组件树中只有StatelessElement在初始化的时候才会调用build方法。
有状态组件
在有状态类中,每次的数据发生变动,在组件树中只会调用state下的build方法进行重新渲染。这时候就能保存state中的状态。我们创建一个需要反复重绘部件的应用程序时,我们会使用一个有状态的部件。
//有状态组件的基本格式
class StatefulElement extends StatefulWidget{
@override
State<StatefulWidget> createState()=>_StatefulElementState();
}
class _StatefulElementState extends State<StatefulElement>{
@override
Widget build(BuildContext context) {
// TODO: implement build
throw UnimplementedError();
}
}
//完整的有状态组件
class StatefulElement extends StatefulWidget{
const StatefulElement({super.key});
@override
State<StatefulWidget> createState()=>_StatefulElementState();
}
class _StatefulElementState extends State<StatefulElement>{
int _counter = 0;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(
padding: const EdgeInsets.all(20),
child: Column(
children: <Widget>[
Text('$_counter'),
ElevatedButton(
onPressed: (){
setState(() {
_counter++;
});
}, child: const Text('按钮'),
)
],
),
),
),
);
}
}
setState() 是一个在有状态部件类中调用的方法,当每次执行setState()时,StatefulElement组件都会调用build方法进行将改变的数据进行重新渲染,以此来保证state中的属性值的保存。
State生命周期
StatefulWidget的内部逻辑与状态,由StatefulWidget的createState创建。
一个 StatefulWidget 类会对应一个 State 类,State表示与其对应的 StatefulWidget 要维护的状态,在State 中保存状态信息。

State方法概述
initState
描述:当 widget 第一次插入到 widget 树时会被调用,对于每一个State对象,Flutter 框架只会调用一次该回调,所以,通常在该回调中做一些一次性的操作,如状态初始化、订阅子树的事件通知等。
注意:不能在该方法中调用BuildContext.dependOnInheritedWidgetOfExactType,该方法用于在 widget 树上获取离当前 widget 最近的一个父级InheritedWidget。但是此方法被调用后会立即调用didChangeDependencies,在didChangeDependencies可以使用BuildContext.dependOnInheritedWidgetOfExactType。
didChangeDependencies()
描述:当此State对象的依赖项(InheritedWidget)更改时调用。在之前build() 中包含了一个InheritedWidget,然后在之后的build() 中Inherited widget发生了变化,那么此时InheritedWidget的子 widget 的didChangeDependencies()回调都会被调用。
build()
描述:StatefulElement通过build()返回的widget并通过调用updateChild来更新自己。
- 在调用
initState()后 - 在调用
didUpdateWidget()后。 - 在调用
setState()后 State对象的依赖(dependency)改变;比如:之前build时引用的InheritedWidget更改。- 调用了
deactivate后,然后将State对象重新插入树中的另一个位置
reassemble()
描述:此回调是专门为了开发调试而提供的,在热重载(hot reload)时会被调用,调用后build方法也将被调用,此回调在Release模式下永远不会被调用,无需在此方法中做任何操作。
didUpdateWidget ()
描述:State对象存在,并且符合Widget.canUpdate的情况下对StatefulWidget进行更新。didUpdateWidget方法最终会调用build方法,因此在此方法中调用setState是多余的。如果重写此方法,请确保调用super.didUpdateWidget(oldWidget)。
deactivate()
描述:当State对象从树中被移除时,会调用此回调。如果移除后没有重新插入到树中则紧接着会调用dispose()方法。
dispose()
描述:当 State 对象从树中被永久移除时调用;通常在此回调中释放资源。
//详细代码
class StatefulElement extends StatefulWidget{
const StatefulElement({super.key});
@override
State<StatefulWidget> createState()=>_StatefulElementState();
}
class _StatefulElementState extends State<StatefulElement>{
int _counter = 0;
@override
void initState(){
super.initState();
//初始化操作
//DO:
}
@override
void didChangeDependencies(){
super.didChangeDependencies();
//依赖发生更改时执行
//DO:
}
@override
void reassemble(){
super.reassemble();
//重新安装时执行,一般在调试的时候用,在发正式版本时 不会执行
}
@override
void didUpdateWidget(covariant StatefulElement oldWidget){
super.didUpdateWidget(oldWidget);
//组件发生变更时调用,当父组件有变动,子组件也会执行此方法
//DO:
}
@override
void deactivate(){
super.deactivate();
//停用
}
@override
void dispose(){
super.dispose();
//销毁
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(
padding: const EdgeInsets.all(20),
child: Column(
children: <Widget>[
Text('$_counter'),
ElevatedButton(
onPressed: (){
setState(() {
_counter++;
});
}, child: const Text('按钮'),
)
],
),
),
),
);
}
}
2、RenderObject分类
StatelessWidget 和 StatefulWidget 都是用于组合其他组件的,它们本身没有对应的 RenderObject,RenderObject的主要职责是布局和绘制,布局的原则是,Constraints向下,Sizes向上,父节点设置本节点的位置。
RenderObject Tree是底层的布局和绘制系统。大部分时候我们并不需要直接和RenderObject Tree交互,而是使用Widget,然后Flutter Framework会自动构建RenderObject Tree。

如上图所示,RenderObject主要分为四类:
RenderView
RenderView是整个RenderObject Tree的根节点,代表了整个输出界面。
RenderSliver
是所有实现了滑动效果的RenderObject基类,其常用子类有RenderSliverSingleBoxAdapter、RenderSliverToBoxAdapter等。关键参数是SliverConstraints和SliverGeometry,SliverConstraints和BoxConstraints对比,BoxContraints只包括了,最大/最小的高度/宽度。但是SliverConstraints则更多的是滑动方向、滑动偏移、滑动容器大小、容器缓存大小和位置等相关参数。
Size和SliverGeometry进行对比,Size只包括了宽和高。但是SliverGeometry包括了滑动方位、绘制范围、偏移等相关参数。
RenderAbstractViewport
RenderAbstractViewport是一类接口,此类接口为只展示其部分内容的RenderObject设计
RenderBox
RenderBox是一个采用2D笛卡尔坐标系的RenderObject的基类,一般的RenderOBject都是继承自RenderBox,例如RenderStack等,它也是一般自定义RenderObject的基类。
RenderBox会根据parent的constraints大小判断自己的布局方法,然后将constraints传递给child得到child的大小,最后根据child返回的Size决定自己的Size,如果没有child,就使用自己的Size。用于那些不涉及的滚动的控件布局,他的两个关键参数就是BoxConstraints和Size。
3、单元素与多元素分类
根据的child是否支持单个/多个child又可以分为SingleChildRenderObjectWidget和MultiChildRenderObjectWidget。
SingleChildRenderObjectWidget
单元素顾名思义就是只有一个Widget,SingleChildRenderObjectWidget单元素有经常使用的:Clip、Opacity、Padding、Align、SizededBox、ConstrainedBox、DecoratedBox等。SingleChildRenderObjectWidget继承RenderObjectWidget,绘制流程是通过RenderObject计算出自身的最大、最小宽高,并且通过performLayout综合得到child返回的Size、最后在进行绘制。
//定义
class CustomCenter extends SingleChildRenderObjectWidget{
//const CustomCenter({super.key});
const CustomCenter({Key? key, Widget? child}):super(key: key,child: child);
@override
RenderObject createRenderObject(BuildContext context) {
// TODO: implement createRenderObject
return RenderCustomCenter();
}
}
class RenderCustomCenter extends RenderShiftedBox{
}
MultiChildRenderObjectWidget
包含多个子Widget,一般都有一个children参数,接受一个Widget数组。如Row、Column、Stack、RichText等。
MultiChildRenderObjectWidget实现起来要复杂许多,主要复杂的部分在于RenderBox,我们需要自定义一个类继承于RenderBox,同时还得混入ContainerRenderObjectMixin和RenderBoxContainerDefaultsMixin,然后去重写他的两个方法:setupParentData和performLayout,然后在重写paint方法,调用系统绘制方法,完成绘制操作。这里就大概了解一下吧。
//定义
class UpDownBox extends MultiChildRenderObjectWidget{
UpDownBox({Key? key,required List<Widget> list}):assert(list.length==2,"只能传两个child"),super(key: key,children: list);
@override
RenderObject createRenderObject(BuildContext context) {
// TODO: implement createRenderObject
return RenderUpDownBox();
}
}
class UpDownParentData extends ContainerBoxParentData<RenderBox>{}
class RenderUpDownBox extends RenderBox{
}