NavController crashes with 'No destination on back stack' when navigating to camera route inside nested navigation graph


I have a nested navigation graph (REG_GROUP) that contains registration screens and a photoGraph extension with camera and photo_preview destinations. When I open a bottom sheet and tap "Camera" or select a photo from gallery, the app crashes.

Error:

java.lang.IllegalArgumentException: No destination with ID -864236036 is on the NavController's back stack. 
The current destination is Destination(0x9a9104f2) route=camera
    at androidx.navigation.NavController.getBackStackEntry(NavController.kt:2209)
    at androidx.navigation.NavController.addEntryToBackStack(NavController.kt:1918)
    at androidx.navigation.NavController.navigate(NavController.kt:1984)
    at com.example.repairkz.common.handlers.PhotoPickerHandlerKt.photoPickerHandler$lambda$6$lambda$5(PhotoPickerHandler.kt:58)
    at com.example.repairkz.ui.features.auth.signUp.ui.SignUpLayoutKt$SignUpLayout$1$1$1.emit(SignUpLayout.kt:94)

Steps to reproduce:

  1. Launch app, land on SignUpData screen

  2. Tap avatar button — bottom sheet opens

  3. Select "Camera" → crash

  4. Or: select from Gallery, pick photo, tap "Done" → crash

registrationGraph:

@SuppressLint("UnrememberedGetBackStackEntry")
fun NavGraphBuilder.registrationGraph(navController: NavController) {
    navigation(
        startDestination = SIGN_UP_DATA,
        route = REG_GROUP
    ) {
        composable(route = SIGN_UP_EMAIL) { nbse ->
            val parentEntry = remember(nbse) { navController.getBackStackEntry(REG_GROUP) }
            val signUpViewModel: SignUpViewModel = hiltViewModel(parentEntry)
            SignUpEmail(signUpViewModel, navController)
        }
        composable(route = SIGN_UP_CODE) { nbse ->
            val parentEntry = remember(nbse) { navController.getBackStackEntry(REG_GROUP) }
            val signUpViewModel: SignUpViewModel = hiltViewModel(parentEntry)
            SignUpCode(signUpViewModel, navController)
        }
        composable(route = SIGN_UP_DATA) { nbse ->
            val parentEntry = remember(nbse) { navController.getBackStackEntry(REG_GROUP) }
            val signUpViewModel: SignUpViewModel = hiltViewModel(parentEntry)
            SignUpData(signUpViewModel, navController)
        }
        photoGraph(
            navController = navController,
            getViewModel = { navBackStackEntry ->
                val parentEntry = remember(navBackStackEntry) {
                    navController.getBackStackEntry(REG_GROUP)
                }
                hiltViewModel<SignUpViewModel>(parentEntry)
            }
        )
    }
}

photoGraph:

fun NavGraphBuilder.photoGraph(
    navController: NavController,
    getViewModel: @Composable (NavBackStackEntry) -> CameraCapable
) {
    composable(Routes.CAMERA) { nbse ->
        val vm = getViewModel(nbse)
        val context = LocalContext.current
        Camera(
            context = context,
            takeNewPhoto = { uri ->
                uri?.let {
                    vm.onPhotoSelected(it)
                    navController.popBackStack()
                }
            }
        )
    }
    composable(Routes.PHOTO_PREVIEW) { nbse ->
        val context = LocalContext.current
        val vm = getViewModel(nbse)
        val uri = vm.getPreviewUri()
        uri?.let { nonNullUri ->
            PhotoPreview(
                context,
                uri = nonNullUri,
                onDismissRequest = { navController.popBackStack() },
            ) { uri ->
                uri?.let {
                    vm.onPhotoSelected(it)
                    navController.popBackStack()
                }
            }
        }
    }
}

SignUpLayout (LaunchedEffect):

val action = photoPickerHandler(
    getPhotoFromMedia = { uri ->
        if (uri != null)
            signUpViewModel.handleIntent(SignUpIntent.GetPhotoFromMedia(uri))
    },
    navController = navController,
    context = context
)

LaunchedEffect(Unit) {
    signUpViewModel.channel.collect { effect ->
        val currentRoute = navController.currentBackStackEntry?.destination?.route
        when (effect) {
            SignUpEffect.NavigateToConfirmation -> navController.navigate(SIGN_UP_CODE)
            is SignUpEffect.ShowSnackBar -> snackbarHostState.showSnackbar(effect.message)
            is SignUpEffect.NavigateToFillingData -> navController.navigate(SIGN_UP_DATA)
            is SignUpEffect.NavigateToMainWindow -> navController.navigate(MAIN_WINDOW)
            is SignUpEffect.OpenPhotoPicker -> {
                if (currentRoute != Routes.CAMERA && currentRoute != Routes.PHOTO_PREVIEW) {
                    when (effect.typeOfSelect) {
                        PhotoSourceEnum.CAMERA -> action.launchCamera()
                        PhotoSourceEnum.GALLERY -> action.launchGallery()
                    }
                }
            }
            SignUpEffect.NavigateToPreview -> {
                if (currentRoute != Routes.PHOTO_PREVIEW)
                    navController.navigate(Routes.PHOTO_PREVIEW)
            }
        }
    }
}
ViewModel:
private val _channel = Channel<SignUpEffect>(Channel.BUFFERED)
val channel = _channel.receiveAsFlow()
is SignUpIntent.ChangeAvatar -> {
    viewModelScope.launch {
        _channel.send(SignUpEffect.OpenPhotoPicker(intent.typeOfSelect))
    }
}
is SignUpIntent.GetPhotoFromMedia -> {
    _uiState.value = _uiState.value.copy(
        userInfo = _uiState.value.userInfo.copy(
            photoUri = intent.uri
        )
    )
    viewModelScope.launch {
        _channel.send(NavigateToPreview)
    }
}

The camera destination is registered inside registrationGraph which has route = REG_GROUP, so REG_GROUP should be on the back stack. Why does getBackStackEntry fail to find it?

1
Mar 14 at 2:22 PM
User Avatarm1zetz
#android#kotlin#navigation#android-jetpack-compose

No answer found for this question yet.