Flutter状态管理:从零开始写一个记账小产品

并发很头大
2025-12-23 23:50
阅读 2236

大家好,我是正在读研的 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

  1. flutter.dev 下载 SDK
  2. 解压到 C:\flutter(Windows)或 ~/flutter(Mac)
  3. 配置环境变量:把 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章详细讲解了各种状态管理方案,比网上碎片化教程系统得多。


学习路径建议

  1. 先掌握 Provider 基础

    • 理解 ChangeNotifier + Consumer 模式
    • 练习:做一个待办事项 App
  2. 进阶:处理异步状态
    比如从网络加载数据:

    Future<void> loadExpenses() async {
      final data = await http.get(...);
      _expenses = parse(data);
      notifyListeners();
    }
    
  3. 探索其他方案

    • 当项目变大,试试 Riverpod(更安全)
    • 如果喜欢响应式编程,学 Bloc
  4. 结合后端
    用 SpringBoot 写个 REST API,让 Flutter App 能同步数据到服务器。这时状态管理就和后端协作了!


结语:产品思维比技术更重要

我见过太多同学沉迷于“哪个状态管理库最好”,却忽略了用户真正需要什么

你做的记账 App:

  • 是给大学生记食堂开销?
  • 还是给家庭主妇管日常账单?

明确产品目标,再选合适的技术。Provider 对 90% 的 Flutter 产品已经足够。

最后送大家一句话:“状态管理不是炫技,而是让数据流动起来服务于用户。”

希望这篇实践教程能帮你跨过 Flutter 的第一道坎。如果你跟着做出了自己的记账本,欢迎在评论区晒图!也别忘了点赞收藏,下次更新我会讲“如何用 Firebase 实现数据云端同步”。

祝你编码愉快,少掉头发!👨‍💻

评论 0

最热最新
暂无评论
并发很头大Lv.1
0
影响力
0
文章
0
粉丝