Flutter 状态管理最佳实践:从零开始写一个 Todo App
开篇:什么是状态管理?为什么它很重要?

在开发移动应用时,你经常会遇到这样一个问题:“用户操作后,界面应该怎么变?”比如:
- 用户勾选了某个任务,任务列表应该如何更新?
- 用户切换了主题颜色,整个界面需要随之改变吗?
- 页面跳转的时候,怎么把数据带到下一个页面?
这些“变化”就是我们常说的状态(State)。而状态管理(State Management),简单来说就是用来管理和控制这些状态的技术。
Flutter 本身已经提供了一些基础的状态管理机制,但当应用越来越复杂时,使用合适的状态管理模式能让你的应用更稳定、代码更容易维护,甚至让团队协作更高效。
本教程将带你一步步了解 Flutter 中常用的状态管理方式,并通过一个完整的 Todo 应用实例来演示如何进行实战开发。
环境准备:搭建你的 Flutter 开发环境

第一步:安装 Flutter SDK
前往 Flutter 官网 根据你的操作系统下载并安装 Flutter。
安装完成后,在终端运行下面这条命令检查是否成功:
flutter doctor
如果显示 No issues found! 就说明一切正常。
第二步:安装 IDE(推荐 VS Code)
你可以选择 Android Studio 或者 VS Code。这里以 Visual Studio Code 为例:
- 下载安装 VS Code
- 打开 VS Code,搜索插件 “Flutter” 和 “Dart”,分别安装
- 打开一个新的 Flutter 项目:
- 按快捷键
Ctrl + Shift + P - 输入
Flutter: New Project,选择空项目模板即可
- 按快捷键
第三步:启动模拟器或连接手机设备
- Android:使用 Android Studio 自带的 AVD Manager 创建一个安卓模拟器
- iOS:使用 Xcode 启动 iOS 模拟器(仅适用于 Mac)
- 真机调试:通过 USB 连接手机,并启用开发者选项和 USB 调试模式
确认设备可用后,在终端运行以下命令启动应用:
flutter run
核心概念:状态管理的几种常见方式

