KMPizza
Kotlin Multiplatform + Pizza = ❤️

Step 18: Add a TopBar and a Floating Action Button to the Jetpack Compose UI

Now we can navigate from one screen to another, but we can’t go back, because there’s no TopBar navigation.
Let’s set it up.

First, create a TopBar composable in the utils folder.

create topbar

It will receive an upPress callback and navigate back when the button is pressed:

 
@Composable
fun TopBar(upPress: () -> Unit) {
   Surface(elevation = 8.dp) {
       Row(modifier = Modifier.fillMaxWidth()) {
           Icon(
               painter = painterResource(id = R.drawable.ic_back),
               modifier = Modifier.clickable(onClick = upPress)
                   .padding(16.dp),
               contentDescription = null
           )
       }
   }
}

Then use it in the RecipeDetailsScreen. Wrap the Column composable that we already had there in a Scaffold with a topBar parameter:

 
Scaffold(
   topBar = { TopBar(upPress = upPress) })
{
   Column( . . . ) { . . . }
}

Build and run the app.
Now we can navigate back from the recipe details to the main list.

create topbar

Next we’ll add a floating button to our RecipesScreen, so that we can create a new recipe.

To do so, first set up the navigation.
We’ll reuse the RecipeDetailsScreen for editing and creating a new recipe.
In the MainScreen add a new navigation destination like this:

 
composable(
   Navigation.RecipeDetails.route
) {
   RecipeDetailsScreen( 
       upPress = { navController.popBackStack() })
}

When the recipeId is null, we’ll use the route /recipeDetails and create a new recipe.
Otherwise we’ll view or edit the one with provided id, using the route that we defined before:

 
composable(
   "${Navigation.RecipeDetails.route}/{id}",
   arguments = listOf(navArgument("id") { type = NavType.LongType })
) {
   RecipeDetailsScreen(
       recipeId = it.arguments!!.getLong("id"),
       upPress = { navController.popBackStack() })
}

Change the parameter to nullable in RecipeDetailsScreen:

 
RecipeDetailsScreen(recipeId: Long? = null, upPress: () -> Unit)

Finally, add a floating button to create new recipes.
In MainScreen add the missing parameter to RecipesScreen destination:

 
onAddRecipe = { navController.navigate("recipeDetails") }

Change the RecipesScreen signature to accept onAddRecipe callback:

 
public fun RecipesScreen(onRecipeClicked: (RecipeResponse) -> Unit, onAddRecipe: () -> Unit)

And wrap the Recipes composable in a Scaffold with a floating action button:

 
Scaffold(floatingActionButton = {
   FloatingActionButton(onClick = onAddRecipe) {
       Icon(
           painter = painterResource(id = R.drawable.ic_add),
           contentDescription = null
       )
   }
}) {
   Recipes(items = recipes, onRecipeClicked = onRecipeClicked)
}

Build and run the app.

create topbar

Now you can navigate to an empty skeleton of the “Create new recipe” screen, but it’s not functional yet.
In the next step we’ll add a new shared ViewModel for the RecipeDetailScreen.