# 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);
}
}
```