Flutter状态管理Bloc详解:实现响应式架构
·
Flutter状态管理Bloc详解:实现响应式架构
一、Bloc概述
Bloc(Business Logic Component)是一种状态管理模式,将业务逻辑与UI分离。
1.1 添加依赖
dependencies:
flutter_bloc: ^8.1.3
equatable: ^2.0.5
1.2 核心概念
| 组件 | 作用 |
|---|---|
| Event | 触发状态变化的事件 |
| State | 当前状态 |
| Bloc | 处理事件并输出状态 |
| Cubit | 简化版Bloc,适合简单场景 |
二、创建Bloc
2.1 定义Event和State
part 'counter_event.dart';
part 'counter_state.dart';
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(const CounterState()) {
on<IncrementEvent>(_onIncrement);
on<DecrementEvent>(_onDecrement);
on<ResetEvent>(_onReset);
}
void _onIncrement(IncrementEvent event, Emitter<CounterState> emit) {
emit(state.copyWith(count: state.count + 1));
}
void _onDecrement(DecrementEvent event, Emitter<CounterState> emit) {
emit(state.copyWith(count: state.count - 1));
}
void _onReset(ResetEvent event, Emitter<CounterState> emit) {
emit(const CounterState(count: 0));
}
}
2.2 Event类
part of 'counter_bloc.dart';
abstract class CounterEvent extends Equatable {
const CounterEvent();
@override
List<Object> get props => [];
}
class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}
class ResetEvent extends CounterEvent {}
2.3 State类
part of 'counter_bloc.dart';
class CounterState extends Equatable {
final int count;
final bool isLoading;
const CounterState({
this.count = 0,
this.isLoading = false,
});
CounterState copyWith({
int? count,
bool? isLoading,
}) {
return CounterState(
count: count ?? this.count,
isLoading: isLoading ?? this.isLoading,
);
}
@override
List<Object> get props => [count, isLoading];
}
三、在UI中使用Bloc
3.1 BlocProvider
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => CounterBloc(),
child: const MaterialApp(
home: CounterPage(),
),
);
}
}
3.2 BlocBuilder
class CounterPage extends StatelessWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Center(
child: Text('Count: ${state.count}'),
);
},
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: () => context.read<CounterBloc>().add(const IncrementEvent()),
child: const Icon(Icons.add),
),
const SizedBox(height: 8),
FloatingActionButton(
onPressed: () => context.read<CounterBloc>().add(const DecrementEvent()),
child: const Icon(Icons.remove),
),
],
),
);
}
}
四、异步操作
class UserBloc extends Bloc<UserEvent, UserState> {
final UserRepository _userRepository;
UserBloc(this._userRepository) : super(const UserState()) {
on<FetchUserEvent>(_onFetchUser);
}
Future<void> _onFetchUser(FetchUserEvent event, Emitter<UserState> emit) async {
emit(state.copyWith(status: UserStatus.loading));
try {
final user = await _userRepository.getUser(event.id);
emit(state.copyWith(
status: UserStatus.success,
user: user,
));
} catch (e) {
emit(state.copyWith(
status: UserStatus.failure,
error: e.toString(),
));
}
}
}
五、状态转换
class UserState extends Equatable {
final UserStatus status;
final User? user;
final String? error;
const UserState({
this.status = UserStatus.initial,
this.user,
this.error,
});
UserState copyWith({
UserStatus? status,
User? user,
String? error,
}) {
return UserState(
status: status ?? this.status,
user: user ?? this.user,
error: error ?? this.error,
);
}
@override
List<Object?> get props => [status, user, error];
}
enum UserStatus {
initial,
loading,
success,
failure,
}
六、Cubit简化版
class CounterCubit extends Cubit<CounterState> {
CounterCubit() : super(const CounterState());
void increment() => emit(state.copyWith(count: state.count + 1));
void decrement() => emit(state.copyWith(count: state.count - 1));
void reset() => emit(const CounterState());
}
// 使用
class CounterPage extends StatelessWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocBuilder<CounterCubit, CounterState>(
builder: (context, state) {
return Center(child: Text('Count: ${state.count}'));
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.read<CounterCubit>().increment(),
child: const Icon(Icons.add),
),
);
}
}
七、BlocListener
BlocListener<CounterBloc, CounterState>(
listener: (context, state) {
if (state.count == 10) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Reached 10!')),
);
}
},
child: const CounterView(),
)
八、最佳实践
8.1 分离关注点
// Repository层
class UserRepository {
Future<User> getUser(int id) async {
// API调用
}
}
// Bloc层
class UserBloc extends Bloc<UserEvent, UserState> {
final UserRepository _repository;
UserBloc(this._repository) : super(const UserState()) {
on<FetchUserEvent>(_onFetchUser);
}
Future<void> _onFetchUser(FetchUserEvent event, Emitter<UserState> emit) async {
emit(state.copyWith(status: UserStatus.loading));
try {
final user = await _repository.getUser(event.id);
emit(state.copyWith(status: UserStatus.success, user: user));
} catch (e) {
emit(state.copyWith(status: UserStatus.failure));
}
}
}
8.2 测试Bloc
void main() {
late CounterBloc counterBloc;
setUp(() {
counterBloc = CounterBloc();
});
tearDown(() {
counterBloc.close();
});
test('initial state is CounterState()', () {
expect(counterBloc.state, const CounterState());
});
blocTest<CounterBloc, CounterState>(
'emits [CounterState(count: 1)] when IncrementEvent is added',
build: () => counterBloc,
act: (bloc) => bloc.add(const IncrementEvent()),
expect: () => [const CounterState(count: 1)],
);
}
总结
Bloc是Flutter中强大的状态管理方案,通过事件驱动实现响应式架构。
关键要点:
- Event:触发状态变化的事件
- State:当前状态
- Bloc:处理事件并输出状态
- Cubit:简化版Bloc
- BlocProvider:提供Bloc实例
- BlocBuilder:根据状态重建UI
- BlocListener:监听状态变化
通过合理使用Bloc,你可以构建清晰、可测试的Flutter应用。
更多推荐
所有评论(0)