---
# System prepended metadata

title: Bloc

---

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