本篇依据Flutter 3.16.4,Dart 3.2.3版别
Flutter 3.16.4 • channel stable • github.com/flutter/flu…
Framework • revision 2e9cb0aa71 (3 days ago) • 2023-12-11 14:35:13 -0700
Engine • revision 54a7145303
Tools • Dart 3.2.3 • DevTools 2.28.4
本篇为Flutter基建的第七篇文章,主要介绍Flutter中StatelessWidget和StatefulWidget运用以及根底类Widget相关常识,希望本篇文章能够帮助咱们了解和熟悉Widget的基本常识,下面一同进入文章的内容吧~
Flutter系列文章
Flutter基建 – 寸步不离的State**Widget
Widget基类
在这之前假如你观察过StatelessWidget和StatefulWidget的源码就会发现,其实它俩都是承继自Widget抽象类。
abstract class StatelessWidget extends Widget
abstract class StatefulWidget extends Widget
二者的差异在于StatelessWidget表明的是一个无状况的Widget,它不能够更新Widget内部状况,而StatefulWidget能够经过setState
来更新Widget的状况,这儿暂时了解这些基本概念,先进入Widget内部看看。
@immutable
abstract class Widget extends DiagnosticableTree {
/// Initializes [key] for subclasses.
const Widget({ this.key });
final Key? key;
@protected
@factory
Element createElement();
@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;
}
}
Widget抽象类的源码很简略,它承继自DiagnosticableTree,此Tree仅仅用于调试所用,这儿就不过多介绍,仍是介绍Widget内部的几个办法。
- createElement()办法用于创立Widget对应的Element目标,Flutter会依据详细的Element生成Render树,Render树便是烘托界面的元素;一个Widget对应一个Element,StatelessWidget创立的是StatelessElement目标,StatefulWidget创立的是StatefulElement目标,关于Widget、Element和Render三者的联系比较杂乱,后面会写一篇文章详细介绍它们三个的联系和效果。
- toStringShort()办法回来的是当时Widget的简略的信息。
- debugFillProperties()办法用于添加一些调试属性,一般情况下很少用到。
- canUpdate()办法效果仍是比较重要的,它用于判别重建时Widget是否需求更新,默认是比照两个Widget的runtimeType和key是否相同。
Widget源码不杂乱,详细杂乱的是Widget和Element、Render的联系,这儿先暂时了解这么多,更深化的常识咱们后续渐渐了解。
StatelessWidget
根底源码
abstract class StatelessWidget extends Widget {
const StatelessWidget({ super.key });
@override
StatelessElement createElement() => StatelessElement(this);
@protected
Widget build(BuildContext context);
}
StatelessWidget源码中完成了父类的createElement()办法,回来了一个StatelessElement目标,而且将本身传入StatelessElement结构办法傍边,然后界说了一个build()办法,用于创立详细的子Widget元素。
接下来咱们再看看怎么运用StatelessWidget来展现界面UI:
实际运用
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({super.key});
@override
Widget build(BuildContext context) {
return buildScaffold(
context,
const Center(
child: Text('MyStatelessWidget'),
),
);
}
}
运用StatelessWidget时,咱们只需求复写build()办法即可,然后在此办法内部将界面Widget回来,这样就能够完成一个无状况的Widget。
假如咱们当时的页面需求信息的更新,那么StatelessWidget就不再适用,此刻咱们需求选择StatefulWidget来完成界面元素的结构,下面咱们接着看看有状况的Widget是怎么运用。
StatefulWidget
根底源码
abstract class StatefulWidget extends Widget {
const StatefulWidget({ super.key });
@override
StatefulElement createElement() => StatefulElement(this);
@protected
@factory
State createState();
}
StatefulWidget源码中也是完成了父类的createElement()办法,回来了一个StatefulElement目标,而且将本身传入StatefulElement结构办法傍边,和StatelessWidget不同的是,它并没有界说build()办法用于创立界面Widget,而是界说了一个createState()办法,用于创立一个State目标,此目标便是用于保存当时Widget中相关的状况信息,更新状况信息也是经过State来完成。
了解了StatefulWidget根底源码之后,咱们紧接着来看看怎么运用StatefulWidget和State展现一个可修改状况信息的Widget。
实际运用
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
@override
State<StatefulWidget> createState() {
return _MyState();
}
}
class _MyState extends State<MyStatefulWidget> {
int count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter组件'),
),
body: Center(
child: Text('count: $count'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
++count;
});
},
child: const Icon(Icons.favorite),
),
);
}
}
经过上述的代码能够看到StatefulWidget中逻辑变得很少了,仅仅复写了createState()办法,回来了_MyState目标而已,详细的子Widget怎么创立,Widget的状况信息都下放到_MyState傍边了,_MyState承继自State类,然后复写了build()办法,创立界面Widget,而且在内部还界说了一个count变量,用于中心Text显现的文本数据,然后在FloatingActionButton的点击事情中经过setState()办法更新count值,随之Text文本也会跟随着count改动而及时更新。
StatefulWidget能够依靠State目标对内部的状况信息做出更新操作,这也是它和StatelessWidget最大的不同,假如你开发的界面需求界面更新,那么选择StatefulWidget来编写界面是名副其实的。
下面咱们再来看看怎么经过State感知当时Widget的生命周期改动。
生命周期
class _MyState extends State<MyStatefulWidget> {
int count = 0;
@override
Widget build(BuildContext context) {
debugPrint('build');
return Scaffold(
appBar: AppBar(
title: const Text('Flutter组件'),
),
body: Center(
child: Text('count: $count'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
++count;
});
},
child: const Icon(Icons.favorite),
),
);
}
@override
void initState() {
super.initState();
debugPrint('initState');
}
@override
void didUpdateWidget(covariant MyStatefulWidget oldWidget) {
super.didUpdateWidget(oldWidget);
debugPrint('didUpdateWidget');
}
@override
void deactivate() {
super.deactivate();
debugPrint('deactivate');
}
@override
void activate() {
super.activate();
debugPrint('activate');
}
@override
void reassemble() {
super.reassemble();
debugPrint('reassemble');
}
@override
void dispose() {
super.dispose();
debugPrint('dispose');
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
debugPrint('didChangeDependencies');
}
}
上述代码仅仅简单的在各个生命周期中加了debug信息的打印,下面咱们逐一来看下每个生命周期是怎么调用的。
当界面第一次被翻开时:
initState
didChangeDependencies
build
咱们会发现当界面第一次被翻开时,生命周期的改动是initStaste -> didChangeDependencies -> build
当界面退出时:
deactivate
dispose
界面从翻开状况变为退出状况时,生命周期的改动是deactivate -> dispose
当咱们点击Android Studio的hot reload时:
reassemble
build
当咱们点击FloatingActionButton将count值改动时:
build
此刻之后调用build()办法进行界面的重绘。
分析到这停止,咱们能够大致将State的生命周期给梳理成一个框架:
上面这个流程图就将State的从进入界面到退出界面,中心包括hot reload动作和initState动作的生命周期就完整的表述出来了,假如咱们有疑问不了解的当地,欢迎在评论区沟通交流
不知道看到这儿之后,咱们有没有发现,activate和didUpdateWidget这两个办法如同自始至终都没有被调用过,莫非它们二者不会在生命周期的过程中出现出来么?不会的,仅仅它们两个比较特殊,有对应的触发条件,下面咱们一个一个来解释下。
didUpdateWidget表明每当Widget装备改动时都会被调用,那么咱们是否有种疑问,上面调用setState办法时count不是现已改动了么,为啥没有被调用呢,didUpdateWidget调用机遇是父Widget的装备改动需求重建时,子Widget会调用此didUpdateWidget办法进行重建,接下来咱们经过代码验证下。
咱们将_MyState中原先Center中Text去掉,换成MyFulWidgetChild()组件,此组件中复写didUpdateWidget办法。
class _ChildState extends State<MyFulWidgetChild> {
@override
Widget build(BuildContext context) {
return const Text('Child');
}
@override
void didUpdateWidget(covariant MyFulWidgetChild oldWidget) {
super.didUpdateWidget(oldWidget);
debugPrint('child didUpdateWidget');
}
}
此刻咱们点击_State中FloatingActionButton改动count值时,_ChildState的didUpdateWidget办法就会被调用了,看到这咱们应该就会理解了此生命周期的触发的详细条件了。
还剩最终一个activate生命周期,此生命周期依据注释能够得出它的触发条件是:当Widget经过deactivate被移出界面时同时又被加入到另外一个界面它才会被调用,此生命周期日常开发中基本不会运用。
写在最终
本篇文章简单概括性的介绍了Widget和StatelessWidget、StatefulWidget的相关常识,希望能够帮助咱们了进一步了解和熟悉StatelessWidget和StatefulWidget的相关常识,后续会按部就班逐渐触摸Flutter更多的常识。
我是Taonce,假如觉得本文对你有所帮助,帮忙关注、点赞或者收藏三连一下,谢谢~