29

I know how to implement BottomSheet in Material 2 Jetpack Compose using BottomSheetScaffold.

But there is no BottomSheetScaffold in Material 3. Also, there is nothing in official samples about BottomSheet.

Raheem
  • 524
  • 1
  • 7
  • 18
  • 2
    You can prefer this article: https://johncodeos.com/how-to-create-a-bottom-sheet-with-jetpack-compose/ – dphans Jun 06 '22 at 13:21
  • 3
    You can refer the Jetpack Compose Material 3 components available [here](https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#bottomappbar). `BottomSheetScaffold` is not yet available in Material 3. – Abhimanyu Jun 06 '22 at 14:11
  • 2
    Checked today with the latest Material 3 Lib `material3:1.0.0-alpha16` and **BottomSheetScaffold** is not yet available on Material 3. – Tonnie Aug 16 '22 at 05:27
  • 1
    ModalBottomSheet has been added to compose version 1.1.0-alpha06, I have edited my answer to update this! – Arthur Kasparian Feb 23 '23 at 09:12

6 Answers6

26

So I was able to make it work!

It seems that, as of today, BottomSheetScaffold is not available yet on Material3, this is discussed in this issue I found digging around: https://issuetracker.google.com/issues/229839039

I quote the important part from the reply of a google dev:

we aren't in a super easy spot with Swipeable. It currently has a number of critical bugs that need to be addressed first (we are working on this), which is why we are limiting the surface we are exposing for Swipeable in M3 for the time. Our plan for the coming months is to focus on this specific area and improve developer experience.

Material 3 for Jetpack Compose is still in alpha - this means we consider components production-ready, but the API shape is flexible while in alpha. This gives us space to iterate while getting real-world feedback from developers, which ultimately helps improve your experience. Copy-pasting source code for components that are not (fully) implemented or exposed in an alpha version can be a good thing to do in the meantime! Owning the source code while the API shape is still flexible gives you a number of benefits like ease of updating dependencies, even if the APIs change, and allows you to evolve your components in your own pace.

So I just followed the advice and I copy pasted BottomSheetScaffold into my project. Of course it did not work straight away because of a few missing classes and some minor API changes. At the end I was able to make it work by pulling and hacking the following classes and adding them to my project:

  • BottomSheetScaffold.kt
  • Drawer.kt
  • Strings.kt
  • Swipeable.kt

I have created a gist with the source code if you want to try: https://gist.github.com/Marlinski/0b043968c2f574d70ee6060aeda54882

You will have to change the import to make it work on your project as well as add the "-Xjvm-default=all" option by adding the following into your gradle file in the android{} section:

android{ 
   ...
   kotlinOptions {
        freeCompilerArgs += ["-Xjvm-default=all"]

        // "-Xjvm-default=all" option added because of this error:
        // ... Inheritance from an interface with '@JvmDefault' members is only allowed with -Xjvm-default option
        // triggered by porting BottomSheetScaffold for Material3 on Swipeable.kt:844
   }
}

It works very well for me, will keep this solution until it is officially supported in material3.

Hope it helps!

Marlinski
  • 276
  • 4
  • 6
  • 3
    Excellent, thanks for hacking it straight from the Oven before it is served. – Tonnie Oct 24 '22 at 06:34
  • 1
    Thanks for the gist! If someone wants to track this issue, here is the [issue tracker for the missing bottom sheet component](https://issuetracker.google.com/issues/244189383). – goldensoju Nov 18 '22 at 05:42
  • This is awesome, great job! How difficult do you think it would be for me to port over [ModalBottomSheetLayout](https://developer.android.com/reference/kotlin/androidx/compose/material/package-summary#modalbottomsheetlayout) for usage? If you had a link to the place you pulled the source you modified that would be helpful, I don't know much about Google's source repos. – Nathan Nov 25 '22 at 11:15
  • It is already available in `M3` – IgorGanapolsky Feb 28 '23 at 13:05
15

We finally have ModalBottomSheet in Material3.

var openBottomSheet by rememberSaveable { mutableStateOf(false) }
val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded  = true)



// Sheet content
if (openBottomSheet) {
    ModalBottomSheet(
        onDismissRequest = { openBottomSheet = false },
        sheetState = bottomSheetState,
    ) {
        Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
            Button(
                // Note: If you provide logic outside of onDismissRequest to remove the sheet,
                // you must additionally handle intended state cleanup, if any.
                onClick = {
                    scope.launch { bottomSheetState.hide() }.invokeOnCompletion {
                        if (!bottomSheetState.isVisible) {
                            openBottomSheet = false
                        }
                    }
                }
            ) {
                Text("Hide Bottom Sheet")
            }
        }
    }
}

For more to read: link.

Jeevan Rupacha
  • 3,055
  • 1
  • 15
  • 29
  • What about BottomSheetScaffold? – IgorGanapolsky Feb 28 '23 at 13:09
  • 1
    @IgorGanapolsky available in `androidx.compose.material3:material3:1.1.0-alpha08` – Andrei R Mar 08 '23 at 21:42
  • Since `androidx.compose.material3:material3:1.1.0-alpha08` you can't use `rememberSheetState` and `skipHalfExpanded`, so you should edit you code from `val bottomSheetState = rememberSheetState(skipHalfExpanded = true)` to `val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)` – Andrei R Mar 09 '23 at 22:53
  • 2
    I just got even worse then the normal ModalBottomSheet of material library, before you could just use the bottom sheet state to show/hide, but now you have to control it over a flag cause if you use the state and try to hide it, it keeps there with a little offset and you can't interact with the screen behind... LOL – Gustavo Ross Mar 16 '23 at 16:55
  • 1
    Erf.. who have a good implementation of it ? – Mehdi.ncb Apr 26 '23 at 01:23
14

UPDATE 23/02/2023

As of Compose Material3 version 1.1.0-alpha06 ModalBottomSheet is now available as an experimental composable (docs)


I got pretty similar results using a fullscreen dialog with AnimatedVisibility, here is the code if interested:

// Visibility state for the dialog which will trigger it only once when called
val transitionState = remember {
    MutableTransitionState(false).apply {
        targetState = true
    }
}

Dialog(
    onDismissRequest = {} // You can set a visibility state variable to false in here which will close the dialog when clicked outside its bounds, no reason to when full screen though,
    properties = DialogProperties(
        // This property makes the dialog full width of the screen
        usePlatformDefaultWidth = false
    )
) {

    // Visibility animation, more information in android docs if needed
    AnimatedVisibility(
        visibleState = transitionState,
        enter = slideInVertically(
            initialOffsetY = { it },
            animationSpec = ...
        ),
        exit = slideOutVertically(
            targetOffsetY = { it },
            animationSpec = ...
        )
    )
) {

    Box(
        modifier = Modifier.fillMaxSize().background(color = ...)
    ) {
        // Your layout

        // This can be any user interraction that closes the dialog
        Button(
            transitionState.apply { targetState = false }
        ) ...
    }
}

All of this is in a composable function that gets called when a UI action to open said dialog is performed, it's not ideal but it works.

Hope I was able to help!

Arthur Kasparian
  • 486
  • 2
  • 12
6

There is already a great answer by Marlinski, but i would like to add that there is also a ModalBottomSheetLayout which also does not have any implementation for Material 3.

I created a gist for people who need it in use right now: https://gist.github.com/Pasha831/bdedcfee01acdc96cf3ae643da64f88a

Pasha831
  • 63
  • 1
  • 4
4

BottomSheetScaffold is available in 1.1.0-alpha08

see https://developer.android.com/jetpack/androidx/releases/compose-material3#1.1.0-alpha08

kassim
  • 3,880
  • 3
  • 26
  • 27
  • Now also in [stable (1.1.0)](https://developer.android.com/jetpack/androidx/releases/compose-material3#1.1.0). But they removed the drawer from it. – goldensoju May 12 '23 at 04:39
2

Update July 2023

BottomSheetScaffold of M2 is now part of M3 as experimental API

Here is a example how you can use it

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.BottomSheetScaffold
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.rememberBottomSheetScaffoldState
import androidx.compose.runtime.rememberCoroutineScope

val scope = rememberCoroutineScope()
val scaffoldState = rememberBottomSheetScaffoldState()

BottomSheetScaffold(
    scaffoldState = scaffoldState,
    sheetPeekHeight = 128.dp,
    sheetContent = {
    Box(
        Modifier
            .fillMaxWidth()
            .height(128.dp),
        contentAlignment = Alignment.Center
    ) {
        Text("Swipe up to expand sheet")
    }
    Column(
        Modifier
            .fillMaxWidth()
            .padding(64.dp),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("Sheet content")
        Spacer(Modifier.height(20.dp))
        Button(
            onClick = {
                scope.launch { scaffoldState.bottomSheetState.partialExpand() }
            }
        ) {
            Text("Click to collapse sheet")
        }
    }
}) { innerPadding ->
    Box(Modifier.padding(innerPadding)) {
        Text("Scaffold Content")
    }
}

Or if you like you can use standalone ModelBottomSheet as shown in code snippet below

https://github.com/android/snippets/blob/7322f58994e9ade5ffee8e49f956e4a97339f384/compose/snippets/src/main/java/com/example/compose/snippets/layouts/MaterialLayoutSnippets.kt#L362

imports:

import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.material3.ModalBottomSheet

Code

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BottomSheetDemo(title: String, modifier: Modifier = Modifier) {
    ModalBottomSheet(onDismissRequest = { /* Executed when the sheet is dismissed */ }) {
        // Sheet content
    }

    val sheetState = rememberModalBottomSheetState()
    val scope = rememberCoroutineScope()
    var showBottomSheet by remember { mutableStateOf(false) }
    Scaffold(
        floatingActionButton = {
            ExtendedFloatingActionButton(
                text = { Text(title) },
                icon = { Icon(Icons.Filled.Add, contentDescription = "") },
                onClick = {
                    showBottomSheet = true
                }
            )
        }
    ) { contentPadding ->
        // Screen content
        Box(modifier = Modifier.padding(contentPadding)) { /* ... */ }

        if (showBottomSheet) {
            ModalBottomSheet(
                onDismissRequest = {
                    showBottomSheet = false
                },
                sheetState = sheetState
            ) {
                // Sheet content
                Button(onClick = {
                    scope.launch { sheetState.hide() }.invokeOnCompletion {
                        if (!sheetState.isVisible) {
                            showBottomSheet = false
                        }
                    }
                }) {
                    Text("Hide bottom sheet")
                }
            }
        }
    }
}
Hitesh Sahu
  • 41,955
  • 17
  • 205
  • 154
  • Thanks Hitesh, until now I did not know there is a `ModalBottomSheet` and then there is the `BottomSheetScaffold`. Thanks for posting on both components on *Material 3*, we are clear, wish I could upvote * 2. – Tonnie Aug 13 '23 at 11:56