Flutter鸿蒙开发指南(二):组件类型与状态管理
我们上面1.1计数器的效果就是使用了StatelessWidget。如果你还不能够理解,那么我举个具体的案例帮助你理解。StatelessWidget就像一个静态的照片——拍完照片是什么样子,就永远是什么样子,不会自己变化。// 最简单的StatelessWidget示例@overridereturn const Text('欢迎来到鸿蒙Flutter开发!');让我们回顾一下刚刚的问题:无状态组
前言
前面学习了Flutter的组件化。这篇文章学习无状态组件和有状态组件的区别,为后面的项目开发巩固基础。前面的基础章节都打算采用Vscode进行编写,后续可能会改为Android Studio。
一、一个“失灵”的计数器
1.1 计数器效果
以下代码采用组件化的形式展示。这是一个计数器的案例:如果点击悬浮按钮的+号应该会进行自增,从99->100->101。但是点击完后发现并没有进行自增。为了弄清楚程序是否执行了自增,我们添加了这段代码在DevEco Studio的控制台进行查看。在DevEco Studio的log可以看见,程序确实已经执行了,但是UI为什么没有进行更新呢?这涉及到Flutter的两大组件特性:
无状态组件和有状态组件
无状态组件:StatelessWidget
有状态组件:StatefulWidget
print('今年$age岁');

