Search code examples
androidkotlinnavigationandroid-jetpack-compose

Can't parse the List<String> data Screen A to Screen B. Using same Viewmodel in both screens


Problem statement: I'm expecting that from this code I can share List data first screen to another screen.

Stacktrace

java.lang.IllegalArgumentException: Navigation destination that matches request NavDeepLinkRequest{ uri=android-app://androidx.navigation/post/filter/SnapshotStateList(value=[/storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/WhatsApp Images/IMG-20240201-WA0010.jpg, /storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/WhatsApp Video/VID-20240210-WA0001.mp4, /storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/WhatsApp Video/VID-20240210-WA0002.mp4, /storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/WhatsApp Video/VID-20240210-WA0000.mp4])@57386330 } cannot be found in the navigation graph ComposeNavGraph(0x0) startDestination={Destination(0x78d845ec) route=home} at androidx.navigation.NavController.navigate(NavController.kt:1815) at androidx.navigation.NavController.navigate(NavController.kt:2221) at androidx.navigation.NavController.navigate$default(NavController.kt:2216) at com.sociallab.feature.post.createPost.PostCreateScreenKt$PostCreateScreen$1$2.invoke(PostCreateScreen.kt:95) at com.sociallab.feature.post.createPost.PostCreateScreenKt$PostCreateScreen$1$2.invoke(PostCreateScreen.kt:91) at androidx.compose.foundation.ClickableNode$clickPointerInput$3.invoke-k-4lQ0M(Clickable.kt:655)

Mainactivity.kt

@Composable
private fun MyNavigationHost(
    modifier: Modifier,
    navController: NavHostController,
) {
    NavHost(
        navController,
        modifier = modifier,
        startDestination = AppRoute.Home.route
    ) {
        postNavGraph(navController)

    }
}

postNavGraph.kt

fun NavGraphBuilder.postNavGraph(
    navController: NavHostController,
) {
    composable(PostRoute.Create.route) {
        PostCreateScreen(
            navController,
            hiltViewModel()
        )
    }
    composable(PostRoute.Filter.route) {
        FilterPostScreen(
            navController,
            hiltViewModel()
        )
    }
  
}

PostRoute.kt

sealed class PostRoute(route: String, routePlaceholder: String) : AppRoute(route, routePlaceholder) {
    data object Create : PostRoute("$PREFIX/create","$PREFIX/create" )
    data object Filter : PostRoute("$PREFIX/filter","$PREFIX/filter")
    data object Final:PostRoute("$PREFIX/final","$PREFIX/final")
    data object Music:PostRoute("$PREFIX/music","$PREFIX/music")
//    data class MediaList:  PostRoute("$PREFIX/filter","$PREFIX/filter")
//    data object MediaList(val data: List<String>) : PostRoute("$PREFIX/filter/$data","$PREFIX/filter/data")
    /*data class MediaList(val data: List<String>) : PostRoute("$PREFIX/filter/{${data}}", "$PREFIX/filter/{${data}}")*/

}

ViewModel.kt

@HiltViewModel
class MediaViewModel @Inject constructor() : ViewModel() {

private val _selectedMediaList = MutableStateFlow<List<String>>(emptyList())
val selectedMediaList: StateFlow<List<String>> = _selectedMediaList

fun updateSelectedMedia(media: List<String>) {
    _selectedMediaList.value = media
}
}

Screen A.kt on Next button click listner

viewModel.updateSelectedMedia(selectedMediaList)
//                    navController.navigate(PostRoute.MediaList(selectedMediaList).route)
  navController.navigate(PostRoute.Filter.route)

Screen B.kt Fetch data

    val selectedMediaList by viewModel.selectedMediaList.collectAsState()


 // Update the ViewModel when selectedMediaList changes
    LaunchedEffect(selectedMediaList) {
        viewModel.updateSelectedMedia(selectedMediaList)
    }

Solution

  • My only problem was to share list of string using route and this is how I solved.

    And this solution of viewmodel i did my changes for solution.

    Navgraph

    composable(PostRoute.Create.route) {
        val viewModel: FilterScreenViewModel = hiltViewModel()
        PostCreateScreen(
            navController,
            viewModel
        )
    }
    composable(PostRoute.MediaList(emptyList()).routePlaceholder) {
        val viewModel: FilterScreenViewModel = hiltViewModel()
        FilterPostScreen(
            navController,
            viewModel
        )
    }
    

    PostRoute

    data class MediaList(
        val data: List<String>
    ) : PostRoute(
        "$PREFIX/filters?references=${
            URLEncoder.encode(
                data.joinToString(separator = ","),
                "UTF-8"
            )
        }",
        "$PREFIX/filters?references={references}"
    )
    

    viewmodel

    val data: List<String>
        get() {
            val joinedString = savedStateHandle.get<String>("references") ?: ""
            return if (joinedString.isNotEmpty()) {
                joinedString.split(",").map { it.replace("+", " ") }
            } else {
                listOf()
            }
        }
    

    Screen B

    sliderList = viewModel.data
    

    mention viewmodel and then get data in list parameter

    Screen A

    navController.navigate(PostRoute.MediaList(selectedMediaList).route)