BlocEventStatusGenerator

BlocEventStatusGenerator CI pub package pub points pub monthly downloads pub Likes License: MIT

Code generation for bloc_event_status: annotate a Bloc and get a typed Emitter extension, one method per status.

Installation

dart pub add bloc_event_status
dart pub add --dev bloc_event_status_generator build_runner

Overview

Emitting an event status by hand means rebuilding the state on every call:

emit(
  state.copyWith(
    eventStatuses: state.eventStatuses.update(event, const LoadingEventStatus()),
  ),
);

The usual fix is a hand-written Emitter extension (see the bloc_event_status README), which has to be kept in sync with your status type by hand. This package writes that extension for you:

emit.loading(event, state);

Getting Started

Step 1: Annotate your Bloc

Add the part directive and the @blocEventStatus annotation:

import 'package:bloc/bloc.dart';
import 'package:bloc_event_status/bloc_event_status.dart';

part 'todo_bloc.bes.g.dart';

@blocEventStatus
class TodoBloc extends Bloc<TodoEvent, TodoState> {
  TodoBloc() : super(const TodoState()) {
    on<AddTodo>(_onAddTodo);
  }

  Future<void> _onAddTodo(AddTodo event, Emitter<TodoState> emit) async {
    emit.loading(event, state);

    try {
      final todos = await _repository.add(event.title);
      emit.success(event, state.copyWith(todos: todos));
    } on Exception catch (e) {
      emit.failure(event, state, e.toString());
    }
  }
}

Step 2: Run the generator

dart run build_runner build

Or, while developing:

dart run build_runner watch

The builder is applied automatically to any package that depends on it (auto_apply: dependents) and writes *.bes.g.dart next to the source file, so no build.yaml is required in your project.

Requirements

The generator reads everything it needs from your existing types. For a @blocEventStatus-annotated class it expects:

Requirement Why
The class extends Bloc<TEvent, TState> TEvent and TState are read from the supertype.
TState uses EventStatusesMixin<TEvent, TStatus> TStatus is read from the mixin.
TState has a copyWith({eventStatuses}) The generated helper emits state.copyWith(eventStatuses: ...).
TStatus has concrete subtypes in the same library One method is generated per concrete subtype.
A part '<file>.bes.g.dart'; directive The extension is generated as a part of your file.

A complete, runnable setup lives in example/lib/main.dart.

If any of these is missing, the build fails with an explicit message — for example State class must use EventStatusesMixin<TEvent, TStatus>. or No concrete subtypes found for EventStatus.

What Gets Generated

Given this status type and state:

sealed class EventStatus extends Equatable {
  const EventStatus();

  @override
  List<Object?> get props => [];
}

class LoadingEventStatus extends EventStatus {
  const LoadingEventStatus();
}

class SuccessEventStatus extends EventStatus {
  const SuccessEventStatus();
}

class FailureEventStatus extends EventStatus {
  const FailureEventStatus(this.message);
  final String message;

  @override
  List<Object?> get props => [message];
}

class TodoState extends Equatable
    with EventStatusesMixin<TodoEvent, EventStatus> {
  const TodoState({
    this.todos = const [],
    this.eventStatuses = const EventStatuses(),
  });

  final List<String> todos;

  @override
  final EventStatuses<TodoEvent, EventStatus> eventStatuses;

  TodoState copyWith({
    List<String>? todos,
    EventStatuses<TodoEvent, EventStatus>? eventStatuses,
  }) { /* ... */ }

  @override
  List<Object?> get props => [todos, eventStatuses];
}

todo_bloc.bes.g.dart contains:

extension $TodoBlocEmitterX on Emitter<TodoState> {
  void _emitEventStatus<T extends TodoEvent>(
    T event,
    EventStatus status,
    TodoState state,
  ) {
    this(
      state.copyWith(
        eventStatuses: state.eventStatuses.update(event, status),
      ),
    );
  }

  void loading<T extends TodoEvent>(T event, TodoState state) =>
      _emitEventStatus(event, const LoadingEventStatus(), state);

  void success<T extends TodoEvent>(T event, TodoState state) =>
      _emitEventStatus(event, const SuccessEventStatus(), state);

  void failure<T extends TodoEvent>(T event, TodoState state, String message) =>
      _emitEventStatus(event, FailureEventStatus(message), state);
}

Every method takes the event and the state to emit, plus whatever its status constructor needs.

Method names

The method name is the subtype name with the prefix and suffix it shares with the base status type stripped, lowerCamelCased:

Status base type Concrete subtype Method
EventStatus LoadingEventStatus loading
CounterEventStatus LoadingEventStatus loading
CustomEventStatus CustomSuccessEventStatus success

Constructor parameters

Status constructor parameters are forwarded as-is, keeping their kind and defaults:

  • required positional → required positional on the method
  • optional positional (with or without default) → optional positional
  • required named → required named
  • optional named (with or without default) → optional named
  • generic type parameters (including bounds) → declared on the method, forwarded to the constructor
  • a const constructor with no arguments → const instantiation in the generated call

Troubleshooting

Version solving fails on analyzer. Every analyzer release is breaking, so build, build_runner and every generator pin a range. If pub reports that bloc_event_status_generator is forbidden, make sure you are on the latest version of this package — analyzer upper bounds are widened as new releases land. Version 1.1.0 supports analyzer >=8.0.0 <15.0.0 (build_runner up to 2.16.x).

Nothing is generated. Check that the file declares part '<file>.bes.g.dart'; and that the annotated class is a Bloc subclass. dart run build_runner build --verbose shows which builders ran.

--delete-conflicting-outputs is ignored. Recent build_runner versions dropped that option and log a warning when it is passed; plain dart run build_runner build is enough.

VS Code tip

To keep generated *.g.dart files nested under their source file in the Explorer, add this to your .vscode/settings.json:

{
  "explorer.fileNesting.patterns": {
    "*.dart": "${capture}*.g.dart"
  }
}

Libraries

bloc_event_status_generator
Code generator for bloc_event_status emitter extensions.
generator