flutter StatefulWidget 轻松了解flutter 有状态组件
flutter的状态组件有两种,分别是有状态组件StatefulWidget和无状态组件StatelessWidget。前一段时间,我只学习了无状态组件StatelessWidget,因为比较简单,直接布局就好了,但是只用无状态组件也有一个问题,就是无法交互和变化,不能实现复杂的功能。所以我决定开始进入下一步的学习,不能一直困在原地不动。
更新 flutter SDK
因为一些原因,很久没使用flutter了,现在已经有了更新,那么先更新一下flutter版本吧。更新也很简单,命令是:flutter upgrade
,按下回车就可以自动更新了。由3.3.2更新到3.3.6.
flutter Widget 组件(控件)
Flutter 从 React 中吸取灵感,通过现代化框架创建出精美的组件。它的核心思想是用 widget 来构建你的 UI 界面。 Widget 描述了在当前的配置和状态下视图所应该呈现的样子。当 widget 的状态改变时,它会重新构建其描述(展示的 UI),框架则会对比前后变化的不同,以确定底层渲染树从一个状态转换到下一个状态所需的最小更改。在写应用的过程中,取决于是否需要管理状态,你通常会创建一个新的组件继承 StatelessWidget 或 StatefulWidget。Widget类本身是一个抽象类,其中最核心的就是定义了createElement()接口,在 Flutter 开发中,我们一般都不用直接继承Widget类来实现一个新组件,相反,我们通常会通过继承StatelessWidget或StatefulWidget来间接继承widget类来实现。StatelessWidget和StatefulWidget都是直接继承自Widget类,而这两个类也正是 Flutter 中非常重要的两个抽象类。如果用户交互或数据改变导致widget改变,那么它就是有状态的。如果一个widget是最终的或不可变的,那么它就是无状态。
flutter StatelessWidget 无状态组件
StatelessWidget和StatefulWidget都是直接继承自Widget类,无状态组件在执行过程中只有一个 build 阶段,在执行期间只会执行一个 build 函数,所以在执行速度和效率方面比有状态组件更好。所以在设计组件时尽量使用无状态组件实现,能尽可能的节省计算机资源。StatelessWidget继承自widget类,重写了createElement()方法,StatelessWidget用于不需要维护状态的场景,它通常在build方法中通过嵌套其他 widget 来构建UI,在构建过程中会递归的构建其嵌套的 widget。下边是一个StatelessWidget无状态组件的例子:
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false, //去掉右上角debug标识
theme: ThemeData(
//主题设置
primarySwatch: Colors.blue,
),
home: const SelectionArea(
//子组件支持文字选定 3.3新特性
child: Scaffold(
//子组件
body: MyAppbody(),
),
),
);
}
}
flutter StatefulWidget 有状态组件
在Flutter默认程序中的计数器中,点击了+号按钮后,显示的数字需要+1,还有一些情况,我们会进行下拉刷新、上拉加载更多,这时数据就会发生变化,而StatelessWidget通常用来展示哪些数据固定不变的,如果数据会发生改变,我们使用StatefulWidget。StatefulWidget也是继承自widget类,并重写了createElement()方法,不同的是返回的Element 对象并不相同;另外StatefulWidget类中添加了一个新的接口createState()。
abstract class StatefulWidget extends Widget {
const StatefulWidget({ Key key }) : super(key: key);
@override
StatefulElement createElement() => StatefulElement(this);
@protected
State createState();
}
StatefulElement 间接继承自Element类,与StatefulWidget相对应(作为其配置数据)。StatefulElement中可能会多次调用createState()来创建状态(State)对象。createState() 用于创建和 StatefulWidget 相关的状态,它在StatefulWidget 的生命周期中可能会被多次调用。例如,当一个 StatefulWidget 同时插入到 widget 树的多个位置时,Flutter 框架就会调用该方法为每一个位置生成一个独立的State实例,其实,本质上就是一个StatefulElement对应一个State实例。
initState
initState是初始化方法,当 widget 第一次插入到 widget 树时会被调用,对于每一个State对象,Flutter 框架只会调用一次该回调,所以,通常在该回调中做一些一次性的操作,如状态初始化、订阅子树的事件通知等。不能在该回调中调用。
didChangeDependencies()
当State对象的依赖发生变化时会被调用;例如:在之前build() 中包含了一个InheritedWidget (第七章介绍),然后在之后的build() 中Inherited widget发生了变化,那么此时InheritedWidget的子 widget 的didChangeDependencies()回调都会被调用。
didUpdateWidget ()
在 widget 重新构建时,Flutter 框架会调用widget.canUpdate来检测 widget 树中同一位置的新旧节点,然后决定是否需要更新,如果widget.canUpdate返回true则会调用此回调。
deactivate()
当 State 对象从树中被移除时,会调用此回调。在一些场景下,Flutter 框架会将 State 对象重新插到树中,如包含此 State 对象的子树在树的一个位置移动到另一个位置时(可以通过GlobalKey 来实现)。如果移除后没有重新插入到树中则紧接着会调用dispose()方法。
dispose()
当 State 对象从树中被永久移除时调用;通常在此回调中释放资源。
flutter StatefulWidget 使用例子
创建项目
接下来就试着写一些StatefulWidget。静态展示的页面我们使用StatelessWidget无状态组件,页面不需要变化,如果我们想改变页面中的数据的话这个时候就需要用到 StatefulWidget。我们先创建一个项目,输入创建项目命令flutter create stateful
,打开我们的安卓虚拟机并执行代码flutter run
。简化一下初始代码。
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Homepage(),
);
}
}
class Homepage extends StatelessWidget {
const Homepage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Text"),
),
body: const Text("test"),
);
}
}
vscode快速生成StatefulWidget组件
我们可以使用快捷键快速生成StatefulWidget组件,在vscode输入state
快速生成,会生成两个类,一个继承自StatefulWidget的类,里面需要实现createState方法另一个继承自State,里面实现build方法,并且可以定义一些成员变量。
点击按钮变化界面示例实现
我们先实现一个背景图片切换例子,点击按钮后背景图片跟随变化。怎么实现还没想好,那就先实现一个按钮吧。使用MaterialButton组件实现一个简单的按钮。
class Testwight extends StatefulWidget {
const Testwight({super.key});
@override
State<Testwight> createState() => _TestwightState();
}
class _TestwightState extends State<Testwight> {
@override
Widget build(BuildContext context) {
return MaterialButton(
color: Colors.blue,
textColor: Colors.white,
child: const Text('点我'),
onPressed: () {
// ...
},
);
}
}
下边的实现都是在有状态组件下实现的,我们首先定义两个全局变量mg1、mg1用来放图片地址,接着在_TestwightState里面先设置一个局部变量pngpath存放当前文件路径。布局我们外层使用Column,竖直布局,里面放置一个Image.asset来显示图片,在放置一个MaterialButton实现点击切换图片功能。
class Testwight extends StatefulWidget {
const Testwight({super.key});
@override
State<Testwight> createState() => _TestwightState();
}
String mg1 = "images/1.png";//图片地址 全局变量
String mg2 = "images/1.jpg";//图片地址 全局变量
class _TestwightState extends State<Testwight> {
String pngpath = mg1;//图片地址 局部变量
@override
Widget build(BuildContext context) {
return Column(
children: [
Image.asset(pngpath),//图片组件
MaterialButton(//按钮组件
color: Colors.blue,
textColor: Colors.white,
child: const Text('点我'),
onPressed: () {
setState(() {//点击动作,调用setState刷新页面
pngpath = pngpath == mg1 ? mg2 : mg1;//实现图片切换
});
// ...
},
)
],
);
}
}
在MaterialButton组件中的onPressed监控点击事件,每次点击就调用setState函数,我们要改变有状态组件的状态,就要通过调用setState函数来实现,setState会在UI中重建此widget。上边也提到了一些其他函数,在合适的时机选择对应的函数就行了。(我不会截取动图,哪个大神教教我呗)
总结
对flutter 的 StatefulWidget组件浅尝辄止的学习了一下,只学到了一点皮毛,还有很多特性还不了解,更不能灵活的使用,后续将会结合实际使用进行进一步的学习,今天算是入门了。