Flutter 提供了多种状态管理方案,适用于不同规模的项目。下面我们逐个介绍,每种方法都会配上代码示例帮助理解。
1. 使用 StatefulWidget(适合小型项目)
这是最基础的状态管理方式,适合只涉及单个组件的状态变化。
示例:一个简单的计数器按钮
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const CounterPage(),
);
}
}
class CounterPage extends StatefulWidget {
const CounterPage({Key? key}) : super(key: key);
@override
State<CounterPage> createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int count = 0;
void increment() {
setState(() {
count++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('计数器')),
body: Center(
child: Text('点击次数: $count', style: Theme.of(context).textTheme.headline4),
),
floatingActionButton: FloatingActionButton(onPressed: increment, child: const Icon(Icons.add)),
);
}
}
这个例子中,点击按钮会触发 setState() 方法,从而刷新 UI。
💡 适用场景:页面逻辑简单、状态独立的小型页面
2. 使用 Provider(适合中大型项目)
Provider 是 Flutter 团队推荐的一种轻量级状态管理工具,特别适合跨组件共享状态。
步骤一:添加依赖包
在 pubspec.yaml 文件中添加:
dependencies:
flutter:
sdk: flutter
provider: ^6.1.1
然后运行:
flutter pub get
步骤二:创建模型类
创建一个表示任务的数据模型:
// models/todo_model.dart
import 'package:flutter/foundation.dart';
class Todo {
final String id;
final String title;
bool completed;
Todo({required this.id, required this.title, this.completed = false});
}
class TodoListModel with ChangeNotifier {
final List<Todo> _todos = [];
List<Todo> get todos => [..._todos]; // 返回副本防止外部修改
void addTodo(String title) {
_todos.add(Todo(id: DateTime.now().toString(), title: title));
notifyListeners(); // 通知监听者更新UI
}
void toggleComplete(String id) {
final todo = _todos.firstWhere((t) => t.id == id);
todo.completed = !todo.completed;
notifyListeners();
}
}
步骤三:配置全局 Provider
// main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'models/todo_model.dart';
import 'pages/home_page.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => TodoListModel(),
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Todo App',
theme: ThemeData(primarySwatch: Colors.deepOrange),
home: const HomePage(),
);
}
}
步骤四:在页面中使用状态
// pages/home_page.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/todo_model.dart';
class HomePage extends StatelessWidget {
final TextEditingController _controller = TextEditingController();
void _addTodo(BuildContext context) {
if (_controller.text.isNotEmpty) {
context.read<TodoListModel>().addTodo(_controller.text);
_controller.clear();
}
}
@override
Widget build(BuildContext context) {
final todos = context.watch<TodoListModel>().todos;
return Scaffold(
appBar: AppBar(title: const Text('Todo 列表')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
TextField(controller: _controller, decoration: const InputDecoration(hintText: "输入新任务")),
ElevatedButton(onPressed: () => _addTodo(context), child: const Text("添加任务")),
const SizedBox(height: 20),
Expanded(
child: ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
final todo = todos[index];
return ListTile(
title: Text(todo.title),
trailing: Checkbox(
value: todo.completed,
onChanged: (_) {
context.read<TodoListModel>().toggleComplete(todo.id);
},
),
);
},
),
),
],
),
),
);
}
}
💡 优点:
- Flutter 官方推荐
- 使用简单,性能良好
- 支持多层级嵌套
3. 使用 Bloc 模式(适合大型项目)
Bloc 是一种响应式编程状态管理模式,通过事件驱动的方式来处理业务逻辑。
安装依赖
dependencies:
flutter_bloc: ^8.1.3
equatable: ^2.0.5 # 帮助比较对象
创建 Bloc 架构
a. 定义事件类型
// blocs/todo_event.dart
import 'package:equatable/equatable.dart';
abstract class TodoEvent extends Equatable {}
class AddTodoEvent extends TodoEvent {
final String title;
AddTodoEvent(this.title);
@override
List<Object?> get props => [title];
}
b. 定义状态
// blocs/todo_state.dart
import 'package:equatable/equatable.dart';
import '../models/todo.dart';
abstract class TodoState extends Equatable {}
class InitialState extends TodoState {
@override
List<Object?> get props => [];
}
class TodosLoadedState extends TodoState {
final List<Todo> todos;
TodosLoadedState(this.todos);
@override
List<Object?> get props => [todos];
}
c. 创建 Bloc
// blocs/todo_bloc.dart
import 'package:flutter_bloc/flutter_bloc.dart';
import 'todo_event.dart';
import 'todo_state.dart';
import '../models/todo.dart';
class TodoBloc extends Bloc<TodoEvent, TodoState> {
TodoBloc() : super(InitialState());
final List<Todo> _todos = [];
@override
Stream<TodoState> mapEventToState(TodoEvent event) async* {
if (event is AddTodoEvent) {
_todos.add(Todo(id: DateTime.now().toString(), title: event.title));
yield TodosLoadedState(_todos);
}
}
}
d. 在页面中使用 Bloc
// pages/home_bloc_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../blocs/todo_bloc.dart';
import '../blocs/todo_event.dart';
class HomeBlocPage extends StatelessWidget {
final TextEditingController _controller = TextEditingController();
void _addTodo(BuildContext context) {
if (_controller.text.isNotEmpty) {
context.read<TodoBloc>().add(AddTodoEvent(_controller.text));
_controller.clear();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Bloc 版 Todo')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
TextField(controller: _controller, decoration: const InputDecoration(hintText: "输入新任务")),
ElevatedButton(onPressed: () => _addTodo(context), child: const Text("添加任务")),
const SizedBox(height: 20),
BlocBuilder<TodoBloc, TodoState>(
builder: (context, state) {
if (state is InitialState) {
return const Center(child: Text("还没有任务哦"));
} else if (state is TodosLoadedState) {
return ListView.builder(
shrinkWrap: true,
itemCount: state.todos.length,
itemBuilder: (_, i) => ListTile(title: Text(state.todos[i].title)),
);
}
return Container();
},
)
],
),
),
);
}
}
💡 特点:
- 解耦 UI 和逻辑
- 可测试性强
- 适合大型团队协作
实战项目:动手做一个 Todo 应用(使用 Provider)
接下来我们将使用 Provider 来完成一个功能较完整的 Todo 应用。
功能需求:
- 添加任务
- 删除任务
- 标记已完成
- 数据持久化(使用 shared_preferences)
步骤一:添加依赖
dependencies:
provider: ^6.1.1
shared_preferences: ^2.2.2
步骤二:改造 Todo 模型
为了保存到本地存储,我们需要支持序列化和反序列化:
// models/todo_model.dart
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
class Todo {
String id;
String title;
bool completed;
Todo({required this.id, required this.title, this.completed = false});
Map<String, dynamic> toJson() => {'id': id, 'title': title, 'completed': completed};
factory Todo.fromJson(Map<String, dynamic> json) {
return Todo(
id: json['id'],
title: json['title'],
completed: json['completed'] ?? false,
);
}
}
步骤三:实现本地存储
// models/todo_local_storage.dart
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter/foundation.dart';
import '../models/todo_model.dart';
class TodoLocalStorage {
static Future<void> saveTodos(List<Todo> todos) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<String> jsonList = todos.map((t) => jsonEncode(t.toJson())).toList();
await prefs.setStringList('todos', jsonList);
}
static Future<List<Todo>> loadTodos() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<String>? jsonList = prefs.getStringList('todos');
if (jsonList == null || jsonList.isEmpty) return [];
return jsonList.map((j) => Todo.fromJson(jsonDecode(j))).toList();
}
}
步骤四:改进 TodoListModel
// models/todo_list_model.dart
import 'package:flutter/foundation.dart';
import 'package:todo/models/todo_model.dart';
import 'package:todo/models/todo_local_storage.dart';
class TodoListModel with ChangeNotifier {
List<Todo> _todos = [];
List<Todo> get todos => [..._todos];
Future<void> init() async {
_todos = await TodoLocalStorage.loadTodos();
notifyListeners();
}
void addTodo(String title) {
_todos.add(Todo(id: DateTime.now().toString(), title: title));
_saveAndNotify();
}
void deleteTodo(String id) {
_todos.removeWhere((t) => t.id == id);
_saveAndNotify();
}
void toggleComplete(String id) {
_todos.firstWhere((t) => t.id == id).completed = !_todos.firstWhere((t) => t.id == id).completed;
_saveAndNotify();
}
void _saveAndNotify() {
TodoLocalStorage.saveTodos(_todos);
notifyListeners();
}
}
步骤五:页面中调用
在 HomePage 页面中加入删除功能,并初始化数据:
// pages/home_page.dart
...
@override
void initState() {
WidgetsBinding.instance!.addPostFrameCallback((_) {
context.read<TodoListModel>().init();
});
super.initState();
}

// ...原有代码不变,添加 delete 功能
IconButton(icon: Icon(Icons.delete), onPressed: () => context.read<TodoListModel>().deleteTodo(todo.id))
...
🎉 恭喜你!你已经完成了一个完整状态管理版本的 Todo 应用啦!
常见问题解答(FAQ)
Q1:什么时候应该使用 Provider 而不是 StatefulWidget?
- 当多个组件之间要共享状态时使用 Provider。
- 单个组件内部的状态可以用 StatefulWidget。
- 如果未来可能扩展为多个页面,建议直接上 Provider。
Q2:Bloc 和 Provider 有什么区别?
- Provider 更适合中小型项目,使用起来简单直观。
- Bloc 更加解耦,适用于大型项目,便于单元测试和多人协作。
Q3:状态管理会不会导致内存泄漏?
- 不规范的使用可能会导致内存泄漏。
- 使用
ChangeNotifier时要注意避免循环引用。 - 推荐使用
Provider内置的 dispose 机制。
学习建议:下一步该学什么?
如果你已经掌握了以上内容,建议继续学习:
- 进阶状态管理库:尝试 Redux、Riverpod、GetX 等框架,理解它们各自的优势
- 架构设计:学习 BLoC、MVVM、Clean Architecture 等架构模式
- 性能优化:学习
const widget、keys、shouldRebuildWidget等 Flutter 渲染优化技巧 - 真实项目开发:尝试构建一个完整社交、电商等类型的项目,并集成网络请求、数据库、路由等功能
总结
本文从零开始讲解了 Flutter 的状态管理基础知识,并逐步引导你完成了一个可运行的 Todo 应用。通过实际动手,相信你对状态管理的理解更加深入了!
记得一句话:好状态管理 = 易维护 + 可扩展 + 高性能。掌握状态管理是成为一名优秀 Flutter 开发者的必经之路。
🚀 继续加油,写出更多惊艳的 App!

评论 0