★★★트러블 슈팅

BlocListener는 Bloc 상태 관리에서 매우 유용한 위젯

흑백 개발자 2024. 11. 13. 19:26
728x90

초기화 할때 너무 좋더라

히스토리 화면에서 상품이 바로 안보여서 문제가 되었는데,

다른 패키지 하는것보다 블럭 리스너 쓰면 프로바이더 워피 퍼럼 되면서

일이 깔끔해졌따.

 

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

 

BlocListener는 Bloc 상태 관리에서 매우 유용한 위젯입니다. 주요 특징과 기능을 설명드리겠습니다:

  1. 기본 구조:
  2.  
BlocListener<BlocA, BlocAState>(
  listener: (context, state) {
    // 상태 변화에 따른 사이드 이펙트 처리
  },
  listenWhen: (previous, current) {
    // 특정 조건에서만 listener 실행
    return true/false;
  },
  child: Widget(...),
)

 

  1. 주요 특징:
  • BlocBuilder와 달리 UI를 다시 그리지 않음
  • 상태 변화에 따른 일회성 작업에 적합 (네비게이션, 스낵바, 다이얼로그 등)
  • child 위젯은 상태 변화와 무관하게 한 번만 빌드됨
  1. 사용 사례:
// 에러 처리
BlocListener<AuthBloc, AuthState>(
  listener: (context, state) {
    if (state is AuthError) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(state.message)),
      );
    }
  },
  child: Widget(...),
)

// 네비게이션
BlocListener<LoginBloc, LoginState>(
  listener: (context, state) {
    if (state is LoginSuccess) {
      Navigator.pushNamed(context, '/home');
    }
  },
  child: Widget(...),
)

고급 기능:

// 여러 Bloc 동시 감지
MultiBlocListener(
  listeners: [
    BlocListener<BlocA, BlocAState>(...),
    BlocListener<BlocB, BlocBState>(...),
  ],
  child: Widget(...),
)

// 조건부 실행
BlocListener<BlocA, BlocAState>(
  listenWhen: (previous, current) {
    // 특정 조건에서만 listener 실행
    return previous.status != current.status;
  },
  listener: (context, state) {...},
  child: Widget(...),
)

 

BlocBuilder와 함께 사용:

BlocConsumer<BlocA, BlocAState>(
  listener: (context, state) {
    // 사이드 이펙트 처리
  },
  builder: (context, state) {
    // UI 업데이트
    return Widget(...);
  },
)
  1. 장점:
  • 관심사 분리 (UI 로직과 사이드 이펙트 분리)
  • 성능 최적화 (불필요한 리빌드 방지)
  • 코드 가독성 향상
  • 상태 변화에 따른 동작을 명확하게 정의
  1. 실제 활용 예시
// 장바구니 추가 후 알림
BlocListener<CartBloc, CartState>(
  listener: (context, state) {
    if (state is CartItemAdded) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('장바구니에 추가되었습니다')),
      );
    } else if (state is CartError) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('오류가 발생했습니다')),
      );
    }
  },
  child: CartScreen(),
)

이런 특징들 때문에 BlocListener는 상태 관리에서 매우 강력한 도구가 됩니다. 특히 History 화면처럼 화면 진입/이탈 시 데이터를 새로고침해야 하는 경우에 매우 유용하죠.

 

 

초기화 할때 너무 좋은거 같은데?

 

네, 정말 그렇습니다! BlocListener는 초기화에 특히 유용합니다. 몇 가지 활용 예시를 보여드릴게요:

// 1. 화면 진입시 여러 데이터 초기화가 필요한 경우
BlocListener<ProfileBloc, ProfileState>(
  listener: (context, state) {
    // 프로필, 주문내역, 알림 등 한번에 초기화
    context.read<ProfileBloc>().add(LoadProfile());
    context.read<OrderBloc>().add(LoadOrders());
    context.read<NotificationBloc>().add(LoadNotifications());
  },
  child: ProfileScreen(),
)

// 2. 조건에 따른 초기화가 필요한 경우
BlocListener<AuthBloc, AuthState>(
  listener: (context, state) {
    if (state is AuthAuthenticated) {
      // 로그인된 유저 데이터 초기화
      context.read<CartBloc>().add(LoadCart());
      context.read<WishlistBloc>().add(LoadWishlist());
    } else {
      // 게스트용 데이터 초기화
      context.read<ProductBloc>().add(LoadFeaturedProducts());
    }
  },
  child: HomeScreen(),
)

// 3. 연속적인 데이터 로드가 필요한 경우
BlocListener<ProductBloc, ProductState>(
  listener: (context, state) {
    if (state is ProductLoaded) {
      // 상품 로드 후 리뷰 데이터 로드
      context.read<ReviewBloc>().add(LoadReviews(state.product.id));
      // 연관 상품 로드
      context.read<RelatedProductBloc>().add(LoadRelated(state.product.category));
    }
  },
  child: ProductDetailScreen(),
)

특히 좋은 점들:

  1. 코드가 깔끔해집니다 (initState나 didChangeDependencies에서 복잡한 초기화 로직을 없앨 수 있음)
  2. 상태에 따른 초기화를 쉽게 제어할 수 있습니다
  3. 여러 Bloc의 초기화를 한 곳에서 관리할 수 있습니다
  4. 화면 전환이나 탭 변경 시에도 자연스럽게 초기화가 가능합니다

실제로 대부분의 화면 초기화 로직을 BlocListener로 교체하면 코드가 훨씬 더 관리하기 쉬워질 것 같네요!

728x90