bloc_signals_hydrate 0.1.4
bloc_signals_hydrate: ^0.1.4 copied to clipboard
State persistence and hydration adapters for BlocSignal state containers.
β‘ bloc_signals_hydrate #
"With the rigor of Bloc and the flex and speed of Signal"
State persistence and hydration adapters for BlocSignal state containers.
HydratedCubitSignal and HydratedBlocSignal automatically persist state changes to storage and restore state synchronously during container instantiation across app restarts.
π Ecosystem Packages #
| Package | Purpose | Pub.dev Link |
|---|---|---|
bloc_signals |
Core pure-Dart state containers, event registry, & VM Service telemetry | π¦ pub.dev |
bloc_signals_flutter |
Flutter UI widgets (BlocSignalProvider, BlocSignalBuilder, BlocSignalListener, BlocSignalConsumer, BlocSignalSelector) |
π¦ pub.dev |
bloc_signals_riverpod |
Bidirectional Riverpod interop adapters (toBlocSignal(ref), toProvider()) |
π¦ pub.dev |
bloc_signals_hydrate |
Persistent state storage (HydratedCubitSignal, HydratedBlocSignal) |
π¦ pub.dev |
bloc_signals_devtools |
Dedicated Flutter DevTools extension inspector UI | π¦ pub.dev |
bloc_signals_test |
Declarative unit testing helpers (blocSignalTest) |
π¦ pub.dev |
bloc_signals_lint |
Static analysis lints & IDE quick-fixes | π¦ pub.dev |
otel_bloc_signals |
OpenTelemetry tracing observers | π¦ pub.dev |
β‘ Key Features #
- π¦
dynamic/Object?JSON Support:fromJson(dynamic json)andtoJson(StateType state)accept primitives (num,String,bool,List,Map). Primitive states do not require map wrappers like{"value": 42}! - β‘ Synchronous Initial Hydration: State is restored synchronously during constructor executionβmeaning initial widget builds render hydrated data immediately with zero frame flicker.
- π οΈ Zero-Dependency Default: Ships with
MemoryHydratedStoragefor fast in-memory unit testing out-of-the-box.
π Getting Started #
Add bloc_signals_hydrate to your pubspec.yaml:
dependencies:
bloc_signals: ^0.2.5
bloc_signals_hydrate: ^0.1.1
π‘ Quick Examples #
1. Primitive State Hydration (HydratedCubitSignal) #
import 'package:bloc_signals_hydrate/bloc_signals_hydrate.dart';
class CounterCubit extends HydratedCubitSignal<int> {
CounterCubit() : super(initialState: 0);
void increment() => emit(stateValue + 1);
@override
int? fromJson(dynamic json) => json as int?;
@override
dynamic toJson(int state) => state; // Return primitive directly!
}
2. Complex Object Hydration (HydratedBlocSignal) #
import 'package:bloc_signals_hydrate/bloc_signals_hydrate.dart';
class UserCubit extends HydratedCubitSignal<UserModel> {
UserCubit() : super(initialState: UserModel.anonymous);
@override
UserModel? fromJson(dynamic json) {
if (json is Map<String, dynamic>) {
return UserModel.fromJson(json);
}
return null;
}
@override
dynamic toJson(UserModel state) => state.toJson();
}
3. Wiring Custom Storage (SharedPreferences) #
import 'dart:convert';
import 'package:bloc_signals_hydrate/bloc_signals_hydrate.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SharedPreferencesHydratedStorage implements HydratedStorage {
SharedPreferencesHydratedStorage(this.prefs);
final SharedPreferences prefs;
@override
dynamic read(String key) {
final value = prefs.getString(key);
return value != null ? jsonDecode(value) : null;
}
@override
Future<void> write(String key, dynamic value) async =>
prefs.setString(key, jsonEncode(value));
@override
Future<void> delete(String key) async => prefs.remove(key);
@override
Future<void> clear() async => prefs.clear();
}
void main() async {
final prefs = await SharedPreferences.getInstance();
HydratedStorage.storage = SharedPreferencesHydratedStorage(prefs);
}
βοΈ HydratedCubitSignal vs PersistentSignal (signals.dart) #
If you are evaluating state persistence approaches, both bloc_signals_hydrate and signals.dart's native PersistentSignal provide state persistence, but cater to different architectural patterns:
| Feature | HydratedCubitSignal / HydratedBlocSignal |
PersistentSignal (signals.dart) |
|---|---|---|
| Architecture Pattern | BLoC container pattern (fromJson / toJson) |
Raw key-value signal primitive |
| Hydration Timing | Synchronous during constructor execution (zero frame flicker) | Asynchronous or synchronous depending on adapter |
| Observer Telemetry | Integrated into BLoC onError / onChange observer pipeline |
Handled per-signal or via storage callbacks |
| Primitive Support | Direct primitive return (toJson(int state) => state) |
Value adapter layers |
Interoperability: Bridging PersistentSignal into BlocSignal #
If you already use PersistentSignal from package:signals, you can easily bridge it into a CubitSignal using an effect():
class CounterCubit extends CubitSignal<int> {
CounterCubit(this.persistent) : super(initialState: persistent.value) {
// Sync updates from PersistentSignal into Cubit state
effect(() => emit(persistent.value));
}
final PersistentSignal<int> persistent;
void increment() => persistent.value++;
}
π€ AI Coding Assistant Skill #
This package is supported by an official pre-packaged AI Coding Skill representing state persistence guidelines, synchronous initial hydration semantics, and storage adapter rules for BlocSignal.
If you develop with AI coding assistants (such as Claude Code, Antigravity, Gemini, Cursor, or Codex), you can load the bloc-signals skill bundle to guide your assistant's code generation and analysis.
π License #
MIT License. See LICENSE for details.