I work in a project that's slowly adopting Jetpack Compose. It's mostly a single Activity
multiple Fragment
s app where we use the Android's navigation component to handle transitions to each screen (Fragment
). Whenever we can, we are only replacing the Fragment
s' XML layout with Composables.
The cases up until now were handling like the following:
A. Fragment
s show composables and handle navigation:
class ScreenFragment : Fragment() {
// For observing events that trigger navigation
private val viewModel by lazyViewModel { ScreenViewModel() }
override fun onCreateView( ... ): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(DisposeOnLifecycleDestroyed(viewLifecycleOwner))
setContent {
AppTheme {
Screen(onBackPressed = { findNavController().navigateUp() })
}
}
}
}
...
}
B. Composable that handles everything else UI related:
@Composable
fun CreatePassword(
onBackPressed: () -> Unit,
) {
// For observing UI states and events
val viewModel: CreatePasswordViewModel = viewModel()
...
}
As you can see, we have a reference for the screen's ViewModel
in both our Fragment
s and our Composables. Up until now, this has been working fine, the composable viewModel()
function was always returning the same existing instance of the Fragment
's ViewModel
.
The problems came when we needed the reference of a ViewModel
scoped to an Activity
on the composable:
ViewModel
on the Activity
:class MainActivity : AppCompatActivity() {
private val viewModel by lazyViewModel { MainViewModel() }
...
}
MainActivity
's ViewModel
on the Fragment
likeclass MainFragment : Fragment() {
private val viewModel: MainViewModel by viewModels(::requireActivity)
...
}
By doing so the Fragment
has the same instance of the Activity
's but the composable doesn't.
My questions is,
would it be possible to get the reference of the Activity
's ViewModel
inside the composable? For now I'm simply passing down the Fragment
's ViewModel
as a parameter to my main composable screen.
I managed to pass down the Activity
's ViewModel
by providing a ViewModelStoreOwner
to my composable as following:
class ScreenFragment : Fragment() {
// For observing events that trigger navigation
private val viewModel by lazyViewModel { ScreenViewModel() }
override fun onCreateView( ... ): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(DisposeOnLifecycleDestroyed(viewLifecycleOwner))
setContent {
val viewModelStoreOwner =
compositionLocalOf<ViewModelStoreOwner> { requireActivity() }
AppTheme {
CompositionLocalProvider(
LocalViewModelStoreOwner provides viewModelStoreOwner.current
) {
Screen(onBackPressed = { findNavController().navigateUp() })
}
}
}
}
}
...
}