Skip to content

Recipe: Return a result to the previous screen

Problem: you navigate to a screen (a picker, an editor) and need the value it produces back on the calling screen — surviving process death.

Use the answering navigator. The caller wraps its Navigator with rememberAnsweringNavigator<R> and a callback. The target screen pops a PopResult, and Circuit delivers it only to the caller that asked.

1. Define the result

PopResult extends Parcelable, so results survive process death. @Parcelize is the easy way to satisfy that.

@Parcelize
data object EditNameScreen : Screen

@Parcelize
data class EditNameResult(val name: String) : PopResult

2. Caller: request and receive

@Composable
override fun present(): ProfileState {
  var name by rememberRetained { mutableStateOf("") }

  val editNameNavigator =
    rememberAnsweringNavigator<EditNameResult>(navigator) { result ->
      name = result.name        // delivered here when the target pops
    }

  return ProfileState(name) { event ->
    when (event) {
      ProfileEvent.EditName -> editNameNavigator.goTo(EditNameScreen)
    }
  }
}

3. Target: pop the result

@Composable
override fun present(): EditNameState {
  var draft by rememberRetained { mutableStateOf("") }

  return EditNameState(draft) { event ->
    when (event) {
      is EditNameEvent.TextChanged -> draft = event.text
      EditNameEvent.Save -> navigator.pop(result = EditNameResult(draft))
    }
  }
}

If the target pops without a result (e.g. the user backs out), the callback simply never fires — treat that as “cancelled”.

Screen result vs. overlay

If the thing you’re asking for is a prompt, such as confirm or pick from a sheet, an overlay is usually a better fit. It’s type-safe and suspends on the result, though it doesn’t survive process death. Full comparison in the Overlays doc.

See also: Navigation: results · Ask for confirmation with a dialog