bloc_signals 1.0.0+1
bloc_signals: ^1.0.0+1 copied to clipboard
Core pure Dart reactive state container bridging BLoC semantics with signals v7 primitives.
|
⚡ 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 |
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:
📖 Background & Architecture References #
bloc_signals bridges two foundational state management technologies:
- BLoC Architecture (bloclibrary.dev): Business Logic Component event-driven state machines, state decoupling, and global lifecycle observation (
BlocSignalObserver). - Signals Primitives (signals.dart): Rody Davis's signals v7 reactive primitives providing fine-grained dependency tracking and zero-latency value holding.
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(), andrestartable()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(), andrestartable()event transformers without stream overhead. - 🛠️ DevTools & Telemetry: Built-in VM Service RPC extensions (
DevToolsService) and standarddart:developerevent 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
options.equality(highest priority if specified inSignalOptions)equalsconstructor parameter or@override bool equals(...)method- 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_bloctoBlocSignal. - 🌁 Universal Interoperability Guide: Bridging state containers across BLoC, Riverpod, Provider, and Listenable primitives.
- 📦 AI Skill Bundle: Load the pre-packaged
bloc-signalsskill 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.