BlocSignal
Start from installed source
BlocSignal is young and its API may change between releases. Inspect the consumer project's
pubspec.yaml, lockfile, imports, and installed package source before editing code. Use this
repository only when the project follows its current branch or the task concerns the repository
itself.
Do not infer API parity from package:bloc, package:flutter_bloc, or Riverpod. BlocSignal uses
signals, has no state stream or provider dependency graph, and does not implement every source
package lifecycle, predicate, or widget API.
Related Signals skills
This plugin is self-contained for BlocSignal work. When the agent already has skills from
rodydavis/signals.dart, use these optional companions for lower-level Signals tasks:
signals-dart for signals_core primitives and utilities.
signals-flutter for Flutter bindings and signal-aware widgets.
signals-hooks for flutter_hooks integration.
signals-lint for analyzer rules and quick fixes.
signals-migration-6-to-7 for Signals 6 to 7 migrations.
Do not assume these skills are installed. If they are unavailable, inspect the consumer project's
installed Signals source before changing code.
Route the task
- Read decision_matrix.md for the architectural decision rubric comparing raw Signals, CubitSignal, BlocSignal, Hydrate, and Replay.
- Read core.md for event dispatch, equality, errors, closure, observers, and reactive
ownership.
- Read flutter.md for providers, builders, listeners, multi-listeners, consumers,
selectors, context extensions, infinite scroll pagination recipes,
flutter_hooks integration (signals_hooks), widget ownership, and derived UI state.
- Read testing.md for synchronous assertions, deterministic async tests, zones, and
widget tests.
- Read migration.md before replacing
bloc, flutter_bloc, bloc_concurrency, hydrated_bloc, replay_bloc, or bloc_test.
- Read migration_bridge.md for Stream and Listenable interop bridges.
- Read riverpod_migration.md before replacing Riverpod providers,
consumers, families, scopes, or generated declarations.
- Read interoperability.md for the universal state bridge across BLoC, Riverpod, and Provider ecosystems.
- Read hydration.md for persistent state storage (
bloc_signals_hydrate).
- Read replay.md for undo and redo state tracking (
bloc_signals_replay).
- Read jaspr.md for Jaspr web component integration (
bloc_signals_jaspr).
- Read devtools.md for VM Service RPC extensions & DevTools UI (
bloc_signals_devtools).
- Read lint.md for analyzer rules and IDE diagnostics (
bloc_signals_lint).
- Read otel.md for
OtelBlocSignalObserver, span completion gaps, and telemetry data
choices.
Load only the references needed for the task.
Workflow
- Identify the exact package versions and target platform.
- Trace the current event, state, ownership, and disposal path before changing it.
- Check the installed public API for every type or member you plan to use.
- Make the smallest coherent change that preserves existing user behavior.
- Format changed Dart files and run scoped analysis plus the nearest tests.
- Report the versions inspected, checks run, and any unsupported source behavior that remains.
Contracts to preserve
BlocSignalBase<State> owns state and lifecycle. Use CubitSignal<State> for public methods and
BlocSignal<Event, State> for event dispatch. Use CubitSignalMixin<State> or BlocSignalMixin<Event, State> when a class already extends an existing superclass (such as ChangeNotifier, TextEditingController, or BaseRepository) and invoke initCubitSignal(initialState: ...) in the constructor.
- Constructors require named parameter
initialState: (for example : super(initialState: initial)), NOT positional super(initial).
- Use
stateValue to read raw StateType values inside methods/handlers (such as emit(stateValue + 1)). state returns ReadonlySignal<StateType> for reactive signal subscriptions.
emit changes state synchronously and skips a value equal to the current state.
BlocSignal.add returns void. Synchronous handlers finish before it returns. Async handler
futures are observed for errors but are not returned or cancelled by close.
on<E> registration is runtime routing. Duplicate exact types throw StateError; registration
does not give sealed-class exhaustiveness. An onEvent override must call super.onEvent.
close returns Future<void>, disposes effects registered through createEffect, and disposes
the internal model. New events are dropped after closure. A post-close emit asserts in debug
mode and returns without updating state in release mode.
- A non-equal event transition runs before state mutation.
onChange runs after mutation. Both
local hooks require super; equal emits run neither hook.
BlocSignalProvider(create:) is lazy by default, owns its bloc, and closes it. Use lazy: false
for eager creation. BlocSignalProvider.value does not own its bloc. Both BlocSignalProvider
and BlocSignalListener support an optional child parameter (defaulting to const SizedBox.shrink() in Flutter and const _NullComponent() in Jaspr), eliminating placeholder child arguments inside MultiBlocSignalProvider or MultiBlocSignalListener.
context.watch<T>() tracks provider replacement, not state changes. Use BlocSignalBuilder or a
signals widget to rebuild for state.
BlocSignalListener suppresses its initial effect run and supports listenWhen(previous, current). It passes only the current state to the listener callback.
- A global
BlocSignalObserver is a single slot. Installing a telemetry observer can replace an
existing logger unless the application composes them.
BlocSignalBase overrides toString() to output $runtimeType($stateValue), providing immediate diagnostic visibility across all CubitSignal and BlocSignal subclasses.
- Prefer inline
late final field declarations with type inference for computed properties (for example late final itemCount = computed(() => stateValue.items.length);) rather than uninitialized field declarations with constructor-body assignments.
Never create an effect or computed during a Flutter build method. Keep its owner and disposal
path explicit.
1---2name: bloc-signals3description: Implement, review, test, or debug Dart and Flutter code that uses bloc_signals, bloc_signals_flutter, or bloc_signals_otel. Use for BlocSignalBase, BlocSignal, CubitSignal, CubitSignalMixin, BlocSignalMixin, Change, Transition, event handlers, createEffect ownership, synchronous state updates, lazy providers, builders, listeners, consumers, selectors, context.select, lifecycle observers, and package-specific test failures. Also use when comparing BlocSignal with package:bloc or Riverpod; read the matching migration reference before changing an existing application.4---56# BlocSignal78## Start from installed source910BlocSignal is young and its API may change between releases. Inspect the consumer project's11`pubspec.yaml`, lockfile, imports, and installed package source before editing code. Use this12repository only when the project follows its current branch or the task concerns the repository13itself.1415Do not infer API parity from `package:bloc`, `package:flutter_bloc`, or Riverpod. BlocSignal uses16signals, has no state stream or provider dependency graph, and does not implement every source17package lifecycle, predicate, or widget API.1819## Related Signals skills2021This plugin is self-contained for BlocSignal work. When the agent already has skills from22`rodydavis/signals.dart`, use these optional companions for lower-level Signals tasks:2324- `signals-dart` for `signals_core` primitives and utilities.25- `signals-flutter` for Flutter bindings and signal-aware widgets.26- `signals-hooks` for `flutter_hooks` integration.27- `signals-lint` for analyzer rules and quick fixes.28- `signals-migration-6-to-7` for Signals 6 to 7 migrations.2930Do not assume these skills are installed. If they are unavailable, inspect the consumer project's31installed Signals source before changing code.3233## Route the task3435- Read [decision_matrix.md](decision_matrix.md) for the architectural decision rubric comparing raw Signals, CubitSignal, BlocSignal, Hydrate, and Replay.36- Read [core.md](core.md) for event dispatch, equality, errors, closure, observers, and reactive37 ownership.38- Read [flutter.md](flutter.md) for providers, builders, listeners, multi-listeners, consumers,39 selectors, context extensions, infinite scroll pagination recipes, `flutter_hooks` integration (`signals_hooks`), widget ownership, and derived UI state.40- Read [testing.md](testing.md) for synchronous assertions, deterministic async tests, zones, and41 widget tests.42- Read [migration.md](migration.md) before replacing `bloc`, `flutter_bloc`, `bloc_concurrency`, `hydrated_bloc`, `replay_bloc`, or `bloc_test`.43- Read [migration_bridge.md](migration_bridge.md) for Stream and Listenable interop bridges.44- Read [riverpod_migration.md](riverpod_migration.md) before replacing Riverpod providers,45 consumers, families, scopes, or generated declarations.46- Read [interoperability.md](interoperability.md) for the universal state bridge across BLoC, Riverpod, and Provider ecosystems.47- Read [hydration.md](hydration.md) for persistent state storage (`bloc_signals_hydrate`).48- Read [replay.md](replay.md) for undo and redo state tracking (`bloc_signals_replay`).49- Read [jaspr.md](jaspr.md) for Jaspr web component integration (`bloc_signals_jaspr`).50- Read [devtools.md](devtools.md) for VM Service RPC extensions & DevTools UI (`bloc_signals_devtools`).51- Read [lint.md](lint.md) for analyzer rules and IDE diagnostics (`bloc_signals_lint`).52- Read [otel.md](otel.md) for `OtelBlocSignalObserver`, span completion gaps, and telemetry data53 choices.5455Load only the references needed for the task.5657## Workflow58591. Identify the exact package versions and target platform.602. Trace the current event, state, ownership, and disposal path before changing it.613. Check the installed public API for every type or member you plan to use.624. Make the smallest coherent change that preserves existing user behavior.635. Format changed Dart files and run scoped analysis plus the nearest tests.646. Report the versions inspected, checks run, and any unsupported source behavior that remains.6566## Contracts to preserve6768- `BlocSignalBase<State>` owns state and lifecycle. Use `CubitSignal<State>` for public methods and69 `BlocSignal<Event, State>` for event dispatch. Use `CubitSignalMixin<State>` or `BlocSignalMixin<Event, State>` when a class already extends an existing superclass (such as `ChangeNotifier`, `TextEditingController`, or `BaseRepository`) and invoke `initCubitSignal(initialState: ...)` in the constructor.70- Constructors require named parameter `initialState:` (for example `: super(initialState: initial)`), NOT positional `super(initial)`.71- Use `stateValue` to read raw `StateType` values inside methods/handlers (such as `emit(stateValue + 1)`). `state` returns `ReadonlySignal<StateType>` for reactive signal subscriptions.72- `emit` changes state synchronously and skips a value equal to the current state.73- `BlocSignal.add` returns `void`. Synchronous handlers finish before it returns. Async handler74 futures are observed for errors but are not returned or cancelled by `close`.75- `on<E>` registration is runtime routing. Duplicate exact types throw `StateError`; registration76 does not give sealed-class exhaustiveness. An `onEvent` override must call `super.onEvent`.77- `close` returns `Future<void>`, disposes effects registered through `createEffect`, and disposes78 the internal model. New events are dropped after closure. A post-close `emit` asserts in debug79 mode and returns without updating state in release mode.80- A non-equal event transition runs before state mutation. `onChange` runs after mutation. Both81 local hooks require `super`; equal emits run neither hook.82- `BlocSignalProvider(create:)` is lazy by default, owns its bloc, and closes it. Use `lazy: false`83 for eager creation. `BlocSignalProvider.value` does not own its bloc. Both `BlocSignalProvider`84 and `BlocSignalListener` support an optional `child` parameter (defaulting to `const SizedBox.shrink()` in Flutter and `const _NullComponent()` in Jaspr), eliminating placeholder `child` arguments inside `MultiBlocSignalProvider` or `MultiBlocSignalListener`.85- `context.watch<T>()` tracks provider replacement, not state changes. Use `BlocSignalBuilder` or a86 signals widget to rebuild for state.87- `BlocSignalListener` suppresses its initial effect run and supports `listenWhen(previous,88 current)`. It passes only the current state to the listener callback.89- A global `BlocSignalObserver` is a single slot. Installing a telemetry observer can replace an90 existing logger unless the application composes them.91- `BlocSignalBase` overrides `toString()` to output `$runtimeType($stateValue)`, providing immediate diagnostic visibility across all `CubitSignal` and `BlocSignal` subclasses.92- Prefer inline `late final` field declarations with type inference for computed properties (for example `late final itemCount = computed(() => stateValue.items.length);`) rather than uninitialized field declarations with constructor-body assignments.9394Never create an `effect` or `computed` during a Flutter `build` method. Keep its owner and disposal95path explicit.