How to pass object between @Composable screens? ``...
# compose
i
How to pass object between @Composable screens?
Copy code
@Parcelize
data class Post(
    val author: String,
    val title: String,
    val description: String,
    val reactionNumber: Int
) : Parcelable
In my `navigationController`;
Copy code
composable(
    "PostDetails/{post}",
    arguments = listOf(
        navArgument("post") { type = NavType.ParcelableType(Post::class.java)}
    )
) {
    PostDetailsScreen(it.arguments?.get("post") as Post)
}
Now I get error: Parcelables don't support default values. Should I pass object using GSON ot this way is corretly? I heard that NavType.ParcelableType didn't work but right now it's implemented and developers can use it (?)
i
There are a lot of threads about this if you search, but the tl/dr is that you shouldn't be doing this at all, ever. You might also read https://stackoverflow.com/a/67553630/1676363
👍 1
i
Thanks, good point. I will try other solution
j
@Ian Lake it is nice you reply a lot of these questions in SO, I like slack but it is not indexable by Google so a lot of knowledge is hard to find (even knowing the existence of a specific thing, I can't find a lot of times the thread).
i
@Ian Lake I put my selected Post to viewModel
Copy code
LazyColumn {
    items(postList) { post ->
        Post(
            onPostClick = { mainViewModel.actions.offer(MainAction.SelectPost(post)) },
            post
        )
    }
}
My viewModel has action channel (I want to put here destination screen with possibly data)
Copy code
class MainViewModel : ViewModel() {

    val state = MainViewState()
    val actions = Channel<MainAction>()

    init {

        viewModelScope.launch {
            try {
                actions.receiveAsFlow().collect { action ->
                    when (action) {
                        is MainAction.SelectPost -> {
                            state.destinationScreen.value = DestinationScreen.POST_DETAILS(action.data)
                        }
                    }
                }
            } catch (e: Exception) {

            }
        }
    }
}

enum class DestinationScreen(val data: Any? = null){
    DEFAULT,
    POST_DETAILS
}

class MainViewState {
    val destinationScreen = mutableStateOf(DestinationScreen.DEFAULT)
}

sealed class MainAction {
    data class SelectPost(val data: Post) : MainAction()
}
I have MainViewState where I put destinationScreen from action with data. In my @Compose screen I observe state and react for specific screenDestination:
Copy code
@Composable
fun AppNavigationHost() {
    val mainViewModel: MainViewModel = viewModel()
    val appNavController = rememberNavController()

    NavHost(
        navController = appNavController,
        startDestination = Screens.HOME.title
    ) {
        when (mainViewModel.state.destinationScreen.value) {
            DestinationScreen.POST_DETAILS -> {
                composable(DestinationScreen.POST_DETAILS.name) {
                    PostDetailsScreen(//Post object here)
                }
            }
            else -> {}
        }
    }
}
The problem is I don't know hot to set DestinationScreen with data to my state.destinationScreen.value Should I use Pair()?
i
I think you need to read my answer again and do a search for the previous threads - the only think you should be sending as arguments is a unique ID of the post following the guide to navigating with arguments: https://developer.android.com/jetpack/compose/navigation#nav-with-args
Your second destination should be talking to your repository layer to retrieve the actual
Post
instance given that unique ID, preferably in some observable format (i.e., a
getPost(postId: String): Flow<Post>
) that you can then call
collectAsState()
on
(Your first destination should also be talking to that same repository, via some other
getPosts(): Flow<List<Post>>
type of method)
a
The prototype in that gist still follows the same principle around not passing destination data by value in navigation operations 🙂
It's a matter of single source of truth. If the event that initiated the navigation were responsible for providing the data itself to the next screen as opposed to an ID into an independent data source, who would be responsible for updating that data when it changed elsewhere? Would the original navigate to screen event have to re-run somehow to pass new data, or would that screen end up with multiple sources of truth?
862 Views