I am using Jetpack Compose and noticed that the preview is not shown. I read articles like this, but it seems my problem has a different root cause. Even I added defaults to all parameters in the compose function like this:
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
@ExperimentalFoundationApi
@Preview
fun VolumeSettingsScreen(
speech: SpeechHelper = SpeechHelper(), // my class that converts text to speech
viewModel: VolumeSettingsViewModel = hiltViewModel(), // using Hilt to inject ViewModels
navController: NavHostController = rememberNavController() // Compose Navigation component
) {
MyAppheme {
Box(
...
)
}
}
When I rollbacked some changes I realized that the @Preview
does not support the viewModels
regardless of whether they are injected with Hilt or not.
Any Idea how this could be fixed?
CodePudding user response:
Have you considered having a structure where you have a Screen
and the actual Content
separated like this?
// data class
data class AccountData(val accountInfo: Any?)
// composable "Screen", where you define contexts, viewModels, hoisted states, etc
@Composable
fun AccountScreen(viewModel: AccountViewModel = hiltViewModel()) {
val accountData = viewModel.accountDataState.collectAsState()
AccountContent(accountData = accountData) {
// click callback
}
}
//your actual composable that hosts your child composable widget/components
@Composable
fun AccountContent(
accountData: AccountData,
clickCallback: () ->
) {
...
}
where you can have a preview for the Content
like this?
@Preview
@Composable
fun AccountContentPreview() {
// create some mock AccountData
val mockData = AccountData(…)
AccountContent(accountData = mockData) {}
}
this way, all components that aren't needed to be configured by the actual content composable are separated, taking you off from headaches configuring a preview.
Just an added note and could be off-topic, I just noticed you have a parameter like this,
speech: SpeechHelper = SpeechHelper()
you might consider utilizing compositionLocalProvider (if needed), that could clean up your parameters.
CodePudding user response:
I managed to visualize the preview of the screen, by wrapping the ViewModels's functions into data classes, like this:
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
@ExperimentalFoundationApi
@Preview
fun VolumeSettingsScreen(
modifier: Modifier = Modifier,
speechCallbacks: SpeechCallbacks = SpeechCallbacks(),
navigationCallbacks: NavigationCallbacks = NavigationCallbacks(),
viewModelCallbacks: VolumeSettingsScreenCallbacks = VolumeSettingsScreenCallbacks()
) {
MyAppheme {
Box(
...
)
}
}
I passed not the ViewModel directly in the compose but needed functions in a Data class for example, like this:
data class VolumeSettingsScreenCallbacks(
val uiState: Flow<BaseUiState?> = flowOf(null),
val onValueUpSelected: () -> Boolean = { false },
val onValueDownSelected: () -> Boolean = { false },
val doOnBoarding: (String) -> Unit = {},
val onScreenCloseRequest: (String) -> Unit = {}
)
I made a method that generates those callbacks in the ViewModel, like this:
@HiltViewModel
class VolumeSettingsViewModel @Inject constructor() : BaseViewModel() {
fun createViewModelCallbacks(): VolumeSettingsScreenCallbacks =
VolumeSettingsScreenCallbacks(
uiState = uiState,
onValueUpSelected = ::onValueUpSelected,
onValueDownSelected = ::onValueDownSelected,
doOnBoarding = ::doOnBoarding,
onScreenCloseRequest = ::onScreenCloseRequest
)
....
}
In the NavHost I hoisted the creation of the ViewModel like this:
@Composable
@ExperimentalFoundationApi
fun MyAppNavHost(
speech: SpeechHelper,
navController: NavHostController,
startDestination: String = HOME.route,
): Unit = NavHost(
navController = navController,
startDestination = startDestination,
) {
...
composable(route = Destination.VOLUME_SETTINGS.route) {
hiltViewModel<VolumeSettingsViewModel>().run {
VolumeSettingsScreen(
modifier = keyEventModifier,
speechCallbacks = speech.createCallback() // my function,
navigation callbacks = navController.createCallbacks(), //it is mine extension function viewModelCallbacks = createViewModelCallbacks()
)
}
}
...
}
It is a bit complicated, but it works :D. I will be glad if there are some comets for improvements.