Flutter状态管理:从零开始写一个记账小产品
大家好,我是正在读研的 Flutter 爱好者。最近很多刚入门的同学问我:“为什么我的界面点一下没反应?”、“数据变了但页面不刷新怎么办?”——这其实都指向同一个问题:状态管理。
我当初学 Flutter 的时候,也卡在状态管理上整整一周。翻遍了官方文档、GitHub 项目,甚至买了三本讲 Flutter 的书籍,才慢慢理清思路。今天我就用最实践的方式,带你从零搭建一个「简易记账本」App,边做边学状态管理的核心逻辑。
📌 小提示:本文不需要你懂区块链或 SpringBoot,但我会在适当位置解释它们和 Flutter 的关系(毕竟题目要求嘛 😄)。
为什么需要状态管理?
想象你在做一个记账 App:
- 用户点击“+”按钮添加一笔支出
- 列表要立刻更新显示新记录
- 总金额要重新计算
如果没有状态管理,你可能会这样写:
class BadExample extends StatefulWidget {
@override
_BadExampleState createState() => _ BADExampleState();
}
class _BadExampleState extends State<BadExample> {
int total = 0;
void addExpense(int amount) {
total += amount;
// 但页面不会自动刷新!
}
@override
Widget build(BuildContext context) {
return Text("总支出: $total");
}
}
问题来了:total 变了,但界面没变!
因为 Flutter 不知道这个变量和 UI 有关联。
状态管理就是解决这个问题的机制:当数据变化时,自动通知 UI 重新绘制。
开发环境准备(5分钟搞定)
步骤 1:安装 Flutter SDK
- 去 flutter.dev 下载 SDK
- 解压到
C:\flutter(Windows)或~/flutter(Mac) - 配置环境变量:把
flutter/bin加入 PATH
步骤 2:验证安装
终端运行:
flutter doctor
如果看到绿色对勾 ✅,说明环境 OK!
步骤 3:创建新项目
flutter create expense_tracker
cd expense_tracker
步骤 4:选择状态管理方案
Flutter 有多种状态管理方式,新手推荐 Provider(简单、官方推荐、适合中小项目)。
在 pubspec.yaml 中添加依赖:
dependencies:
flutter:
sdk: flutter
provider: ^6.1.1 # 添加这一行
然后运行:
flutter pub get
核心概念:什么是“状态”?什么是“提供者”?
用最通俗的话解释:
| 概念 | 说明 | 类比 |
|---|---|---|
| 状态(State) | 应用中会变化的数据 | 记账本里的“余额” |
| 提供者(Provider) | 把状态“共享”给多个页面的工具 | 公司财务系统,所有部门都能查账 |
| 消费者(Consumer) | 使用状态的 UI 组件 | 老板看报表 |
💡 我当初混淆了
StatefulWidget和状态管理。记住:StatefulWidget是组件内部状态,而 状态管理是跨组件共享状态!
实战:构建记账小产品
我们要做一个功能简单的 App:
- 显示总支出
- 列出所有支出记录
- 点击“+”按钮添加随机支出
第一步:定义数据模型
创建 lib/models/expense.dart:
class Expense {
final String id;
final String description;
final double amount;
Expense({
required this.id,
required this.description,
required this.amount,
});
}
第二步:创建状态管理类(关键!)
新建 lib/providers/expense_provider.dart:
import 'package:flutter/foundation.dart';
import '../models/expense.dart';
class ExpenseProvider with ChangeNotifier {
List<Expense> _expenses = [];
// 提供只读访问
List<Expense> get expenses => [..._expenses];
double get totalAmount {
return _expenses.fold(0, (sum, expense) => sum + expense.amount);
}
// 添加支出
void addExpense(String desc, double amount) {
final newExpense = Expense(
id: DateTime.now().toString(),
description: desc,
amount: amount,
);
_expenses.add(newExpense);
notifyListeners(); // 通知所有监听者:数据变了!
}
}
🔑 重点:
ChangeNotifier是 Flutter 内置的观察者模式实现。调用notifyListeners()后,所有用Consumer包裹的 UI 都会重建。
第三步:在应用顶层注入 Provider
修改 lib/main.dart:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'providers/expense_provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => ExpenseProvider(),
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '记账小产品',
theme: ThemeData(primarySwatch: Colors.blue),
home: const ExpenseScreen(),
);
}
}
第四步:构建主界面
创建 lib/screens/expense_screen.dart:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/expense_provider.dart';
import 'dart:math';
class ExpenseScreen extends StatelessWidget {
const ExpenseScreen({super.key});
@override
Widget build(BuildContext context) {
final expenseProvider = Provider.of<ExpenseProvider>(context);
return Scaffold(
appBar: AppBar(title: const Text('我的记账本')),
body: Column(
children: [
// 显示总金额
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'总支出: ¥${expenseProvider.totalAmount.toStringAsFixed(2)}',
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
// 支出列表
Expanded(
child: ListView.builder(
itemCount: expenseProvider.expenses.length,
itemBuilder: (context, index) {
final expense = expenseProvider.expenses[index];
return ListTile(
title: Text(expense.description),
trailing: Text('¥${expense.amount.toStringAsFixed(2)}'),
);
},
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 随机添加一笔支出
final descriptions = ['午餐', '打车', '买书', '咖啡'];
final random = Random();
final desc = descriptions[random.nextInt(descriptions.length)];
final amount = 10.0 + random.nextInt(200);
// 通过 Provider 添加数据
expenseProvider.addExpense(desc, amount);
},
child: const Icon(Icons.add),
),
);
}
}
第五步:运行看看!
flutter run
点击右下角 “+” 按钮,你会发现:
- 列表自动新增一条记录
- 总金额实时更新
这就是状态管理的力量!
常见问题解答(避坑指南)
❓ 问题1:为什么不用 setState?
setState 只能更新当前组件的状态。如果你有多个页面(比如首页和详情页)都要显示总金额,用 setState 就得层层传递回调函数,代码会非常混乱。
我当初尝试用
setState做多页面同步,结果写了 200 行回调,最后崩溃重写 😭
❓ 问题2:Provider 和 GetX / Riverpod 有什么区别?
| 方案 | 优点 | 适合场景 |
|---|---|---|
| Provider | 官方支持、学习曲线平缓 | 中小型项目、新手入门 |
| GetX | 功能强大、代码量少 | 快速开发、个人项目 |
| Riverpod | 类型安全、无 context 依赖 | 大型项目、追求稳定性 |
建议新手从 Provider 开始,等熟悉后再尝试其他方案。
❓ 问题3:区块链和 SpringBoot 在这里有什么用?
- 区块链:如果你的记账 App 需要不可篡改的交易记录(比如做去中心化金融产品),可以把支出哈希存到区块链。但普通记账完全不需要!
- SpringBoot:这是后端框架。如果你的 App 需要用户登录、云端同步数据,就需要 SpringBoot 写 API。但状态管理只负责前端数据流,和后端无关。
📚 推荐书籍:《Flutter实战》(第二版)第7章详细讲解了各种状态管理方案,比网上碎片化教程系统得多。
学习路径建议
先掌握 Provider 基础
- 理解
ChangeNotifier+Consumer模式 - 练习:做一个待办事项 App
- 理解
进阶:处理异步状态
比如从网络加载数据:Future<void> loadExpenses() async { final data = await http.get(...); _expenses = parse(data); notifyListeners(); }探索其他方案
- 当项目变大,试试 Riverpod(更安全)
- 如果喜欢响应式编程,学 Bloc
结合后端
用 SpringBoot 写个 REST API,让 Flutter App 能同步数据到服务器。这时状态管理就和后端协作了!
结语:产品思维比技术更重要
我见过太多同学沉迷于“哪个状态管理库最好”,却忽略了用户真正需要什么。
你做的记账 App:
- 是给大学生记食堂开销?
- 还是给家庭主妇管日常账单?
明确产品目标,再选合适的技术。Provider 对 90% 的 Flutter 产品已经足够。
最后送大家一句话:“状态管理不是炫技,而是让数据流动起来服务于用户。”
希望这篇实践教程能帮你跨过 Flutter 的第一道坎。如果你跟着做出了自己的记账本,欢迎在评论区晒图!也别忘了点赞收藏,下次更新我会讲“如何用 Firebase 实现数据云端同步”。
祝你编码愉快,少掉头发!👨💻

评论 0