https://kotlinlang.org logo
#compose
Title
# compose
l

Lukasz Kalnik

08/12/2022, 9:40 AM
Using Compose Destinations library, I pass a navigation argument and read it in ViewModel from
savedStateHandle
. Then I need to load data in the ViewModel based on the passed argument. Do I get some callback when the ViewModel has been navigated to? How do I perform the initial data loading based on the navigation argument?
When I do it in the ViewModel's
init
block, it crashes, because it tries to access the navigation argument when the ViewModel is created in containing Fragment to be passed to the composable in the initial navigation graph setup.
I suppose this is the cause of the problem, passing the ViewModel to composable for setup in the Fragment:
Copy code
class SystemFragment : Fragment() {
    
    private val sensorSettingsViewModel by viewModels<SensorSettingsViewModel>(
        factoryProducer = { SensorSettingsViewModelFactory(this) }
    )

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        return ComposeView(requireContext()).apply {
            setContent {
                        DestinationsNavHost(
                            navGraph = NavGraphs.root
                        ) {
                            composable(SensorSettingsSheetDestination) {
                                SensorSettingsSheet(
                                    viewModel = sensorSettingsViewModel
                                )
                            }
                        }
              }
         }
    }
}
This is what I have in my ViewModel (and it crashes when entering the
SystemFragment
, even without any actual navigation to the
SensorSettingsSheet
):
Copy code
class SensorSettingsViewModel(
    savedStateHandle: SavedStateHandle,
) : ViewModel() {

    private val navArgs: SensorSettingsSheetNavArgs = savedStateHandle.navArgs()
    private val currentId: String = navArgs.sensorId

}
It crashes with
RuntimeException: 'sensorId' argument is mandatory but was not present
Copy code
at myapp.systemoverview.presentation.destinations.SensorSettingsSheetDestination.argsFrom (SensorSettingsSheetDestination.kt:77)
c

Colton Idle

08/12/2022, 10:26 AM
Hm. Typically in this case I will just grab the arg from sSH in init like you mentioned and I've never had any issues. I wonder if it has something to do with the fact that you're using fragments? im not being too helpful except with basically saying that, what you did _should work (at least in my exp)
l

Lukasz Kalnik

08/12/2022, 11:05 AM
No problem, thanks for chiming in anyway. Every feedback is helpful.
However in my code it's clear that the
SensorSettingsSheetViewModel
gets constructed in
SystemFragment.onCreateView()
even though there was no navigation to it (because I pass it to the composable). I will test a delayed passing of viewmodels (as composable default argument), maybe it fixes the issue.
Yes, I can confirm - not creating the ViewModel in Fragment fixed the crash.
In the end I generated the viewmodels with
Koin
and passed them as default parameters to the composables:
Copy code
val systemOverviewModule = module {
     viewModel { SensorSettingsViewModel(savedStateHandle = get()) }
}
Copy code
@Composable
fun SystemScreen(
    viewModel: SystemViewModel = getViewModel(),
) {}
(I just noticed that there are different screens/viewmodels in both snippets, but you get the idea.)
30 Views