Home > Net >  How Can I set Dynamic Snackbar Colors in Compose in a Scaffold
How Can I set Dynamic Snackbar Colors in Compose in a Scaffold

Time:02-18

My app has a lot of colour coding in it, and I can't figure out how to cleanly change the colour of a Snackbar action in a scaffold based on the action that shows the snackbar.

I have a composable, and inside there is a lazy list with an item that can be clicked. When you click it, that row has a specific colour associated with it.

Now, the click listener isn't a composable itself, I can't create a snackbar in it. I have to use the scaffolds snackbarHost

onClick {
   scope.launch {
            val snackbarResult = scaffoldState.snackbarHostState.showSnackbar(
                message = "message",
                actionLabel = "action"
            )
            when (snackbarResult) {
                SnackbarResult.ActionPerformed -> do something
                SnackbarResult.Dismissed -> do something
            }
        }
]

From here, I can't choose or set a colour.

In the scaffold, where I can make the snackbar, I can't get a colour back to it as it only has the snackbarData

Scaffold(
        scaffoldState = scaffoldState,
        snackbarHost = {
            SnackbarHost(it) { snackbarData ->
                Snackbar(          
                    actionColor = I want a color here!
                    snackbarData = snackbarData
                )
            }
        },

Using this set up, if 2 snackbars are triggered back to back, I can action both. You action the second, and then you can action the first. I like this functionality.

The only way I can get a colour in this way, is to set a global variable in the onClick but that is incredibly gross. Changing a local variable in the same file does not work.

Alternatively, I can trigger a recomposition via the ViewModel which will trigger showing a snackbar with the correct colour, but then I lose the functionality of triggering 2 items and still being capable of actioning both snackbars one after the other.

I even tried putting the colour into the action label so i could split it out, but I can't instantiate the existing SnackbarDataImpl to then recreate it without it because it's private. I would have to copy the entire scaffold implementation to simply add a colour this way as the host has to come from the scaffold

Is there some better way I can do this? It's way too complicated for something as simple as wanting to set a colour based on the action?

CodePudding user response:

Maybe you could use something like this:

var color by rememberSaveable { mutableStateOf(Color.Red) }

Snackbar(
    actionColor = color,
    ...
)

CodePudding user response:

Probably your best bet is to have your ViewModel store the snackbar color:

class MyViewModel : ViewModel() {
    var snackBarColor by mutableStateOf(Color.Blue)
    (...)
}

And in the Scaffold:

Scaffold(
    scaffoldState = scaffoldState,
    snackbarHost = {
        SnackbarHost(it) { snackbarData ->
            Snackbar(          
                actionColor = myViewModel.snackBarColor,
                snackbarData = snackbarData
            )
    }
},

So now when calling the Snackbar, also set the snackBarColor in the ViewModel:

onClick {
   myViewModel.snackBarColor = Color.Red
   scope.launch {
        val snackbarResult = scaffoldState.snackbarHostState.showSnackbar(
            message = "message",
            actionLabel = "action"
        )
        when (snackbarResult) {
            SnackbarResult.ActionPerformed -> do something
            SnackbarResult.Dismissed -> do something
        }
    }
}

As long you have the same instance of the MyViewModel both in the Scaffold and where you are calling the onClick, the Scaffold will observe the snackBarColor from MyViewModel and update automatically when it changes.

  • Related