Skip to content

Repository files navigation

ResultBackNavigator

A lightweight, type-safe way to pass results back between screens when navigating with Compose Multiplatform Navigation.

The problem

Compose Navigation has no built-in mechanism for a screen to return data to the screen that launched it. The common workarounds (shared ViewModel, callback lambdas) either add boilerplate or break with the navigation back stack.

The solution

Two interfaces that mirror the producer/consumer relationship:

  • ResultBackNavigator<R> — used by the screen that needs to return a result
  • ResultBackRecipient<R> — used by the screen that receives the result

Results are stored in SavedStateHandle, so they survive process death and configuration changes. Delivery happens on ON_RESUME, which avoids race conditions with the back stack.

Usage

ScreenB — returning a result

@Composable
fun ScreenB(resultBackNavigator: ResultBackNavigator<String>) {
    var text by remember { mutableStateOf("") }

    Button(onClick = { resultBackNavigator.navigateWithResult(text) }) {
        Text("Confirm")
    }
    OutlinedButton(onClick = { resultBackNavigator.navigateBack() }) {
        Text("Cancel")
    }
}

ScreenA — receiving the result

@Composable
fun ScreenA(
    navController: NavController,
    resultRecipient: ResultBackRecipient<String>
) {
    resultRecipient.onResult { result ->
        when (result) {
            is NavResult.Result -> println("Got: ${result.value}")
            is NavResult.Cancelled -> println("Cancelled")
        }
    }
}

Wiring it up in the NavHost

NavHost(navController = navController, startDestination = ScreenA) {
    composable<ScreenA> {
        ScreenA(
            navController = navController,
            resultRecipient = it.provideResultRecipient()
        )
    }
    composable<ScreenB> {
        ScreenB(resultBackNavigator = it.provideResultBackNavigator(navController))
    }
}

NavResult

sealed class NavResult<T> {
    class Result<T>(val value: T) : NavResult<T>()   // navigateWithResult() was called
    object Cancelled : NavResult<Nothing>()           // navigateBack() was called
}

If the user presses the physical back button, neither case fires — handle that separately with BackHandler if needed.

Notes

  • The result type R must be parcelable on Android (primitive types and strings work out of the box).
  • Results are keyed by route, so they won't leak between unrelated screen pairs.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages