bloc_signals 1.0.0+1 copy "bloc_signals: ^1.0.0+1" to clipboard
bloc_signals: ^1.0.0+1 copied to clipboard

Core pure Dart reactive state container bridging BLoC semantics with signals v7 primitives.

bloc_signals

⚡ bloc_signals

"With the rigor of BLoC and the flex and speed of Signals"

A synchronous state management library bridging the Business Logic Component (BLoC) pattern with a reactive signals foundation (using Rody Davis's signals package v7).

This package provides core pure-Dart reactive state containers (BlocSignalBase, CubitSignal, BlocSignal), event concurrency transformers (Mutex, droppable, sequential, restartable), VM Service telemetry (DevToolsBlocSignalObserver, DevToolsService), and stream interop extensions.


🌐 Ecosystem Packages #

The BlocSignal monorepo consists of 10 modular packages:

Package Version Description
bloc_signals pub Core pure Dart reactive state primitives bridging BLoC & Signals
bloc_signals_flutter pub Flutter UI bindings, providers, builders, listeners & selectors
bloc_signals_jaspr pub Jaspr web component integration and state binding for BlocSignal
bloc_signals_riverpod pub Bidirectional Riverpod 2/3 interop adapters & provider extensions
bloc_signals_hydrate pub Automated synchronous local state persistence & hydration
bloc_signals_replay pub Undo & redo state history tracking for CubitSignal and BlocSignal
bloc_signals_otel pub OpenTelemetry tracing and span generation for state transitions
bloc_signals_devtools pub Universal DevTools telemetry observer using dart:developer
bloc_signals_test pub Declarative unit testing utilities (blocSignalTest)
bloc_signals_lint pub Custom analyzer lint rules & automated IDE quick-fixes

📖 Background & Architecture References #

bloc_signals bridges two foundational state management technologies:

Key Architectural Differences & Design Choices: #

  • Synchronous State Propagation: State changes run synchronously when calling emit(newState) rather than asynchronously on microtask-queue Streams.
  • 🔒 Streamless Concurrency: Support for Mutex, droppable(), sequential(), and restartable() event transformers using pure Dart higher-order functions with zero stream memory allocations.

⚡ Key Features #

  • 🚀 Synchronous Propagation: emit() updates state immediately in the exact same frame without microtask delay.
  • 🎯 Automatic De-duplication: Identical states (== or custom equality) are automatically de-duplicated to prevent unnecessary downstream recalculations.
  • 🔒 Streamless Concurrency: Support for Mutex, droppable(), sequential(), and restartable() event transformers without stream overhead.
  • 🛠️ DevTools & Telemetry: Built-in VM Service RPC extensions (DevToolsService) and standard dart:developer event posting (DevToolsBlocSignalObserver).

🚀 Getting Started #

Add bloc_signals to your pubspec.yaml:

dependencies:
  bloc_signals: ^1.0.0

💡 Quick Examples #

1. CubitSignal (Simple State Management) #

import 'package:bloc_signals/bloc_signals.dart';

class CounterCubit extends CubitSignal<int> {
  CounterCubit() : super(initialState: 0);

  void increment() => emit(stateValue + 1);
  void decrement() => emit(stateValue - 1);
}

void main() {
  final counter = CounterCubit();
  print(counter.stateValue); // 0
  counter.increment();
  print(counter.stateValue); // 1
  counter.close();
}

2. BlocSignal (Event-Driven State Management) #

import 'package:bloc_signals/bloc_signals.dart';

sealed class CounterEvent {}
final class IncrementEvent extends CounterEvent {}
final class DecrementEvent extends CounterEvent {}

class CounterBloc extends BlocSignal<CounterEvent, int> {
  CounterBloc() : super(initialState: 0) {
    on<IncrementEvent>((event, emit) => emit(stateValue + 1));
    on<DecrementEvent>((event, emit) => emit(stateValue - 1));
  }
}

void main() {
  final bloc = CounterBloc();
  bloc.add(IncrementEvent()); // Synchronously transitions state to 1
  print(bloc.stateValue); // 1
  bloc.close();
}

3. Event Concurrency Transformers (droppable, sequential, restartable) #

class AsyncDataBloc extends BlocSignal<DataEvent, DataState> {
  AsyncDataBloc(Repository repo) : super(initialState: DataInitial()) {
    // Drop incoming FetchData events while current handler is active
    on<FetchData>(
      (event, emit) async {
        final data = await repo.load();
        emit(DataLoaded(data));
      },
      transformer: droppable(),
    );
  }
}

4. Custom Equality Comparators #

class UserBloc extends CubitSignal<UserModel> {
  UserBloc(UserModel initial)
      : super(
          initialState: initial,
          equals: (a, b) => a.id == b.id, // Custom property equality
        );
}

5. Stream Interop Extensions #

// Convert any BlocSignal into a Dart Stream
final Stream<int> stream = counterBloc.toStream();

// Convert any Dart Stream into a StreamBlocSignal
final streamBloc = stream.toBlocSignal(initialState: 0);

🏷️ Debug Names, Signal Options & Custom Equality #

All BlocSignalBase containers (CubitSignal, BlocSignal), side-effect handlers (createEffect), and Flutter selectors (BlocSignalSelector) accept explicit options configuration (SignalOptions, EffectOptions, ComputedOptions) and generate descriptive automatic debug names for DevTools inspection.

1. Automatic & Custom Debug Names #

By default, state signals and internal effects are assigned rich diagnostic names in VM Service / DevTools telemetry:

  • State Signal: '$runtimeType.state' (e.g. 'CounterCubit.state')
  • Lifecycle Effect: '$runtimeType.lifecycleEffect'
  • Custom Effects: '$runtimeType.effect#1', '$runtimeType.effect#2'

You can customize debug names using the options: parameter:

final cubit = CounterCubit(
  options: SignalOptions<int>(name: 'CustomCounterCubit.state'),
);

2. Custom Equality & Identity Comparison (identical) #

By default, state updates use standard value equality (previous == current). You can customize state de-duplication strategy using equals: or options:.

💡 FAQ: How do I force Reference Identity Equality (identical)?

To ensure every emit() call triggers a state update regardless of == value equality, pass Dart's built-in identical top-level function tear-off:

// Option A: Passing `identical` tear-off to the constructor
class ForceRebuildCubit extends CubitSignal<StateModel> {
  ForceRebuildCubit(StateModel initial)
      : super(initialState: initial, equals: identical);
}

// Option B: Using SignalOptions.identity()
class IdentityBloc extends CubitSignal<StateModel> {
  IdentityBloc(StateModel initial)
      : super(
          initialState: initial,
          options: SignalOptions(equality: SignalEquality.identity()),
        );
}

⚖️ Equality Evaluation Precedence Order

  1. options.equality (highest priority if specified in SignalOptions)
  2. equals constructor parameter or @override bool equals(...) method
  3. Default value equality (previous == current)

🔍 DevTools & Telemetry Setup #

Enable global DevTools telemetry in main.dart:

void main() {
  // Enables VM Service RPC extensions & developer.postEvent telemetry
  BlocSignalObserver.observer = DevToolsBlocSignalObserver();

  runApp(const MyApp());
}

🤖 AI Coding Assistant Skill & Guides #

This package is supported by official pre-packaged AI Coding Skills and architectural documentation guides representing best practices, lifecycle contracts, and usage patterns for BlocSignal:

  • 🔄 Migration Guide: Transitioning from classic package:bloc / package:flutter_bloc to BlocSignal.
  • 🌁 Universal Interoperability Guide: Bridging state containers across BLoC, Riverpod, Provider, and Listenable primitives.
  • 📦 AI Skill Bundle: Load the pre-packaged bloc-signals skill bundle for AI coding assistants (such as Claude Code, Antigravity, Gemini, Cursor, or Codex) to guide code generation and analysis.

📜 Credits & Acknowledgements #

Inspired by bloc by Felix Angelov and signals by Rody Davis.

11
likes
160
points
1.51k
downloads

Documentation

API reference

Publisher

verified publisherstonehenge.com

Weekly Downloads

Core pure Dart reactive state container bridging BLoC semantics with signals v7 primitives.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

meta, preact_signals, signals_core

More

Packages that depend on bloc_signals