onRequest<Request, Response> method

EventSubscription onRequest<Request, Response>(
  1. RequestHandler<Request, Response> handler, {
  2. int priority = Priority.normal,
  3. String? identifier,
})

Register a handler for request/response communication.

When request<Request, Response>(...) is called with a matching (Request, Response) type pair (and, optionally, identifier), handlers walk in descending priority. Each handler may:

  • Return a non-null value to claim the call; dispatch stops there.
  • Return null to concede so the next handler can claim.
  • Throw to signal a real error; the chain stops and the exception propagates to the caller unchanged. Subsequent handlers do not run.

Concession by null works whether or not Response is nullable. null is the framework's "I won't answer" signal. Throws are reserved for genuine errors. See request for how the consumer observes the all-conceded case (it depends on which method they call).

Returns an EventSubscription for cancellation.

Example:

final sub = bus.onRequest<SearchQuery, SearchResults>((env) async {
  final hit = await tryProvider(env.event);
  return hit; // null to let the next provider try
}, priority: 10);

Implementation

EventSubscription onRequest<Request, Response>(
  RequestHandler<Request, Response> handler, {
  int priority = Priority.normal,
  String? identifier,
}) {
  _checkNotDisposed();
  final key = (Request, Response);
  final buckets =
      _requestHandlers.putIfAbsent(
            key,
            _RequestBuckets<Request, Response>.new,
          )
          as _RequestBuckets<Request, Response>;
  final List<_RequestEntry<Request, Response>> list = identifier == null
      ? buckets.general
      : buckets.byId.putIfAbsent(
          identifier,
          () => <_RequestEntry<Request, Response>>[],
        );

  final entry = _RequestEntry<Request, Response>(
    priority: priority,
    run: handler,
  );
  list.add(entry);
  if (identifier == null) {
    buckets.markGeneralDirty();
  } else {
    buckets.markIdDirty(identifier);
  }

  return _EventHandlerSub(
    onCancel: () {
      list.remove(entry);
      if (list.isEmpty) {
        if (identifier == null) {
          if (buckets.byId.isEmpty) _requestHandlers.remove(key);
        } else {
          buckets.removeIdBucket(identifier);
          if (buckets.byId.isEmpty && buckets.general.isEmpty) {
            _requestHandlers.remove(key);
          }
        }
      }
    },
  );
}