Sparround

Bloc: on<Event>, emit və handler qaydaları

Bloc — sənədin ifadəsi ilə state dəyişikliklərini funksiyalar yerinə event-lərlə tetikləyən daha inkişaf etmiş sinifdir. Struktur belədir:

  • Event sinifləri elan olunur (adətən sealed class + alt siniflər).
  • Konstruktorda hər event tipi üçün handler qeyd olunur: on<CounterIncrementPressed>((event, emit) => emit(state + 1));
  • UI event əlavə edir: context.read<CounterBloc>().add(CounterIncrementPressed()).

Handler-lər haqqında praktik qaydalar:

  • Bir event tipi üçün bir handler qeyd olunur; eyni tip üçün ikinci on<T> çağırışı səhvdir.
  • emit handler-in daxilində, await-dən sonra da işləyir; lakin handler bitdikdən sonra emit-i saxlayıb istifadə etmək olmaz.
  • Stream-i state-ə çevirmək üçün emit.forEachemit.onEach var — abunəlik handler ilə birlikdə idarə olunur.
dart
// Event-lər keçmiş zamanda adlandırılır (rəsmi konvensiya).
sealed class TodoEvent {}

final class TodoStarted extends TodoEvent {}

final class TodoAdded extends TodoEvent {
  TodoAdded(this.title);
  final String title;
}

final class TodoDeleted extends TodoEvent {
  TodoDeleted(this.id);
  final String id;
}

class TodoBloc extends Bloc<TodoEvent, TodoState> {
  TodoBloc(this._repository) : super(const TodoState()) {
    on<TodoStarted>(_onStarted);
    on<TodoAdded>(_onAdded);
    on<TodoDeleted>(_onDeleted);
  }

  final TodoRepository _repository;

  Future<void> _onStarted(TodoStarted event, Emitter<TodoState> emit) async {
    emit(state.copyWith(status: TodoStatus.loading));
    try {
      final todos = await _repository.fetchAll();
      emit(state.copyWith(status: TodoStatus.success, todos: todos));
    } catch (error) {
      emit(state.copyWith(status: TodoStatus.failure, error: '$error'));
    }
  }

  Future<void> _onAdded(TodoAdded event, Emitter<TodoState> emit) async {
    await _repository.create(event.title);
    add(TodoStarted()); // yenidən yükləmə üçün event əlavə etmək məqbul yanaşmadır
  }

  Future<void> _onDeleted(TodoDeleted event, Emitter<TodoState> emit) async {
    emit(state.copyWith(
      todos: state.todos.where((t) => t.id != event.id).toList(),
    ));
    await _repository.remove(event.id);
  }
}

Sealed event-lər, handler-lər və async iş.

dart
class ChatBloc extends Bloc<ChatEvent, ChatState> {
  ChatBloc(this._repository) : super(const ChatState()) {
    on<ChatSubscriptionRequested>(_onSubscriptionRequested);
  }

  final ChatRepository _repository;

  Future<void> _onSubscriptionRequested(
    ChatSubscriptionRequested event,
    Emitter<ChatState> emit,
  ) async {
    // Stream-in hər elementi state-ə çevrilir; abunəlik handler ilə
    // birlikdə bağlanır — əl ilə StreamSubscription saxlamağa ehtiyac yoxdur.
    await emit.forEach<List<Message>>(
      _repository.watchMessages(),
      onData: (messages) => state.copyWith(messages: messages),
      onError: (error, stackTrace) => state.copyWith(error: '$error'),
    );
  }
}

`emit.forEach` — stream-i state-ə çevirmək.

Rəsmi adlandırma konvensiyaları (böyük komandalar üçün xüsusilə tövsiyə olunur):

  • Event-lər keçmiş zamanda adlandırılır, çünki bloc üçün onlar artıq baş vermiş hadisələrdir: BlocSubject + isim (opsional) + fel. İlk yükləmə event-i: BlocSubject + Started. Baza event sinfi: BlocSubject + Event.
  • State-lər isimdir, çünki state müəyyən andaki mənzərədir. Alt siniflərlə göstərildikdə: BlocSubject + fel + State, burada StateInitial, Success, Failure və ya InProgress.

📚 Mənbələr və sənədlər