Flutter状态管理Bloc详解:实现响应式架构
Flutter状态管理Bloc详解:实现响应式架构
一、Bloc概述
Bloc(Business Logic Component)是一种状态管理模式,将业务逻辑与UI分离。
1.1 添加依赖
dependencies: flutter_bloc: ^8.1.3 equatable: ^2.0.51.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应用。
