the default ModalBottomsheet is not give me a flexibility to customize the UI to look like the UI in the screenshot

You can customize the ModalBottomSheet to match the UI from the picture, and here is an example:
Firstly, add the following parameters to the BottomSheet composable function:
isBottomSheetVisible: Boolean - Used for controlling the visibility of the bottom sheet.sheetState: SheetState - The state of the bottom sheet.onDismiss: () -> Unit - The callback to dismiss the bottom sheet.Then, wrap the ModalBottomSheet inside if (isBottomSheetVisible) {} block and change the following sheet properties:
onDismissRequest to onDismiss.sheetState to sheetState.containerColor to Color.Transparent.contentColor to your content color e.g. a text color.shape to a RectangleShape.dragHandle to nullscrimColor to the scrim color of the design (e.g. Color.Black.copy(alpha = .5f)).windowInsets to WindowInsets(0, 0, 0, 0) if you have Edge-To-Edge enabled, to expand the bottom sheet under the system bars.So far, the BottomSheet composable should look like this:
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BottomSheet(
isBottomSheetVisible: Boolean,
sheetState: SheetState,
onDismiss: () -> Unit
) {
if (isBottomSheetVisible) {
ModalBottomSheet(
onDismissRequest = onDismiss,
sheetState = sheetState,
containerColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.onSurface,
shape = RectangleShape,
dragHandle = null,
scrimColor = Color.Black.copy(alpha = .5f),
windowInsets = WindowInsets(0, 0, 0, 0)
) {
// Implement the custom layout here ...
}
}
}
Now, you can start implementing the custom layout inside the content block of the ModalBottomSheet:
ModalBottomSheet can be wrapped inside a Box with a bit of padding and customizing it further:Box(
modifier = Modifier
.statusBarsPadding()
.fillMaxWidth()
.padding(8.dp),
contentAlignment = Alignment.Center
) {
FilledIconButton(
modifier = Modifier.size(48.dp),
onClick = onDismiss,
colors = IconButtonDefaults.filledIconButtonColors(
containerColor = MaterialTheme.colorScheme.background
)
) {
Icon(
imageVector = Icons.Rounded.Close,
contentDescription = "Dismiss the dialog."
)
}
}
Box add a Column, and implement the remaining UI components:Column(
modifier = Modifier
.navigationBarsPadding()
.padding(12.dp) // Outer padding
.clip(shape = RoundedCornerShape(24.dp))
.background(color = MaterialTheme.colorScheme.background)
.fillMaxWidth()
.padding(24.dp) // Inner padding
) {
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = "10th avenue, Some, State",
onValueChange = {},
label = { Text(text = "Delivery Address") },
readOnly = true,
shape = RoundedCornerShape(12.dp)
)
Spacer(modifier = Modifier.height(24.dp))
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = "09090606000",
onValueChange = {},
label = { Text(text = "Number we can call") },
readOnly = true,
shape = RoundedCornerShape(12.dp)
)
Spacer(modifier = Modifier.height(48.dp))
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
OutlinedButton(
onClick = {},
content = { Text(text = "Pay on delivery") }
)
OutlinedButton(
onClick = {},
content = { Text(text = "Pay with card") }
)
}
}
Finally, wherever you need the bottom sheet, declare the following variables:
scope - A coroutine scope used to efficiently expand or hide the bottom sheet.isBottomSheetVisible - Used for controlling the bottom sheet visibility.sheetState - The state of the bottom sheet.Then, implement the BottomSheet composable:
val scope = rememberCoroutineScope()
var isBottomSheetVisible by rememberSaveable { mutableStateOf(false) }
val sheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true
)
BottomSheet(
isBottomSheetVisible = isBottomSheetVisible,
sheetState = sheetState,
onDismiss = {
scope.launch { sheetState.hide() }
.invokeOnCompletion { isBottomSheetVisible = false }
}
)
This is the full code that I've wrote and a visual demonstration:
class MainActivity : ComponentActivity() {
@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
val scope = rememberCoroutineScope()
var isBottomSheetVisible by rememberSaveable { mutableStateOf(false) }
val sheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true
)
// A box with a simple button
// just to show the bottom sheet.
Box(
modifier = Modifier
.fillMaxSize()
.systemBarsPadding()
.padding(64.dp),
contentAlignment = Alignment.BottomCenter
) {
OutlinedButton(
onClick = {
scope.launch {
isBottomSheetVisible = true
sheetState.expand()
}
}
) {
Text(text = "Show")
}
}
BottomSheet(
isBottomSheetVisible = isBottomSheetVisible,
sheetState = sheetState,
onDismiss = {
scope.launch { sheetState.hide() }
.invokeOnCompletion { isBottomSheetVisible = false }
}
)
}
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BottomSheet(
isBottomSheetVisible: Boolean,
sheetState: SheetState,
onDismiss: () -> Unit
) {
if (isBottomSheetVisible) {
ModalBottomSheet(
onDismissRequest = onDismiss,
sheetState = sheetState,
containerColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.onSurface,
shape = RectangleShape,
dragHandle = null,
scrimColor = Color.Black.copy(alpha = .5f),
windowInsets = WindowInsets(0, 0, 0, 0)
) {
Box(
modifier = Modifier
.statusBarsPadding()
.fillMaxWidth()
.padding(8.dp),
contentAlignment = Alignment.Center
) {
FilledIconButton(
modifier = Modifier.size(48.dp),
onClick = onDismiss,
colors = IconButtonDefaults.filledIconButtonColors(
containerColor = MaterialTheme.colorScheme.background
)
) {
Icon(
imageVector = Icons.Rounded.Close,
contentDescription = "Hide the dialog."
)
}
}
Column(
modifier = Modifier
.navigationBarsPadding()
.padding(12.dp) // Outer padding
.clip(shape = RoundedCornerShape(24.dp))
.background(color = MaterialTheme.colorScheme.background)
.fillMaxWidth()
.padding(24.dp) // Inner padding
) {
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = "10th avenue, Some, State",
onValueChange = {},
label = { Text(text = "Delivery Address") },
readOnly = true,
shape = RoundedCornerShape(12.dp)
)
Spacer(modifier = Modifier.height(24.dp))
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = "09090606000",
onValueChange = {},
label = { Text(text = "Number we can call") },
readOnly = true,
shape = RoundedCornerShape(12.dp)
)
Spacer(modifier = Modifier.height(48.dp))
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
OutlinedButton(
onClick = {},
content = { Text(text = "Pay on delivery") }
)
OutlinedButton(
onClick = {},
content = { Text(text = "Pay with card") }
)
}
}
}
}
}

Happy coding! 🙌🏼
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With