简体   繁体   中英

How to show snackbars from Composables or ViewModels on a multi-module, single activity, Compose only, project

What's the best way to show a Snackbar on a multi-module, single activity, Compose only project?

This is how the project dependency graph looks like:

依赖图

The only activity of the project is inside the app module and it just sets the NavHost as content .

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. But it's missing a Scaffold, since it only takes a portion of the screen:

+-------------------------+
| Debug screen            |
|-------------------------+
|                         |
| Feature A debug section |
|                         |
|------------------------ +
|                         |
| Feature B debug section |
|                         |
|------------------------ +
|                         |
| Feature C debug section |
|                         |
|------------------------ +
|                         |
| Feature D debug section |
|                         |
+------------------------ +

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 .

CompositionLocal can be used to pass data through the composition tree implicitly.

The first thing to do is to declare a variable that must be visible by provider and consumers (in my case, I created it inside the core-ui module):

val LocalSnackbarHostState = compositionLocalOf<SnackbarHostState> { error("No SnackbarHostState provided") }

Then the provider should wrap its children with a CompositionLocalProvider :

val scaffoldState = rememberScaffoldState()
    CompositionLocalProvider(
        LocalSnackbarHostState provides scaffoldState.snackbarHostState
    ) {
        Scaffold(
[...]

And finally the children can grab an instance of the SnackbarHostState accessing the variable LocalSnackbarHostState :

val snackbarHostState = LocalSnackbarHostState.current

I would only use a single snackbar for your entire app. It needs to be included at the root level of your UI hierarchy and accessible through a global object. This can be done by using a class that inherits from Application and placing a method inside that class to handle displaying the snackbar.

There's an example app showing how this is done. The demo app can be downloaded at:

https://github.com/JohannBlake/Jetmagic

When you run the app, open up the navigation tray and select any navigation item. On the screen that appears, click on the button labeled Return value from another screen . This will take you to another screen where you select an item and return to the previous screen. The selected item will be shown in a snackbar. This snackbar is global throughout the app.

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