> Какие возможности есть у bloc из коробки во Flutter (Flutter, Android)

Уровень: senior · Роль: mobile · Категория: Технические вопросы

Компании: PashaPay

Стек: Flutter, Android

> Пример ответа

Короткий ответ

Bloc из коробки даёт: разделение UI и бизнес-логики через BlocProvider, BlocBuilder, BlocListener, BlocConsumer; управление состоянием через Stream/Sink; обработку ошибок через onError; трансформации событий через EventTransformer (debounce, throttle); автоматическую отмену подписок через close(). Плюс - BlocObserver для глобального логирования, Equatable для сравнения состояний, и HydratedBloc для персистентности. Всё это - без сторонних пакетов, только flutter_bloc и bloc.

Подробное объяснение

Bloc - это реализация паттерна Business Logic Component поверх Streams. Из коробки доступны:

  • Базовые виджеты: BlocProvider (инъекция), BlocBuilder (перестройка UI), BlocListener (side-effects), BlocConsumer (комбинация builder + listener), MultiBlocProvider и MultiBlocListener для композиции.
  • Жизненный цикл: close() для освобождения ресурсов, автоматическая отмена подписок при dispose.
  • Обработка ошибок: переопределение onError в Bloc - ловит все исключения из mapEventToState/onEvent и emit.
  • Трансформации событий: EventTransformer - встроенные debounceTime, throttleTime, restartable, droppable через StreamTransformer.
  • Глобальный наблюдатель: BlocObserver - onChange, onTransition, onError для логирования, аналитики, отладки.
  • Сравнение состояний: Equatable (из пакета equatable) - позволяет избежать лишних rebuild, если состояние не изменилось.
  • Персистентность: HydratedBloc - автоматическое сохранение состояния в хранилище (например, SharedPreferences) и восстановление при старте.
  • Асинхронность: emit с поддержкой async* и yield, а также emit в новых версиях (без генераторов).
  • Тестирование: bloc_test - встроенный пакет для unit-тестов с blocTest.

На практике

Для senior-позиции важно не просто перечислить, а показать понимание trade-off. Например:

  • BlocBuilder с buildWhen - для точечной перестройки. Без этого - лишние rebuild.
  • BlocListener - для навигации, snackbar, показа диалогов. Не использовать в builder.
  • EventTransformer - критичен для поиска (debounce) или кнопок (throttle).
  • HydratedBloc - удобен, но требует аккуратности с версионированием состояния (миграции).
  • BlocObserver - обязателен в продакшене для логирования ошибок, но не для чувствительных данных.
  • На практике часто комбинируют с freezed для иммутабельных состояний и get_it для DI, но это уже не из коробки.

Пример кода

class AuthBloc extends Bloc<AuthEvent, AuthState> {
  AuthBloc() : super(AuthInitial()) {
    on<LoginRequested>(_onLogin, transformer: debounce(const Duration(milliseconds: 300)));
  }

  @override
  void onError(Object error, StackTrace stackTrace) {
    // логирование в Crashlytics
    super.onError(error, stackTrace);
  }

  Future<void> _onLogin(LoginRequested event, Emitter<AuthState> emit) async {
    emit(AuthLoading());
    try {
      final user = await api.login(event.email, event.password);
      emit(AuthSuccess(user));
    } catch (e) {
      emit(AuthFailure(e.toString()));
    }
  }
}

// Использование
BlocProvider(
  create: (_) => AuthBloc(),
  child: BlocConsumer<AuthBloc, AuthState>(
    listenWhen: (prev, curr) => curr is AuthFailure,
    listener: (context, state) => showSnackBar(context, state.message),
    buildWhen: (prev, curr) => curr is! AuthFailure,
    builder: (context, state) {
      if (state is AuthLoading) return CircularProgressIndicator();
      return LoginForm();
    },
  ),
)

Как отвечать на собеседовании

  • Начни с краткого перечисления, затем углубляйся в детали по мере вопросов.
  • Подчеркни, что bloc - это не просто state management, а архитектурный паттерн с чёткими правилами.
  • Приведи пример из реального проекта: как использовал EventTransformer для поиска, BlocObserver для аналитики.
  • Упомяни ограничения: много boilerplate, сложность для простых экранов, необходимость продумывать иерархию BlocProvider.
  • Покажи понимание альтернатив: Provider, Riverpod, GetX - и объясни, когда bloc избыточен.

Что проверяет интервьюер

  • Глубину понимания реактивного программирования (Streams, backpressure).
  • Умение проектировать иерархию блоков и их взаимодействие.
  • Знание lifecycle и утечек памяти (когда вызывать close).
  • Практический опыт: как дебажить состояния, как тестировать блоки.
  • Способность объяснить trade-off между BlocBuilder и BlocListener.

Типичные ошибки

  • Путают BlocBuilder и BlocListener - используют listener для rebuild.
  • Забывают про buildWhen/listenWhen - отсюда лишние rebuild.
  • Не переопределяют onError - ошибки молча проглатываются.
  • Используют Equatable неправильно: не переопределяют props или включают изменяемые поля.
  • Создают BlocProvider внутри build - теряют состояние при rebuild.
  • Не закрывают блоки вручную, если используют не BlocProvider (например, в initState).
  • Слишком много блоков на один экран - усложняют поддержку.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью