跳到主要内容

状态管理

状态管理是 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),
);
},
);
},
);
}
}

小结

本章我们学习了:

  1. 状态的概念:什么是状态,局部状态和全局状态
  2. InheritedWidget:Flutter 内置的状态共享机制
  3. Provider:官方推荐的状态管理方案
  4. Riverpod:Provider 的改进版本
  5. Bloc:结构化的状态管理方案
  6. 最佳实践:状态粒度、不可变状态、业务逻辑分离

练习

  1. 使用 Provider 实现一个简单的购物车
  2. 使用 Riverpod 重构购物车,比较两种方案
  3. 使用 Bloc 实现一个登录流程
  4. 实现一个主题切换功能,支持浅色和深色模式

下一步

下一章我们将学习 导航和路由,掌握页面跳转和路由管理。