androidtabsandroid-jetpack-composefloating-action-button

Can I use FloatingActionButton and NavigationSuiteScaffold together?


I am using NavigationSuiteScaffold for navigation in my app. I want to add a FloatingActionButton to my app while still having adaptive navigation for small and large screens, but I can't figure out how to add elements that would usually be in a Scaffold to my app.

Is there a way to display an FAB according to the Material Design Guidelines while using adaptive navigation?


Solution

  • This is not possible currently out of the box. There is a Feature Request open on the Google Issue Tracker, the design team is investigating it. You can leave a comment there and upvote the issue to draw more attention to it.

    You can implement this yourself as follows (Screenshots below):

    NavigationSuiteScaffoldFab.kt

    @Composable
    fun NavigationSuiteScaffoldFab(
        navigationSuiteItems: NavigationSuiteScope.() -> Unit,
        modifier: Modifier = Modifier,
        layoutType: NavigationSuiteType =
            NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(currentWindowAdaptiveInfo()),
        navigationSuiteColors: NavigationSuiteColors = NavigationSuiteDefaults.colors(),
        containerColor: Color = NavigationSuiteScaffoldDefaults.containerColor,
        contentColor: Color = NavigationSuiteScaffoldDefaults.contentColor,
        floatingActionButton: @Composable () -> Unit = {},
        content: @Composable () -> Unit = {},
    ) {
        Surface(modifier = modifier, color = containerColor, contentColor = contentColor) {
            NavigationSuiteScaffoldLayout(
                navigationSuite = {
                    Column(
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        if (layoutType == NavigationSuiteType.NavigationRail 
                            || layoutType == NavigationSuiteType.NavigationDrawer) {
                            Box(
                                modifier = Modifier.padding(16.dp)
                            ) {
                                floatingActionButton()
                            }
                        }
                        NavigationSuite(
                            layoutType = layoutType,
                            colors = navigationSuiteColors,
                            content = navigationSuiteItems
                        )
                    }
                },
                layoutType = layoutType,
                content = {
                    Scaffold(
                        modifier = Modifier.consumeWindowInsets(
                            when (layoutType) {
                                NavigationSuiteType.NavigationBar ->
                                    NavigationBarDefaults.windowInsets.only(WindowInsetsSides.Bottom)
                                NavigationSuiteType.NavigationRail ->
                                    NavigationRailDefaults.windowInsets.only(WindowInsetsSides.Start)
                                NavigationSuiteType.NavigationDrawer ->
                                    DrawerDefaults.windowInsets.only(WindowInsetsSides.Start)
                                else -> WindowInsets(0, 0, 0, 0)
                            }
                        ),
                        floatingActionButton = {
                            if (layoutType == NavigationSuiteType.NavigationBar) {
                                floatingActionButton()
                            }
                        }
                    ) {
                        Box(modifier = Modifier.padding(it)) {
                            content()
                        }
                    }
                }
            )
        }
    }
    

    Usage

    @Composable
    fun NavigationSuiteDemo() {
    
        var currentDestination by rememberSaveable { mutableStateOf(AppDestinations.HOME) }
    
        NavigationSuiteScaffoldFab(
            navigationSuiteItems = {
                AppDestinations.entries.forEach {
                    item(
                        icon = {
                            Icon(
                                it.icon,
                                contentDescription = stringResource(it.contentDescription)
                            )
                        },
                        label = { Text(stringResource(it.label)) },
                        selected = it == currentDestination,
                        onClick = { currentDestination = it }
                    )
                }
            },
            floatingActionButton = {
                FloatingActionButton(
                    onClick = {}
                ) {
                    Icon(Icons.Filled.Edit, "")
                }
            }
        ) {
            // TODO: Destination content.
        }
    }
    
    enum class AppDestinations(
        @StringRes val label: Int,
        val icon: ImageVector,
        @StringRes val contentDescription: Int
    ) {
        HOME(R.string.home, Icons.Default.Home, R.string.home),
        FAVORITES(R.string.favorites, Icons.Default.Favorite, R.string.favorites),
        SHOPPING(R.string.shopping, Icons.Default.ShoppingCart, R.string.shopping),
        PROFILE(R.string.profile, Icons.Default.AccountBox, R.string.profile),
    }
    

    Output:

    Screenshot small

    Screenshot big