How to change Scaffold Floating Action Button action with different screens. Jetpack Compose

2k Views Asked by At

How can I change FAB action(onClick{}) depending on current screen with Scaffold.

            Scaffold(
                floatingActionButton = {
                    FloatingActionButton(onClick = { 
                        //Different actions here depending on current screen
                    }) { }
                }
            ) {
                NavHost(
                    navController = navController,
                    startDestination = "route1"
                ) {
                    composable(
                        route = "route1"
                    ) {
                        ScreenOne()
                    }

                    composable(
                        route = "route2"
                    ) {
                        ScreenTwo()
                    }
                }
            }

For example with ScreenOne() I want to add some entities to database and with ScreenTwo() I'd like to save edited entity. And all this using same FAB shared by Scaffold.

UPDATE: Just understood that I also need to mention that I'm also need FAB to know some data to operate. For example I'd like to save some entity to database by clicking on FAB, so FAB need to know about this entity.

Example: I'm clicking on some item in the list. It opens some navigation direction with some entity. I'm modifying this entity and clicking on FAB to save edited entity.

3

There are 3 best solutions below

1
On

If you are using Navigation, you can retrieve the navController by rememberNavController() and then use this to getCurrentBackStackEntry().destination, applying conditionals in the onClick block and setting the action accordingly

2
On

You can observe the current route of the NavController and do the action accordingly.

// When you navigate to another screen, this value is updated.
val currentRoute = navController
    .currentBackStackEntryFlow
    .collectAsState(initial = navController.currentBackStackEntry)

Scaffold(
    floatingActionButton = {
        FloatingActionButton(
            onClick = {
                when (currentRoute.value?.destination?.route) {
                    "screen1" -> { /* Action for Screen 1 */ }
                    "screen2" -> { /* Action for Screen 2 */ }
                    // ...
                }
            }
        ) {
            Icon(Icons.Default.Add, contentDescription = null)
        }
    }
) {
    NavHost(navController = navController, startDestination = "screen1") {
        composable("screen1") { Screen1() }
        composable("screen2") { Screen2() }
    }
}
1
On

I solved the problem like this.

  1. Define a mutable state of a callback function. This is somewhere outside your Scaffold (in my case in my navigation composable):
val floatingActionButtonFunction = remember { mutableStateOf({})}
  1. Provide a callback function in your composable containing the Scaffold and call it when the floatingActionButton is clicked:
@Composable
fun LayoutScreen(
    appState: AppState,
    onFloatingActionButtonClick: (() -> Unit)? = null,
    content: @Composable () -> Unit,
) {
    Scaffold(
        floatingActionButtonPosition = FabPosition.End,
        floatingActionButton = {
            FloatingActionButton(onClick = {
                if (onFloatingActionButtonClick != null) {
                    onFloatingActionButtonClick()
                }
            }) {
                Icon(imageVector = Icons.Default.Add, contentDescription = "fab icon")
            }
        },
        // rest of your Scaffold
    )
}
  1. Set up your navigation with providing the function to your actual view composable:
composable(route = ScreenNavigation.AccountScreeen.route) {
            LayoutScreen(appState = appState, onFloatingActionButtonClick = {floatingActionButtonFunction.value()}) {
                AccountScreen(appState, floatingActionButtonFunction)
            }
        }
  1. In your view (the actually shown composable) overwrite the function:
@Composable
fun AccountScreen(appState: AppState, floatingActionButtonAction: MutableState<() -> Unit>) {
floatingActionButtonAction.value = {
            Store.instance.dispatch(NavigationActions.SetNavigation(
                Navigation(ScreenNavigation.PaymentScreen.route)))
        }
}