HydratedBloc

abstract class HydratedBloc<S : Any, E : Any>(initialState: S, serializer: KSerializer<S>, storageKeyParam: String? = null, storage: HydratedStorage = HydratedBloc.storage) : Bloc<S, E> , AnyHydratedBloc

A Bloc subclass that automatically persists its state to HydratedStorage on every emit and restores it the next time the Bloc is instantiated.

Setup

Assign the storage backend once at app startup before any HydratedBloc is created:

class App : Application() {
override fun onCreate() {
super.onCreate()
HydratedBloc.storage = SharedPreferencesStorage(this)
}
}

Usage

@Serializable
data class CounterState(val count: Int = 0)

class CounterBloc : HydratedBloc<CounterState, CounterEvent>(
initialState = CounterState(),
serializer = CounterState.serializer(),
) {
init {
on<CounterEvent.Increment> { _, emit -> emit(state.copy(count = state.count + 1)) }
on<CounterEvent.Decrement> { _, emit -> emit(state.copy(count = state.count - 1)) }
}
}

The count now survives app restarts with no extra code.

Storage key

Defaults to the class simple name (e.g. "CounterBloc"). Override for a stable, refactor-proof key:

override val storageKey get() = "counter_v2"

Resetting state

MethodStorageIn-memory stateUse case
clearStoredStateDeletedUnchangedWipe storage; visible next launch
resetToInitialStateDeleted + re-written with initialStateSet immediatelyInstant full reset

Custom storage (e.g. for tests)

val bloc = CounterBloc(storage = InMemoryStorage())

Parameters

initialState

Fallback used when no persisted data is found.

serializer

kotlinx.serialization serializer for S. Use MyState.serializer() (generated by the @Serializable annotation).

storage

Storage backend. Defaults to HydratedBloc.storage.

Type Parameters

S

State type — must be annotated with @Serializable.

E

Event type.

Constructors

Link copied to clipboard
constructor(initialState: S, serializer: KSerializer<S>, storageKeyParam: String? = null, storage: HydratedStorage = HydratedBloc.storage)

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard
open override val errorsFlow: SharedFlow<Throwable>

Hot SharedFlow of errors surfaced via addError().

Link copied to clipboard
val eventsFlow: SharedFlow<E>

Hot stream of every dispatched event, in arrival order.

Link copied to clipboard
open override val isClosed: Boolean

true after close has been called. Further emissions become no-ops.

Link copied to clipboard
open override val state: S

The current state snapshot.

Link copied to clipboard
open override val stateFlow: StateFlow<S>

Hot StateFlow that replays the latest state to new collectors.

Link copied to clipboard

The key under which this Bloc's state is persisted. Resolved at construction time to avoid the leaking-this trap with open val.

Functions

Link copied to clipboard
fun addError(error: Throwable)

Signals an error without encoding it into the state type. Broadcasts to errorsFlow, then calls onError and BlocObserver.onError.

Link copied to clipboard
open override fun clearStoredState()

Deletes the persisted state without changing the current in-memory state. The next app launch will start from initialState.

Link copied to clipboard
open override fun close()

Closes this Bloc: cancels the scope, cancels all active jobs, and marks it closed. After calling close, send and emit become no-ops.

Link copied to clipboard
open override fun emit(nextState: S)

Emits nextState to the state stream and persists it to storage.

Link copied to clipboard
open fun onChange(change: Change<S>)

Called after every emit. Always call super.onChange(change) to forward to BlocObserver.onChange.

Link copied to clipboard
open fun onClose()

Called when close completes. Cancel any background coroutines here. Always call super.onClose() to forward to BlocObserver.onClose.

Link copied to clipboard
open fun onError(error: Throwable)

Called whenever addError is invoked. Always call super.onError(error) to forward to BlocObserver.onError.

Link copied to clipboard
open fun onEvent(event: E)

Called immediately before an event is dispatched to its handler. Always call super.onEvent(event) to forward to BlocObserver.onEvent.

Link copied to clipboard
open fun onTransition(transition: Transition<E, S>)

Called after a synchronous emit that has an active event context. Always call super.onTransition(transition) to forward to BlocObserver.onTransition.

Link copied to clipboard
open override fun resetToInitialState()

Deletes the persisted state and immediately emits initialState, resetting the UI right now without restarting the app.

Link copied to clipboard
fun send(event: E)

Dispatches event to the first registered handler whose predicate matches.