# Bloc 流 (Stream) 的管理套件。 把每次資料的更動都視為一個 event,能夠自己定義每次事件發送的時機、是否發送通知、當前的 State 狀態,下一個 State 狀態,資料的轉換和合併等等... ### Usage * 定義抽象類別 `CounterEvent` * 定義 `CounterBloc` 並繼承 `Bloc<Event, State>` 其中的 Event 就是用來發送通知的事件,State 存放需要變動的資料。 * 通過 `on<E extends Event>` 決定資料如何發送 * `BlocBuilder` 取得定義好的 Bloc 物件,底下的 widget 可以直接拿到 State 裡面的資料,並在資料變動時自動刷新。 * 需要主動發送事件時用 `context.read<Bloc>().add(event)` ``` import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'main_view_model.dart'; void main() { Bloc.observer = const AppBlocObserver(); runApp(const App()); } class AppBlocObserver extends BlocObserver { const AppBlocObserver(); @override void onChange(BlocBase bloc, Change change) { super.onChange(bloc, change); if (bloc is Cubit) print(change); } @override void onTransition(Bloc bloc, Transition transition) { super.onTransition(bloc, transition); print(transition); } } class App extends StatelessWidget { const App({super.key}); @override Widget build(BuildContext context) { return const AppView(); } } class AppView extends StatelessWidget { const AppView({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( home: CounterPage(), ); } } class CounterPage extends StatelessWidget { const CounterPage({super.key}); @override Widget build(BuildContext context) { return BlocProvider( create: (_) => CounterBloc(), child: const CounterView(), ); } } class CounterView extends StatelessWidget { const CounterView({super.key}); @override Widget build(BuildContext context) { return BlocBuilder<CounterBloc, MainViewModel>( builder: (context, viewModel) { return Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '${viewModel.selectedColorIndex}', style: Theme.of(context).textTheme.displayLarge, ), const SizedBox(width: 20), FloatingActionButton( child: const Icon(Icons.add), onPressed: () { context.read<CounterBloc>().add(UpIncrementPressed()); }, ), const SizedBox(width: 20), FloatingActionButton( child: const Icon(Icons.remove), onPressed: () { context.read<CounterBloc>().add(UpDecrementPressed()); }, ), ], ), Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '${viewModel.selectedSizeIndex}', style: Theme.of(context).textTheme.displayLarge, ), const SizedBox(width: 20), FloatingActionButton( child: const Icon(Icons.add), onPressed: () { context.read<CounterBloc>().add(DownIncrementPressed()); }, ), const SizedBox(width: 20), FloatingActionButton( child: const Icon(Icons.remove), onPressed: () { context.read<CounterBloc>().add(DownDecrementPressed()); }, ), ], ), ], ); }, ); } } abstract class CounterEvent {} class UpIncrementPressed extends CounterEvent {} class UpDecrementPressed extends CounterEvent {} class DownIncrementPressed extends CounterEvent {} class DownDecrementPressed extends CounterEvent {} class CounterBloc extends Bloc<CounterEvent, MainViewModel> { CounterBloc() : super(MainViewModel()) { on<UpIncrementPressed>((event, emit) { emit(state.selectColor( selectedColorIndex: state.selectedColorIndex + 1, selectedSizeIndex: 0 )); }); on<UpDecrementPressed>((event, emit) { emit(state.selectColor( selectedColorIndex: state.selectedColorIndex - 1, selectedSizeIndex: 0 )); }); on<DownIncrementPressed>((event, emit) { emit(state.selectSize( selectedColorIndex: state.selectedColorIndex, selectedSizeIndex: state.selectedSizeIndex + 1 )); }); on<DownDecrementPressed>((event, emit) { emit(state.selectSize( selectedColorIndex: state.selectedColorIndex, selectedSizeIndex: state.selectedSizeIndex - 1 )); }); } } ``` ``` class MainViewModel { final int selectedColorIndex; final int selectedSizeIndex; MainViewModel({this.selectedColorIndex = 0, this.selectedSizeIndex = 0}); MainViewModel selectColor({int? selectedColorIndex, int? selectedSizeIndex}) { return MainViewModel( selectedColorIndex: selectedColorIndex ?? this.selectedColorIndex, selectedSizeIndex: selectedSizeIndex ?? this.selectedSizeIndex ); } MainViewModel selectSize({int? selectedColorIndex, int? selectedSizeIndex}) { return MainViewModel( selectedColorIndex: selectedColorIndex ?? this.selectedColorIndex, selectedSizeIndex: selectedSizeIndex ?? this.selectedSizeIndex ); } } ``` ### Observer 在 main 註冊觀察者,可以監聽每次事件的發送狀態、當前的 State、下一個 State。 * onCreate: 當 Bloc 對象創建時調用。 * onEvent: 當一個事件被 `added` 到 Bloc 時調用。 * onTransition: 當狀態被轉換時調用。 * onChange: 當狀態被發送時調用。 * onClose: 當 Bloc 對象關閉時調用。 * onError: 當出現錯誤時調用。 ``` void main() { Bloc.observer = const AppBlocObserver(); runApp(const App()); } class AppBlocObserver extends BlocObserver { const AppBlocObserver(); @override void onChange(BlocBase bloc, Change change) { super.onChange(bloc, change); print(change); } @override void onTransition(Bloc bloc, Transition transition) { super.onTransition(bloc, transition); print(transition); } } ```