Как организовать взаимодействие между BLoC/Cubit в Flutter?

«Как организовать взаимодействие между BLoC/Cubit в Flutter?» — вопрос из категории Архитектура, который задают на 29% собеседований Flutter Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В архитектуре на основе BLoC я организую взаимодействие через потоки (Streams) и менеджеры состояний, избегая прямых жестких связей. Вот основные паттерны:

1. Использование BlocListener для реакции на изменения состояния одного блока и вызова событий в другом:

BlocListener<AuthBloc, AuthState>(
  listener: (context, authState) {
    if (authState is AuthSuccess) {
      // При успешной авторизации инициируем загрузку профиля
      context.read<UserProfileBloc>().add(LoadUserProfile(authState.userId));
    }
  },
  child: ...
)

2. Общий репозиторий или сервис как источник данных для нескольких блоков:

class DataRepository {
  final StreamController<DataEvent> _dataStream = StreamController.broadcast();
  Stream<DataEvent> get dataStream => _dataStream.stream;

  void updateData(Data newData) {
    // Обновление данных...
    _dataStream.add(DataUpdated(newData));
  }
}
// Несколько блоков могут слушать один и тот же поток из репозитория.

3. Композиция блоков (один блок использует другой):

class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
  final CartBloc cartBloc;
  late final StreamSubscription _cartSubscription;

  CheckoutBloc(this.cartBloc) : super(CheckoutInitial()) {
    // Реагируем на изменения состояния корзины
    _cartSubscription = cartBloc.stream.listen((cartState) {
      if (cartState is CartLoaded) {
        add(CalculateTotal(cartState.items));
      }
    });
  }

  @override
  Future<void> close() {
    _cartSubscription.cancel(); // Важно отписаться
    return super.close();
  }
}

Ключевой принцип — однонаправленный поток данных. Блоки общаются через события и состояния, а не вызывают методы друг друга напрямую, что сохраняет предсказуемость и упрощает тестирование.