1.2 计数器代码
main.dart代码:
import 'package:flutter/material.dart';
import 'package:wan_android/components/01.%E6%8A%BD%E7%A6%BB%E7%BB%84%E4%BB%B6.dart';
import 'package:wan_android/components/02.%E6%97%A0%E7%8A%B6%E6%80%81%E7%BB%84%E4%BB%B6.dart';
void main() {
runApp(MaterialApp(
home: MyApp2(age:99),
));
}
MyApp2.dart代码:
// ignore_for_file: must_be_immutable
import 'package:flutter/material.dart';
//快捷键:stless
class MyApp2 extends StatelessWidget {
MyApp2({super.key, this.age});
int? age;
final String name = '张三';
@override
Widget build(BuildContext context) {
return Scaffold(
//导航条
appBar: AppBar(
title: const Text('无状态组件',
style: TextStyle(color: Colors.white, fontSize: 18)),
backgroundColor: Colors.pink,
centerTitle: true,
),
//body一般放Container()
body: Center(
child: Text('我叫$name,今年$age岁', style: TextStyle(fontSize: 30)),
),
//floatingActionButton:浮动按钮
floatingActionButton: FloatingActionButton(
onPressed: () {
age = age! + 1;
print('今年$age岁');
},
child: const Icon(Icons.add),
),
);
}
}
二、无状态组件:StatelessWidget
2.1 什么是StatelessWidget?
我们上面1.1计数器的效果就是使用了StatelessWidget。如果你还不能够理解,那么我举个具体的案例帮助你理解。
一句话定义StatelessWidget:StatelessWidget就像一个静态的照片——拍完照片是什么样子,就永远是什么样子,不会自己变化。
// 最简单的StatelessWidget示例
class WelcomeText extends StatelessWidget {
const WelcomeText({super.key});
@override
Widget build(BuildContext context) {
return const Text('欢迎来到鸿蒙Flutter开发!');
}
}
2.2 三大核心特征
特征1:不可变性(所有属性必须是final)
// ✅ 正确写法:属性都是final
class UserCard extends StatelessWidget {
final String name; // final:不可变
final int age; // final:不可变
const UserCard({super.key, required this.name, required this.age});
@override
Widget build(BuildContext context) {
return Card(
child: Column(
children: [
Text('姓名: $name'),
Text('年龄: $age'),
],
),
);
}
}
// ❌ 错误写法:属性不是final
class BadUserCard extends StatelessWidget {
String name; // 不是final → 编译警告
int age; // 不是final → 编译警告
// 会看到提示:'name' should be final
特征2:纯函数式(相同的输入,相同的输出)
// 无论调用多少次,相同参数得到相同结果
UserCard(name: '张三', age: 20) // 总是显示"张三, 20岁"
UserCard(name: '张三', age: 20) // 再次调用,还是"张三, 20岁"
UserCard(name: '张三', age: 20) // 永远都是"张三, 20岁"
特征3:依赖外部数据(自己不保存状态)
// StatelessWidget就像餐厅的菜单
// 厨房(父组件)做什么菜,菜单就显示什么
class MenuItem extends StatelessWidget {
final String dishName; // 菜名来自厨房
final double price; // 价格来自厨房
final bool isAvailable; // 是否可点来自厨房
const MenuItem({
super.key,
required this.dishName,
required this.price,
required this.isAvailable,
});
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(dishName),
subtitle: Text('¥$price'),
enabled: isAvailable,
);
}
}
2.3 为什么刚才的计数器会“失灵”
让我们回头看计数器的问题代码:
class MyApp2 extends StatelessWidget {
MyApp2({super.key, this.age});
int? age; // ❌ 问题1:不是final
final String name = '张三';
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
age = age! + 1; // ❌ 问题2:试图修改属性
print('今年$age岁');
},
child: const Icon(Icons.add),
),
);
}
}
根本原因:
(1)StatelessWidget被设计的初衷就是不可变的。
(2)即使修改了age的值,Flutter也不会重新调用build()方法。
(3)没有build()的重新执行,UI就不会进行更新
打个比方:
就像你想更新一张纸质照片的内容。你在照片反面上写"age+1",但是照片正面的画面依旧不会改变,要想改变照片正面的画面,必须重新拍一张照片。(重新执行build()方法)
使用场景:没有状态改变的Widget,通常这种Widget仅仅是做一些展示工作,如发送网络请求,拿到数据,进行渲染。
那么上面计数器的UI不能被更新吗?答案是能的,使用有状态组件:StatefulWidget即可。
三、有状态组件:StatefulWidget
3.1 什么是StatefulWidget?
让我们回顾一下刚刚的问题:
无状态组件程序输出了,但是UI没进行更新,那么我要更新怎么更新?
答:使用StatefulWidget有组件状态进行更新。
StatelessWidget就像一张静态照片,拍完就固定了。
StatefulWidget则像一段动态视频,可以记录变化的过程。
// 需求:点击按钮,数字要实时更新
// ❌ StatelessWidget做不到:修改数据,UI不更新
// ✅ StatefulWidget能做到:修改数据,UI同步更新
接下来,让我们一起使用Statefulwidget来修正这个计数器。
3.2 setState():让UI动起来的魔法
main.dart代码:
import 'package:flutter/material.dart';
import 'package:wan_android/components/03.%E6%9C%89%E7%8A%B6%E6%80%81%E7%BB%84%E4%BB%B6.dart';
import
void main() {
runApp(MaterialApp(
home: MyApp3();
));
}
MyApp3.dart代码:
import 'package:flutter/material.dart';
//有状态组件:stful 命名遵循大驼峰命名法
class MyApp3 extends StatefulWidget {
const MyApp3({super.key});
@override
State<MyApp3> createState() => _MyApp3State();
}
//以下划线 _ 开头的类名、变量名或方法名,表示它是“库私有”的。
class _MyApp3State extends State<MyApp3> {
//状态变量(数据变化,视图会更新)
String name = '张三';
int age = 20;
@override
Widget build(BuildContext context) {
return Scaffold(
//导航条
appBar: AppBar(
title: const Text('无状态组件',
style: TextStyle(color: Colors.white, fontSize: 18)),
backgroundColor: Colors.pink,
centerTitle: true,
),
//body一般放Container()
body: Center(
child: Text('我叫$name,今年$age岁', style: TextStyle(fontSize: 30)),
),
//floatingActionButton:浮动按钮
floatingActionButton: FloatingActionButton(
onPressed: () {
age = age + 1;
print('今年$age岁');
},
child: const Icon(Icons.add),
),
);
}
}
运行效果:点击悬浮按钮的+号,你会发现UI依然没有进行更改。为什么呢?因为没有使用setState()方法包裹。
为什么需要setState()?因为我们需要一种方式告诉Flutter:“状态已经改变了,请根据新的状态重新构建UI。"SetState方法会标记该State对象为”脏“的,然后在下一帧中,Flutter会重新执行该State对象的build方法,从而更新UI。

使用SetState()方法包裹住"age = age + 1;修改MyApp3.dart代码:
setState(() {
age = age + 1;
});
点击+号,你会发现UI可以进行正常更新了。

3.3 setState()的作用
(1)更新状态变量的值
(2)标记这个Widget需要进行重建【执行build()方法】
(3)触发build()方法重新执行
(4)Flutter用新的状态重新绘制UI
四、结语
本次文章到此结束,感谢大家的观看,如果有错误或者更好的建议,可以在评论区指出。这篇文章带大家认识了无状态组件和有状态组件的区别。希望大家可以通过代码实践加以领悟,“纸上谈兵终觉浅”,还需要多敲代码,加深印象。
更多推荐





所有评论(0)