So, how bad would it be to persist the same instan...
# compose
r
So, how bad would it be to persist the same instance of
ScaffoldState
across the entire application (e.g. via singleton in Hilt)? I have the feeling that would be really bad but I can't think of another way to solve this issue: https://stackoverflow.com/questions/69902988/how-to-show-snackbars-from-composables-or-viewmodels-on-a-multi-module-single-a
1
t
I would think passing one
SnackbarHostState
from your top-level Composable function would work. You can then use
rememberScaffoldState(snackbarHostState=...)
where needed.
Child Composables can then tell that host state to show a Snackbar, and any SnackbarHost using it will update.
r
hey, thanks for the reply. I'm not sure how can I pass the
SnackbarHostState
since the Composable do not see each others. I'm not sure if you checked the SO link, but the architecture of the project is a little complex, mainly because of the multi module aspect.

https://i.stack.imgur.com/60RWh.png

Each feature module provides a list of composable screens that will be shown on the NavHost. Each screen has its own Scaffold, so it can easily show Snackbars from the each screen's ViewModel. There is a special feature module, feature-debug, that shows on a single screen, a list of composable provided by each feature module, that are called debug sections. It is used to allow any feature module to show automatically some settings inside the debug screen. Each debug section has its own ViewModel so it works exactly like a Screen. So I'm not sure how can I show a Snackbar on the Scaffold of the feature-debug screen, from a composable that is declared inside another feature module that has no visibility of the any class inside feature-debug.
t
It shouldn't matter; your functions just accept a
SnackbarHostState
, and whatever calls those functions can supply one.
r
OK, I think I got it. I think you are saying to add the
SnackbarHostState
as parameter here and then send it from here
t
Yes. It's easier with top-level functions instead of lambda properties, though, because functions can define default argument values.
👍 1
r
but out of curiosity, why would it be bad to provide the same instance of
SnackbarHostState
via Hilt? This way I could just grab the instance in every ViewModel where is needed and just show a Snackbar from there. I'm just curious to understand what issue it might cause: could it be some concurrency issues or some leaks?
t
It would probably work, but I'm unsure about how snapshot state works across top-level ComposeViews.
In any case, something like CompositionLocalProvider makes more sense to me, since this is purely a dependency of your Compose UI and not of your data/business layer.
Even then, being explicit and passing dependencies via functions is the best approach for testability and understandability, because DI hides what dependencies are available as state to your Composables, and their lifetimes.
r
I tried and it seems to work but, since also the guidelines say to avoid singletons, I'll go with the function parameter option. Too bad, I was excited to be able to show Snackbars simply injecting a manager on the VMs. The other solution requires a lot of boilerplate code (declaring the effect on the contract, sending the effect to the composable, launching the effect and finally showing the snackbar. And this for every single screen, since I have a scaffold on every screen...
oh this
CompositionLocalProvider
seems pretty interesting!
I was actually looking for something like this but my googlefu failed me
@tad It works like a charm, thanks!
Copy code
val LocalSnackbarHostState = compositionLocalOf<SnackbarHostState> { error("No SnackbarHostState provided") }
Copy code
CompositionLocalProvider(
        LocalSnackbarHostState provides scaffoldState.snackbarHostState
    ) {
[...]
}
Copy code
val snackbarHostState = LocalSnackbarHostState.current