NullPointerException
NullPointerException

Reputation: 37731

No recomposition called when State Holder class modified

I want to use a state holder class to keep some app level state things, for example, if a dialog must be displayed. For that, I added title and message variables in the app state holder class. When I set them to a value different from null, that should display a dialog on my app, because I'm checking if these variables are different from null for displaying it on my screen composable. Something is not working because when I set these variables to a value different from null nothing happens. It seems that recomposition is not being started.

This is the app state composable and the remember function I use to remember it:

val appStateHolder = rememberAppStateHolder()

@Composable
fun rememberAppStateHolder(
    navController: NavHostController = rememberNavController(),
    dialogTitle: StringResource? = null,
    dialogMessage: StringResource? = null,
): AppStateHolder {
    return remember(
        navController,
        dialogTitle,
        dialogMessage
    ) {
        AppStateHolder(
            navController = navController,
            dialogTitle = dialogTitle,
            dialogMessage = dialogMessage
        )
    }
}

More:

@Stable
class AppStateHolder(
    val navController: NavHostController = NavHostController(),
    var dialogTitle: StringResource? = null,
    var dialogMessage: StringResource? = null
) {
    // UI State
    val currentDestination: NavDestination?
        @Composable get() = navController
            .currentBackStackEntryAsState().value?.destination

    fun isDialogEnabled(): Boolean {
        return (dialogTitle != null || dialogMessage != null)
    }

    // UI logic
    fun navigate(route: String) {
        navController.navigate(route)
    }

    fun showDialog(dialogTitle: StringResource, dialogMessage: StringResource) {
        this.dialogTitle = dialogTitle
        this.dialogMessage = dialogMessage
    }

    fun hideDialog() {
        this.dialogTitle = null
        this.dialogMessage = null
    }
}

This is how I check if the dialog should be displayed:

if (appStateHolder.isDialogEnabled()) {
    MessageDialog(
        title = stringResource(Res.string.about),
        message = stringResource(Res.string.about_message),
        onCloseRequest = { appStateHolder.hideDialog() }
    )
}

This is how I set the dialog values to different of null:

appStateHolder.showDialog(title, message)

Upvotes: -2

Views: 57

Answers (1)

KingOfTheNorth
KingOfTheNorth

Reputation: 571

As long as the keys passed into remember has not changed the enclosing block cannot be re-evaluated. In other words the initial instance of your AppStateHolder class is what you still have in the composition.

So every time you do this:

appStateHolder.showDialog(title, message)

The changes are not reflected in the composition because it doesn't know of any mutating object.

To fix that, you can make dialogTitle and dialogMessage be a MutableState.

class AppStateHolder(
    val navController: NavHostController = NavHostController()
){
   var dialogTitle: String? by mutableStateOf(null)
   var dialogMessage: String? by mutableStateOf(null)

}

Then mutate the values like you did before. That should work.

Upvotes: 3

Related Questions