이전 글 : 2024.06.04 - [FLUTTER] - [Bloc] 개념 이해하기 2
Bloc
Bloc은 조금 더 나은 클래스로 events에 의존해서 state변경을 만듭니다.Bloc 은 또한 BlocBase를 extends하여 Cubit과 유사한 공통 Api가 있습니다.그러나 bloc은 새로운 state를 직접 emit하는 대신 event 를 수신하고 수신된 event를 나가는 state로 변환합니다.
Bloc 만들기
Bloc을 생성하는 것은 cubit을 생성하는 것과 비슷하지만, 관리할 state를 정의하는것 외에 bloc이 처리할 event도 정의해야 한다는 점이 다르다. event는 bloc에 대한 입력을 받는 것이다.
sealed class CounterEvent{}
final class CounterIncrementPressed extends CounterEvent{}
class CounterBloc extencds Bloc<CounterEvent, int> {
CounterBloc() : super(0);
}
Bloc의 state변화
Bloc 은 Cubit의 함수가 아닌 on<Event> Api를 통해 이벤트 핸들러를 등록해야 합니다. 이벤트 핸들러는 들어오는 모든 event를 0개 이상의 나가는 state로 변환하는 역활을 수행합니다.
sealed class CounterEvent {}
final class CounterIncrementPressed extends CounterEvent{}
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0) {
on<CounterIncrementPressed>((event, emit){
// 여기서 CounterIncrementPressed 를 구현한다.
});
}
그런 다음 EventHandler를 업데이트 하여 CounterIncrementPressed 이벤트를 처리할 수 있다.
sealed class CounterEvent {}
final class CounterIncrementPressed extends CounterEvent{}
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0) {
on<CounterIncrementPressed>((event, emit){
// 여기서 CounterIncrementPressed 를 구현한다.
emit(state + 1);
});
}
Bloc 사용하기
먼저 CounterBloc 의 인스턴스를 생성하여 사용할 수 있습니다.
Future<void> main() async {
final bloc = CounterBloc();
print(bloc.state); // 0
bloc.add(CounterIncrementPressed());
await Future.delayed(Duration.zero);
print(bloc.state); // 1
await bloc.close();
}
Stream 사용법
cubit과 마찬가리조 Bloc은 Stream의 특수한 유형으로 Bloc을 구독하여 state를 실시간으로 업데이트 할 수도 있습니다.
Future<void> main() async {
final bloc = CounterBloc();
final subscription = bloc.stream.listen(print); // 1
bloc.add(CounterIncrementPressed());
await Future.delayed(Duration.zero);
await subscription.cancel();
await bloc.close();
}
Bloc 관찰하기
bloc은 blocbase 를 extends하기 때문에 onChanged를 사용하여 Bloc의 모든 state 변화를 관찰 할 수 있습니다.
sealed class CounterEvent {}
final class CounterIncrementPressed extends CounterEvent {}
class CounterBloc extends Bloc<CounterEvent, int> {
Counter() : super(0) {
on<CounterIncrementPressed>((event, emit){
emit(state + 1);
})
}
@override
void onChange(Change<int> change){
super.onChange(change);
print(change);
}
}
다음 main.dart로 업데이트 합니다.
void main(){
CounterBloc()
..add(CounterIncrementPressed())
..close();
}
Bloc 과 Cubir의 주요 차별화 요소는 Bloc 이 event 기반이기 때문에 state 변화를 유발한 원인에 대한 정보도 알수 있습니다.
이는 onTransition을 override하여 수행할 수 있습니다.
sealed class CounterEvent {}
final class CounterIncrementPressed extends CounterEvent {}
class CounterBloc extends Bloc<CounterEvent, int> {
Counter() : super(0) {
on<CounterIncrementPressed>((event, emit){
emit(state + 1);
})
}
@override
void onChange(Change<int> change){
super.onChange(change);
print(change);
}
@override
void onTransition(Transition<CounterEvent, int> transition){
super.onTransition(transition);
print(transition);
}
}
BlocObserver
이전과 마찬가리고 커스텀 BlocObserver에서 onTransition을 overrided하여 단일 위치에서 발생하는 모든 Transition을 관찰할 수 있습니다.
class SimpleBlocObserver extends BlocObserver {
@override
void onChange(BlocBase bloc, Change change){
super.onChange(bloc, change);
print('${bloc.runtimeType} $change');
}
@override
void onTransition(Bloc bloc, Transition transition){
super.onTransition(bloc, transition);
print('${bloc.runtimeType} $transition');
}
@override
void onError(Blocbase bloc, Object error, StackTrace stackTrace){
print('${bloc.runtimeType} $error $stackTrace');
super.onError(bloc, error, stackTrace);
}
}
Bloc 인스턴스의 또 다른 독특한 특징은 Blocd에 새 event가 추가될 때마다 호출되는 onEvent를 override할 수 있다는 점입니다.
conChange 및 onTransition과 마찬가지로 onEvent는 전역뿐만 아니라 로컬에서도 override할 수 있습니다.
sealed class CounterEvent {}
final class CounterIncrementPressed extends CounterEvent {}
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0) {
on<CounterIncrementPressed>((event, emit) => emit(state + 1));
}
@override
void onEvent(CounterEvent event) {
super.onEvent(event);
print(event);
}
@override
void onChange(Change<int> change) {
super.onChange(change);
print(change);
}
@override
void onTransition(Transition<CounterEvent, int> transition) {
super.onTransition(transition);
print(transition);
}
}
class SimpleBlocObserver extends BlocObserver {
@override
void onEvent(Bloc bloc, Object? event) {
super.onEvent(bloc, event);
print('${bloc.runtimeType} $event');
}
@override
void onChange(BlocBase bloc, Change change) {
super.onChange(bloc, change);
print('${bloc.runtimeType} $change');
}
@override
void onTransition(Bloc bloc, Transition transition) {
super.onTransition(bloc, transition);
print('${bloc.runtimeType} $transition');
}
}
Bloc의 에러 처리
cubit과 마찬가지로 각 Bloc에는 addError와 onError메서드가 있습니다. Bloc 내부 어디에서나 addError를 호출하여 에러가 발생했음을 알릴 수 있습니다. 그런 다음 Cubit과 마찬가지로 onError를 override하여 모든 에러에 대응할 수 있습니다.
sealed class CounterEvent {}
final class CounterIncrementPressed extends CounterEvent {}
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0) {
on<CounterIncrementPressed>((event, emit) {
addError(Exception('increment error!'), StackTrace.current);
emit(state + 1);
});
}
@override
void onChange(Change<int> change) {
super.onChange(change);
print(change);
}
@override
void onTransition(Transition<CounterEvent, int> transition) {
print(transition);
super.onTransition(transition);
}
@override
void onError(Object error, StackTrace stackTrace) {
print('$error, $stackTrace');
super.onError(error, stackTrace);
}
}
'FLUTTER' 카테고리의 다른 글
[FLUTTER][iOS] '..., but they required a higher minimum deployment target’ 오류 (0) | 2024.08.23 |
---|---|
[Bloc] 개념 이해하기 2 (0) | 2024.06.05 |
[Flutter] Bloc 개념 이해하기 1 (2) | 2024.06.04 |
.framework does not support the minimum OS Version specified in the (2) | 2024.04.27 |
[Flutter] Gradle issue (2) | 2023.11.24 |