简体   繁体   中英

Collecting StateFlow in compose as lifecycle aware first emit initial value then the original value

I am showing a dialog based on the showPermissionDialogState bool whose initial value is true ie show dialog first-time app open.
In Preference class

override val showPermissionDialog = _dataStore.data.map {
      it[PREF_SHOW_PERM_DIALOG] ?: DEF_SHOW_PREM_DIALOG
}

In UseCase

override fun execute(parameters: Unit): Flow<Result<Boolean>> =
    _preferenceStorage.showPermissionDialog.map {
        Result.Success(it)
}

Initially DEF_SHOW_PREM_DIALOG = true In ViewModel

val showPermissionDialogState = showPermissionDialog(Unit).map {
      it.successOr(DEF_SHOW_PREM_DIALOG)
}.stateIn(viewModelScope, WhileViewSubscribed, DEF_SHOW_PREM_DIALOG)

After the dialog is shown I am saving the false in dataStore but when I open the app again the dialog shows and suddenly disappears ie first getting the initial value than the original one.
In composable screen

val canShowPermissionDialogState by viewModel.showPermissionDialogState.collectAsStateWithLifecycle()

 var openPermissionDialogState by rememberSaveable { mutableStateOf(canShowPermissionDialogState) }
    LaunchedEffect(canShowPermissionDialogState) {
        // Do not show battery optimization dialog if battery optimization is disabled for this app.
        if (viewModel.isBatteryOptimizationPermissionGranted) {
            viewModel.onHomeEvent(HomeEvent.NeverShowPermissionDialog)
        }
        openPermissionDialogState = canShowPermissionDialogState
    }


    if (openPermissionDialogState) {
        PermissionMessageDialog(
            title = stringResource(R.string.battery_opti_perm_title),
            message = stringResource(R.string.battery_opti_perm_msg)
                .withBold(boldText = stringResource(R.string.battery_opti_perm_guide)),
            onDialogDismiss = { permanently ->
                if (permanently) {
                    viewModel.onHomeEvent(HomeEvent.NeverShowPermissionDialog)
                }
                openPermissionDialogState = false
            }
        ) {
            openAppSettingPage(context)
        }
    }

Initialise your state-holder like so,

var showPermissionDialogState by Delegates.notNull<Boolean>()

Then create an initialisation method inside the viewModel to initialise this variable in,

fun initPermState() {...} // Add thou, thy logic

BEFORE you display this variable anywhere (by accessing it in an @Composable ), make sure to call this initialisation method.

Like, call it right after you initialise your viewModel in your main activity onCreate .

You yourself stated the diagnosis that the issue was that the default value was being shown then the actual values means the default value was being READ before the actual value. Since the same variable is responsible for displaying the value, it means the variable was being READ before it was UPDATED. HENCE, all you had to do was to make sure you update the variable BEFORE reading it. That's what we've done here.

Uh, the fact that the dialog just flashes could also be indicative of asynchronous update of the variable by the proto reader. The update is TRIGGERED before the access but actually ends up updating after the fact, because of its non-blocking nature. Solution for that, as easy as it gets is to set the default value to false , instead of true, so the first card might be a bit delayed but it won't make for an unpleasant ux by flashing every time the user opens your app again.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM