BlocListener

fun <B : StateEmitter<S>, S : Any> BlocListener(bloc: B, listenWhen: (previous: S, current: S) -> Boolean? = null, listener: (state: S) -> Unit, content: @Composable () -> Unit = {})

A composable that reacts to StateEmitter state changes as side effects, without causing content to rebuild.

Use BlocListener whenever a state change should trigger a side effect — showing a snackbar, playing a sound, navigating to another screen — but must not cause the surrounding UI to recompose.

Overview

BlocListener(
bloc = scoreBloc,
listenWhen = { _, new -> new > 0 && new % 5 == 0 },
listener = { state -> showMilestoneBanner("🎯 ${state} points!") },
) {
ScoreView()
}

listenWhen

Provide a listenWhen predicate to restrict the listener to specific transitions. Both previous and current states are available:

BlocListener(
bloc = authBloc,
listenWhen = { prev, curr -> prev.isAuthenticated != curr.isAuthenticated },
listener = { state ->
if (state.isAuthenticated) navController.navigate("home")
else navController.navigate("login")
},
) {
LoginForm()
}

When listenWhen is omitted, listener is called on every state change.

Side-effect only

The listener closure runs on the main thread. It is safe to perform Compose state mutations inside it (e.g. updating a mutableStateOf to show a dialog).

listener is not called for the initial state — only for subsequent changes.

Combining with BlocBuilder

Nest a BlocBuilder inside content when you need both selective rebuilds and side effects:

BlocListener(
bloc = authBloc,
listenWhen = { prev, curr -> prev.isAuthenticated != curr.isAuthenticated },
listener = { state -> if (state.isAuthenticated) navController.navigate("home") },
) {
BlocBuilder(authBloc) { state -> LoginForm(state) }
}

Parameters

bloc

The StateEmitter to observe.

listenWhen

Optional predicate (previous, current) -> Boolean. Called on every emission; the listener fires only when this returns true. The previous value is the last emitted state, regardless of whether the listener fired.

listener

Side-effect callback invoked with the new state. Never invoked for the initial state.

content

The composable subtree to render. Never recomposed in response to state changes from this listener.


fun <B : StateEmitter<S>, S : Any> BlocListener(blocClass: KClass<B>, listenWhen: (previous: S, current: S) -> Boolean? = null, listener: (state: S) -> Unit, content: @Composable () -> Unit = {})

Overload that resolves the bloc from BlocRegistry by KClass.

BlocListener(
blocClass = ScoreBloc::class,
listenWhen = { _, new -> new.score % 10 == 0 },
listener = { playChime() },
) {
ScoreContent()
}