handle-bloc-event-subclasses

Path
lint/correctness/handle-bloc-event-subclasses
Default
Off, opt in
Origin
Falcon
Domain
bloc
Rule
241 of 512
bloc

Warns when a Bloc constructor leaves a concrete event subclass without a handler.

Register a handler for every event subclass, or register a handler for their parent event. This rule checks event classes declared in the same file and on<Event> registrations in each generative constructor, including cascades. Handler coverage compares full event instantiations and accepts unknown subtyping. Inferred registrations conservatively count as coverage. Factories, redirects, abstract event classes, generic event roots and overridden on APIs are ignored. Calls to instance helpers on this, including inherited helpers, count as unresolved coverage.

Invalid

example.dartdart
import 'package:bloc/bloc.dart';

sealed class Event {}
final class Start extends Event {}
final class Stop extends Event {}

class CounterBloc extends Bloc<Event, int> {
  CounterBloc() : super(0) {
    on<Start>((event, emit) {});
  }
  CounterBloc.empty() : super(0) {}
  CounterBloc.partialCascade() : super(0) {
    this..on<Start>((event, emit) {});
  }
  CounterBloc.otherReceiver(Bloc<Event, int> other) : super(0) {
    other
      ..on<Start>((event, emit) {})
      ..on<Stop>((event, emit) {});
  }
  CounterBloc.localOn() : super(0) {
    void on(Object handler) {}
    on((Event event, Emitter<int> emit) {});
  }
}

abstract class GenericEvent {}
abstract class Value<T> extends GenericEvent {}
class IntValue extends Value<int> {}

class MismatchedValueBloc extends Bloc<GenericEvent, int> {
  MismatchedValueBloc() : super(0) {
    on<Value<String>>((event, emit) {});
  }
}

