Что такое handler в архитектуре Flutter-приложений?

Ответ

В контексте архитектуры Flutter-приложений (например, BLoC, Provider, Riverpod) Handler (или обработчик) — это функция или метод, который отвечает за реакцию на событие или изменение состояния. Он содержит бизнес-логику: обрабатывает пользовательский ввод, выполняет запросы к API, трансформирует данные и emits новое состояние.

Пример Handler'а в паттерне BLoC (с использованием flutter_bloc):

// Событие
class LoginButtonPressed extends AuthEvent {
  final String email;
  final String password;
  LoginButtonPressed({required this.email, required this.password});
}

// BLoC с Handler'ом (метод mapEventToState или on<Event>)
class AuthBloc extends Bloc<AuthEvent, AuthState> {
  final AuthRepository authRepo;

  AuthBloc(this.authRepo) : super(AuthInitial()) {
    // Регистрация Handler'а для события LoginButtonPressed
    on<LoginButtonPressed>(_onLoginButtonPressed);
  }

  // Сам Handler — содержит бизнес-логику
  Future<void> _onLoginButtonPressed(
    LoginButtonPressed event,
    Emitter<AuthState> emit,
  ) async {
    emit(AuthLoading()); // Новое состояние: загрузка
    try {
      final user = await authRepo.login(
        email: event.email,
        password: event.password,
      );
      emit(AuthSuccess(user)); // Новое состояние: успех
    } catch (e) {
      emit(AuthFailure(e.toString())); // Новое состояние: ошибка
    }
  }
}

Роль и характеристики Handler'а:

  • Изоляция логики: Handler инкапсулирует реакцию на конкретное событие, делая код предсказуемым и тестируемым.
  • Асинхронность: Часто выполняет асинхронные операции (сетевые запросы, чтение БД).
  • Чистая функция (в идеале): Для данного события и текущего состояния должен возвращать детерминированное новое состояние. Побочные эффекты (навигация, показ диалогов) обычно выносятся в отдельный слой (например, BlocListener).

Где ещё встречаются Handler'ы:

  • В Riverpod — это методы внутри StateNotifier или логика внутри ref.read/ref.watch в провайдерах.
  • В GetX — это методы внутри GetxController.
  • В обработчиках жестов (GestureDetector.onTap) или жизненного цикла (WidgetsBindingObserver).

Ответ 18+ 🔞

Блин, слушай, вот объясняю как есть. Представь, что твой Flutter-приложуха — это такой забегаловка. А Handler — это тот самый повар на кухне, который, когда официант крикнул «заказ на бургер!», начинает его, блядь, готовить. То есть, он реагирует на событие и делает всю основную работу.

Вот смотри на примере этого BLoC, там всё как на ладони. Ты жмёшь кнопку логина — это событие. А Handler — это тот кусок кода, который в этот момент просыпается и начинает орать «ёпта, работа пошла!».

// Это событие — типа крик из зала: "Эй, чел, залогинь меня!"
class LoginButtonPressed extends AuthEvent {
  final String email;
  final String password;
  LoginButtonPressed({required this.email, required this.password});
}

// А это сам BLoC — кухня. В нём регистрируем нашего повара-хендлера.
class AuthBloc extends Bloc<AuthEvent, AuthState> {
  final AuthRepository authRepo;

  AuthBloc(this.authRepo) : super(AuthInitial()) {
    // Вот тут говорим: когда придёт событие LoginButtonPressed, дерни-ка ты метод _onLoginButtonPressed
    on<LoginButtonPressed>(_onLoginButtonPressed);
  }

  // А ВОТ И ОН! Сам Handler, звезда кухни, ёбаный гений логики.
  Future<void> _onLoginButtonPressed(
    LoginButtonPressed event,
    Emitter<AuthState> emit,
  ) async {
    emit(AuthLoading()); // Первым делом кричим на весь зал: "Ждите, ща будет!"
    try {
      // А теперь делаем самую важную хуйню — лезем в апишку
      final user = await authRepo.login(
        email: event.email,
        password: event.password,
      );
      emit(AuthSuccess(user)); // Если всё заебись — выносим готовый заказ: "Вот ваш юзер, халявно!"
    } catch (e) {
      emit(AuthFailure(e.toString())); // Если всё пиздец — выносим подгоревшую хрень: "Ой, всё, ошибка!"
    }
  }
}

И в чём же, блядь, его сила, этого Handler'а?

  • Он один за всё в ответе. Типа солдат-одиночка. Получил событие — и пошёл его ебашить. Не размазывает логику по всему коду. Тестировать его — одно удовольствие, подсовывай ему любые данные и смотри, не обосрётся ли он.
  • Любит повисеть в ожидании. Часто ему надо сходить в сеть или в базу, а это, мать его, асинхронные операции. Поэтому он async и делает await, чтобы не тормозить весь интерфейс.
  • В идеале — он чистюля. Дал ему одно и то же событие и состояние — получишь один и тот же результат. Как химическая реакция, ёпта. А всякие побочные эффекты вроде навигации или показа всплывашек — это уже не его епархия, для этого есть другие ребята (типа BlocListener).

Да и вообще, чувак, они везде! В Riverpod'е — это методы внутри StateNotifier или та логика, что в провайдерах крутится. В GetX — методы в контроллерах. Да даже обычный onTap у кнопки — это, по сути, простейший хендлер, ядрёна вошь! Всё один принцип: произошло что-то — вызови эту функцию.