状态管理
状态管理是 Flutter 开发中的核心概念。本章将介绍状态管理的基本概念和几种常用的状态管理方案。
什么是状态
状态是指在应用运行过程中可以改变的数据。例如:
- 用户输入的内容
- 从网络获取的数据
- 当前选中的选项
- 计数器的值
状态分类
局部状态
只在单个 Widget 或小范围内使用的状态,使用 StatefulWidget 管理:
class CounterWidget extends StatefulWidget {
@override
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('计数:$_counter'),
ElevatedButton(
onPressed: () => setState(() => _counter++),
child: Text('增加'),
),
],
);
}
}
全局状态
需要在多个 Widget 之间共享的状态,需要使用状态管理方案。
InheritedWidget
InheritedWidget 是 Flutter 内置的状态共享机制,许多状态管理库都基于它实现。
基本用法
// 1. 创建 InheritedWidget
class CounterProvider extends InheritedWidget {
final int counter;
final VoidCallback increment;
const CounterProvider({
Key? key,
required this.counter,
required this.increment,
required Widget child,
}) : super(key: key, child: child);
// 提供便捷的访问方法
static CounterProvider? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<CounterProvider>();
}
@override
bool updateShouldNotify(CounterProvider oldWidget) {
return counter != oldWidget.counter;
}
}
// 2. 使用 InheritedWidget
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int _counter = 0;
void _increment() {
setState(() => _counter++);
}
@override
Widget build(BuildContext context) {
return CounterProvider(
counter: _counter,
increment: _increment,
child: MaterialApp(
home: CounterPage(),
),
);
}
}
// 3. 在子 Widget 中访问
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final provider = CounterProvider.of(context);
return Scaffold(
body: Center(
child: Text('计数:${provider?.counter}'),
),
floatingActionButton: FloatingActionButton(
onPressed: provider?.increment,
child: Icon(Icons.add),
),
);
}
}
Provider
Provider 是官方推荐的状态管理方案,简单易用。
添加依赖
在 pubspec.yaml 中添加:
dependencies:
provider: ^6.0.0
基本用法
import 'package:provider/provider.dart';
// 1. 创建状态类
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 通知监听者
}
}
// 2. 提供状态
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(),
child: MyApp(),
),
);
}
// 3. 使用状态
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
// 读取状态(会重建 Widget)
child: Text('计数:${context.watch<Counter>().count}'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 调用方法(不监听变化)
context.read<Counter>().increment();
},
child: Icon(Icons.add),
),
);
}
}
多个 Provider
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Counter()),
ChangeNotifierProvider(create: (_) => User()),
Provider(create: (_) => ApiService()),
],
child: MyApp(),
)
Consumer
Consumer 可以在特定位置重建 Widget:
class CounterDisplay extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<Counter>(
builder: (context, counter, child) {
return Column(
children: [
child!, // 不重建的部分
Text('计数:${counter.count}'),
],
);
},
child: Text('这是不会重建的部分'), // 缓存的子 Widget
);
}
}
Selector
Selector 可以选择性地监听状态的一部分:
class UserNameDisplay extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Selector<User, String>(
selector: (context, user) => user.name, // 只监听 name
builder: (context, name, child) {
return Text('用户名:$name');
},
);
}
}
完整示例
// 用户状态
class User with ChangeNotifier {
String _name = '';
int _age = 0;
String get name => _name;
int get age => _age;
void updateProfile(String name, int age) {
_name = name;
_age = age;
notifyListeners();
}
}
// 应用
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => User(),
child: MaterialApp(
home: ProfilePage(),
),
);
}
}
class ProfilePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('个人资料')),
body: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
Consumer<User>(
builder: (context, user, _) {
return Column(
children: [
Text('姓名:${user.name}'),
Text('年龄:${user.age}'),
],
);
},
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
context.read<User>().updateProfile('张三', 25);
},
child: Text('更新资料'),
),
],
),
),
);
}
}
Riverpod
Riverpod 是 Provider 的改进版本,提供更安全和灵活的状态管理。
添加依赖
dependencies:
flutter_riverpod: ^2.0.0
基本用法
import 'package:flutter_riverpod/flutter_riverpod.dart';
// 1. 定义 Provider
final counterProvider = StateProvider<int>((ref) => 0);
// 2. 包装应用
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
// 3. 使用状态
class CounterPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final counter = ref.watch(counterProvider);
return Scaffold(
body: Center(
child: Text('计数:$counter'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
ref.read(counterProvider.notifier).state++;
},
child: Icon(Icons.add),
),
);
}
}
Provider 类型
// 只读 Provider(适合计算值)
final doubledProvider = Provider<int>((ref) {
return ref.watch(counterProvider) * 2;
});
// StateProvider(简单状态)
final counterProvider = StateProvider<int>((ref) => 0);
// StateNotifierProvider(复杂状态逻辑)
class CounterNotifier extends StateNotifier<int> {
CounterNotifier() : super(0);
void increment() => state++;
void decrement() => state--;
void reset() => state = 0;
}
final counterNotifierProvider = StateNotifierProvider<CounterNotifier, int>(
(ref) => CounterNotifier(),
);
// FutureProvider(异步数据)
final userProvider = FutureProvider<User>((ref) async {
final response = await http.get(Uri.parse('https://api.example.com/user'));
return User.fromJson(jsonDecode(response.body));
});
// StreamProvider(流数据)
final tickerProvider = StreamProvider<int>((ref) {
return Stream.periodic(Duration(seconds: 1), (count) => count);
});
Bloc
Bloc 是一种更结构化的状态管理方案,适合复杂应用。
添加依赖
dependencies:
flutter_bloc: ^8.0.0
基本概念
- Event:触发状态改变的事件
- State:应用的状态
- Bloc:处理事件并输出新状态
示例
import 'package:flutter_bloc/flutter_bloc.dart';
// 1. 定义事件
abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}
class ResetEvent extends CounterEvent {}
// 2. 定义状态
class CounterState {
final int count;
CounterState(this.count);
}
// 3. 创建 Bloc
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterState(0)) {
on<IncrementEvent>((event, emit) {
emit(CounterState(state.count + 1));
});
on<DecrementEvent>((event, emit) {
emit(CounterState(state.count - 1));
});
on<ResetEvent>((event, emit) {
emit(CounterState(0));
});
}
}
// 4. 提供 Bloc
void main() {
runApp(
BlocProvider(
create: (_) => CounterBloc(),
child: MyApp(),
),
);
}
// 5. 使用 Bloc
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Text('计数:${state.count}');
},
),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: () => context.read<CounterBloc>().add(IncrementEvent()),
child: Icon(Icons.add),
),
SizedBox(height: 8),
FloatingActionButton(
onPressed: () => context.read<CounterBloc>().add(DecrementEvent()),
child: Icon(Icons.remove),
),
],
),
);
}
}
状态管理选择指南
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| setState | 简单局部状态 | 简单直接 | 不适合共享状态 |
| InheritedWidget | 自定义状态管理 | 无依赖 | 样板代码多 |
| Provider | 中小型应用 | 简单易用 | 复杂场景需配合其他方案 |
| Riverpod | 中大型应用 | 类型安全、灵活 | 学习曲线 |
| Bloc | 大型应用 | 结构清晰、可测试 | 样板代码多 |
最佳实践
1. 状态粒度
不要将所有状态放在一个类中:
// 不好:一个巨大的状态类
class AppState with ChangeNotifier {
User user;
List<Todo> todos;
Settings settings;
ThemeMode themeMode;
// ... 几十个属性和方法
}
// 好:分离状态
class UserProvider with ChangeNotifier {
User _user;
// 只管理用户相关状态
}
class TodoProvider with ChangeNotifier {
List<Todo> _todos;
// 只管理待办相关状态
}
2. 不可变状态
使用不可变状态可以避免意外修改:
class Counter with ChangeNotifier {
// 使用 final
final int _count = 0;
int get count => _count;
// 每次修改创建新实例
void increment() {
_count = _count + 1;
notifyListeners();
}
}
3. 业务逻辑分离
将业务逻辑从 UI 中分离:
// 状态类包含业务逻辑
class TodoList with ChangeNotifier {
List<Todo> _todos = [];
List<Todo> get todos => _todos;
List<Todo> get completedTodos => _todos.where((t) => t.isCompleted).toList();
List<Todo> get pendingTodos => _todos.where((t) => !t.isCompleted).toList();
void addTodo(String title) {
_todos.add(Todo(id: DateTime.now().toString(), title: title));
notifyListeners();
}
void toggleTodo(String id) {
final index = _todos.indexWhere((t) => t.id == id);
if (index != -1) {
_todos[index].isCompleted = !_todos[index].isCompleted;
notifyListeners();
}
}
}
// UI 只负责展示
class TodoPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<TodoList>(
builder: (context, todoList, _) {
return ListView.builder(
itemCount: todoList.todos.length,
itemBuilder: (context, index) {
final todo = todoList.todos[index];
return CheckboxListTile(
title: Text(todo.title),
value: todo.isCompleted,
onChanged: (_) => todoList.toggleTodo(todo.id),
);
},
);
},
);
}
}
小结
本章我们学习了:
- 状态的概念:什么是状态,局部状态和全局状态
- InheritedWidget:Flutter 内置的状态共享机制
- Provider:官方推荐的状态管理方案
- Riverpod:Provider 的改进版本
- Bloc:结构化的状态管理方案
- 最佳实践:状态粒度、不可变状态、业务逻辑分离
练习
- 使用 Provider 实现一个简单的购物车
- 使用 Riverpod 重构购物车,比较两种方案
- 使用 Bloc 实现一个登录流程
- 实现一个主题切换功能,支持浅色和深色模式
下一步
下一章我们将学习 导航和路由,掌握页面跳转和路由管理。