class UnrelatedHelperBloc extends Bloc<Event, int> {
  UnrelatedHelperBloc.other(UnrelatedHelperBloc other) : super(0) {
    other._registerHandlers();
  }
  UnrelatedHelperBloc.shadowed() : super(0) {
    void _registerHandlers() {}
    _registerHandlers();
  }
  UnrelatedHelperBloc.staticCall() : super(0) {
    _staticHelper();
  }
  UnrelatedHelperBloc.addEvent() : super(0) {
    on<Start>((event, emit) {});
    add(Start());
  }
  UnrelatedHelperBloc.explicitAddEvent() : super(0) {
    on<Start>((event, emit) {});
    this.add(Start());
  }
  void _registerHandlers() {
    on<Start>((event, emit) {});
    on<Stop>((event, emit) {});
  }
  static void _staticHelper() {}
}
This Bloc leaves event subclasses unhandled. Register their handlers or handle the parent event.
7class CounterBloc extends Bloc<Event, int> {
8 CounterBloc() : super(0) {
This Bloc leaves event subclasses unhandled. Register their handlers or handle the parent event.
10 }
11 CounterBloc.empty() : super(0) {}
This Bloc leaves event subclasses unhandled. Register their handlers or handle the parent event.
11 CounterBloc.empty() : super(0) {}
12 CounterBloc.partialCascade() : super(0) {
This Bloc leaves event subclasses unhandled. Register their handlers or handle the parent event.
14 }
15 CounterBloc.otherReceiver(Bloc<Event, int> other) : super(0) {
This Bloc leaves event subclasses unhandled. Register their handlers or handle the parent event.
19 }
20 CounterBloc.localOn() : super(0) {
This Bloc leaves event subclasses unhandled. Register their handlers or handle the parent event.
30class MismatchedValueBloc extends Bloc<GenericEvent, int> {
31 MismatchedValueBloc() : super(0) {
This Bloc leaves event subclasses unhandled. Register their handlers or handle the parent event.
36class UnrelatedHelperBloc extends Bloc<Event, int> {
37 UnrelatedHelperBloc.other(UnrelatedHelperBloc other) : super(0) {
This Bloc leaves event subclasses unhandled. Register their handlers or handle the parent event.
39 }
40 UnrelatedHelperBloc.shadowed() : super(0) {
This Bloc leaves event subclasses unhandled. Register their handlers or handle the parent event.
43 }
44 UnrelatedHelperBloc.staticCall() : super(0) {
This Bloc leaves event subclasses unhandled. Register their handlers or handle the parent event.
46 }
47 UnrelatedHelperBloc.addEvent() : super(0) {
This Bloc leaves event subclasses unhandled. Register their handlers or handle the parent event.
50 }
51 UnrelatedHelperBloc.explicitAddEvent() : super(0) {

Valid

example.dartdart
import 'package:bloc/bloc.dart';

sealed class Event {}
final class Start extends Event {}
final class Stop extends Event {}

class CompleteBloc extends Bloc<Event, int> {
  CompleteBloc() : super(0) {
    on<Start>((event, emit) {});
    on<Stop>((event, emit) {});
  }
  CompleteBloc.parent() : super(0) {
    on<Event>((event, emit) {});
  }
  CompleteBloc.redirect() : this();
  CompleteBloc.inferred() : super(0) {
    on((Event event, Emitter<int> emit) {});
  }
  CompleteBloc.cascade() : super(0) {
    this
      ..on<Start>((event, emit) {})
      ..on<Stop>((event, emit) {});
  }
  CompleteBloc.inferredCascade() : super(0) {
    this..on((Event event, Emitter<int> emit) {});
  }
  CompleteBloc.cascadeWithLocalOn() : super(0) {
    void on<T>(Object handler) {}
    this
      ..on<Start>((event, emit) {})
      ..on<Stop>((event, emit) {});
  }
}
class NotABloc {
  NotABloc();
}

class GenericBloc<Event extends Object> extends Bloc<Event, int> {
  GenericBloc() : super(0) {
    on<Event>((event, emit) {});
  }
}

abstract class GenericEvent {}
abstract class Value<T> extends GenericEvent {}
class IntValue extends Value<int> {}

class MatchingValueBloc extends Bloc<GenericEvent, int> {
  MatchingValueBloc() : super(0) {
    on<Value<int>>((event, emit) {});
  }
  MatchingValueBloc.raw() : super(0) {
    on<Value>((event, emit) {});
  }
  MatchingValueBloc.dynamicArgument() : super(0) {
    on<Value<dynamic>>((event, emit) {});
  }
}

abstract class UnresolvedEvent {}
abstract class UnknownValue<T> extends UnresolvedEvent {}
class FooValue extends UnknownValue<Foo> {}

class UnresolvedValueBloc extends Bloc<UnresolvedEvent, int> {
  UnresolvedValueBloc() : super(0) {
    on<UnknownValue<Foo>>((event, emit) {});
  }
}

abstract class WrappedEvent {}
abstract class WrappedValue<T> extends WrappedEvent {}
class Wrapped<T> extends WrappedValue<T> {}

class WrappedValueBloc extends Bloc<WrappedEvent, int> {
  WrappedValueBloc() : super(0) {
    on<WrappedValue<int>>((event, emit) {});
  }
}

class HelperBloc extends Bloc<Event, int> {
  HelperBloc() : super(0) {
    _registerHandlers();
  }
  HelperBloc.explicitThis() : super(0) {
    this._registerHandlers();
  }
  void _registerHandlers() {
    on<Start>((event, emit) {});
    on<Stop>((event, emit) {});
  }
}

mixin HandlerRegistration on Bloc<Event, int> {
  void _registerHandlers() {
    on<Start>((event, emit) {});
    on<Stop>((event, emit) {});
  }
}

class MixinHelperBloc extends Bloc<Event, int> with HandlerRegistration {
  MixinHelperBloc() : super(0) {
    _registerHandlers();
  }
  MixinHelperBloc.explicitThis() : super(0) {
    this._registerHandlers();
  }
}

Configure handle-bloc-event-subclasses

Set its severity in falcon.json. See the configuration guide.

falcon.jsonjson
{
  "linter": {
    "rules": {
      "correctness": {
        "handle-bloc-event-subclasses": "error"
      }
    }
  }
}

Or silence one occurrence with a suppression comment; the reason is required.

example.dartdart
// falcon-ignore lint/correctness/handle-bloc-event-subclasses: <reason>