Jetpack Compose Scaffold + Modal Bottom Sheet - android

I'm trying to design a layout using Compose that consists of:
TopAppBar
Body (content)
BottomAppBar
A Bottom Sheet that represents a menu when clicked (Modal Bottom Sheet)
-------TopAppBar-------
------MainContent------
------BottomAppBar-----
----ModalBottomSheet---
Compose offers 3 components:
Scaffold
BottomSheetScaffold
ModalBottomSheetLayout
Scaffold has no bottom sheet property
BottomSheetScaffold has no BottomAppBar property
ModalBottomSheetLayout has only content and sheetContent
Which of these components should I combine and in what **structure** to achieve what I want?
Scaffold(
topBar = { TopBar() },
content = { innerPadding -> Body(innerPadding) },
bottomAppbar = { BottomAppBar() }
)
ModalBottomSheetLayout(
sheetState = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Hidden
),
sheetContent = { SheetContent() },
)
BottomSheetScaffold(
scaffoldState = ...,
sheetContent = { SheetContent() },
content = { ScreenContent() },
)

You can use something like:
val bottomState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
ModalBottomSheetLayout(
sheetState = bottomState,
sheetContent = {
//. sheetContent
}
) {
Scaffold(
scaffoldState = scaffoldState,
topBar = {
TopAppBar(
title = {
Text(text = "TopAppBar")
}
)
},
bottomBar = {
BottomAppBar(modifier = Modifier) {
IconButton(
onClick = {
coroutineScope.launch { bottomState.show() }
}
) {
Icon(Icons.Filled.Menu, contentDescription = "Localized description")
}
}
},
content = { innerPadding ->
//...main content
}
)
}

Related

Compose BottomNavigation padding

Im trying to properly apply a bottom window inset on the BottomNavigation composable. I have an edge to edge scaffold it looks like that:
When using:
modifier.navigationBarsPadding()
On the bottom navigation im getting the following:
I'm trying to achieve the following:
Scaffold(
modifier = Modifier
.background(brush = NanitColors.blueGradient),
topBar = {
topBar()
},
bottomBar = {
if (shouldShowBottomBar) {
BottomNavigationBar(navController)
}
},
scaffoldState = scaffoldState,
drawerContent = {
Drawer { route ->
scope.launch {
scaffoldState.drawerState.close()
}
navController.navigate(route.route) {
popUpTo(navController.graph.startDestinationId)
launchSingleTop = true
}
currentDestination = route
}
},
drawerGesturesEnabled = scaffoldState.drawerState.isOpen,
) { innerPadding ->
NavigationHost(navController = navController, modifier = Modifier.padding(innerPadding))
}
val topBar: #Composable () -> Unit = {
MainToolbar(
modifier = modifier.statusBarsPadding(),
title = title ?: "",
onMenuClicked = {
scope.launch {
scaffoldState.drawerState.open()
}
}
)
}
BottomNavigation(
modifier = modifier.navigationBarsPadding()
) {
items.forEach { item ->
BottomNavigationBarItem(
item = item,
isSelected = selectedItem?.route == item.route,
onSelectedChange = { onSelectedItemChange(item) }
)
}
}
You can use accompanist systemuicontroller to set color to navigation bar, use default color for other screens and add color just for the screen you need:
#Composable
fun AppTheme(
systemBarColor: Color = MaterialTheme.colors.background,
content: #Composable () -> Unit,
) {
...
SideEffect {
systemUiController.setNavigationBarColor(
color = systemBarColor,
darkIcons = useDarkIcons
)
}
}

ModalDrawer Jetpack Compose - DrawerContent vs Content?

I'm trying to implement ModalDrawer with Jetpack Compose. Now I'm using ModalDrawer composable function which requires two main parameters: DrawerContent and Content. This is what kind of result I get when I pass an empty content parameter and few elements inside a DrawerContent parameter. I get those two drawers, instead of one. It's really confusing.
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = "Home") }
)
},
drawerContent = {
val drawerState = rememberDrawerState(DrawerValue.Open)
ModalDrawer(
drawerState = drawerState,
drawerContent = { MyDrawerContent() },
content = { }
)
},
drawerContentColor = MaterialTheme.colors.onSurface,
content = {}
)
Better to use or only ModalDrawer without scaffold or Scaffold with drawer content
because ModalDrawer already have drawerContent parameter so here is showing two drawers because of two drawer contents (nested).
Scaffold(
drawerContent = {
Text("Drawer title", modifier = Modifier.padding(16.dp))
Divider()
// Drawer items
}
) {
// Screen content
}
OR
ModalDrawer(
drawerState = drawerState,
drawerContent = {
Column {
Text("Text in Drawer")
Button(onClick = {
scope.launch {
drawerState.close()
}
}) {
Text("Close Drawer")
}
}
},
content = {
Column {
Text("Text in Bodycontext")
Button(onClick = {
scope.launch {
drawerState.open()
}
}) {
Text("Click to open")
}
}
}
)

Jetpack Compose collapse Bottom Sheet on outside click

I currently display a Bottom Sheet through a BottomSheetScaffold and want to collapse it when the user clicks outside the Bottom Sheet. Is there a way to detect the click outside of the Bottom Sheet?
This is the screen with my BottomSheetScaffold:
#ExperimentalMaterialApi
#ExperimentalMaterial3Api
#Composable
fun HomeScreen() {
val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed)
)
val coroutineScope = rememberCoroutineScope()
BottomSheetScaffold(
scaffoldState = bottomSheetScaffoldState,
sheetContent = {
Box(
Modifier
.fillMaxWidth()
.fillMaxHeight(0.9f)
) {
Text("Hello from Sheet")
}
},
sheetShape = RoundedCornerShape(
topStart = Spacing.l,
topEnd = Spacing.l
),
sheetPeekHeight = LocalConfiguration.current.screenHeightDp.dp * 0.15f,
sheetBackgroundColor = MaterialTheme.colorScheme.surface,
) {
Scaffold() {
Button(
onClick = {
coroutineScope.launch {
if (bottomSheetScaffoldState.bottomSheetState.isCollapsed) {
bottomSheetScaffoldState.bottomSheetState.expand()
} else {
bottomSheetScaffoldState.bottomSheetState.collapse()
}
}
},
) {
Text("Toggle Sheet")
}
}
}
}
This is a visualization of the area in which I want to detect the click if the Bottom Sheet is expanded.
You can add the pointerInput modifier with detectTapGestures to your Scaffold:
Scaffold( modifier =
Modifier.pointerInput(Unit) {
detectTapGestures(onTap = {
coroutineScope.launch {
if (bottomSheetScaffoldState.bottomSheetState.isCollapsed) {
bottomSheetScaffoldState.bottomSheetState.expand()
} else {
bottomSheetScaffoldState.bottomSheetState.collapse()
}
}
})
}){
//.....
}
I had the same problem using the BottomSheetScaffold, and I changed to the ModalBottomSheetLayout that already has that behavior by default.
I hope this could help anybody with the same problem

What is Scaffold? Jetpack compose

I want to know what is Scaffold in jetpack compose with a BottomAppBar example can anyone help me
Scaffold
Scaffold allows you to implement a UI with the basic Material Design layout structure. Scaffold provides slots for the most common top-level Material components, such as TopAppBar, BottomAppBar, FloatingActionButton, and Drawer.
Something like:
val scaffoldState = rememberScaffoldState()
// Create a coroutineScope for the animation
val coroutineScope = rememberCoroutineScope()
Scaffold(
scaffoldState = scaffoldState,
drawerContent = { Text("Drawer content") },
bottomBar = {
BottomAppBar(cutoutShape = CircleShape) {
IconButton(
onClick = {
coroutineScope.launch { scaffoldState.drawerState.open() }
}
) {
Icon(Icons.Filled.Menu, contentDescription = "....")
}
}
},
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text("Action") },
onClick = { /* .... */ }
)
},
floatingActionButtonPosition = FabPosition.Center,
isFloatingActionButtonDocked = true,
content = { innerPadding ->
//....
}
)

Add drawer toggle button in Jetpack compose

I want to add the hamburger icon to the Appbar and open/close drawer using the icon.
How would I achieve this?
Scaffold(
drawerShape = RoundedCornerShape(topRight = 10.dp, bottomRight = 10.dp),
drawerElevation = 5.dp,
drawerContent = {
// Drawer
},
topBar = {
TopAppBar(
navigationIcon = {
Icon(
Icons.Default.Menu,
modifier = Modifier.clickable(onClick = {
// Open drawer => How?
})
)
},
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(bottomLeft = 10.dp, bottomRight = 10.dp)),
title = { Text(text = "Hello") }
)
},
) {}
Use rememberScaffoldState() to modify drawer state.
create a variable:
val state = rememberScaffoldState()
val scope = rememberCoroutineScope() // in 1.0.0-beta `open()` and `close` are suspend functions
Pass the state to Scaffold
Scaffold(
scaffoldState = state,
// ...
)
Use state.drawerState.open() or state.drawerState.close() in an onClick to open/close the drawer.
Create an Icon for navigationIcon in TopAppBar:
val state = rememberScaffoldState()
Scaffold(
scaffoldState = state,
topBar = {
TopAppBar(
title = { Text(text = "AppBar") },
navigationIcon = {
Icon(
Icons.Default.Menu,
modifier = Modifier.clickable(onClick = {
scope.launch { if(it.isClosed) it.open() else it.close() }
})
)
}
)
},
drawerShape = RoundedCornerShape(topRight = 10.dp, bottomRight = 10.dp),
drawerContent = {
Text(text = "Drawer")
}
) {
// Scaffold body
}
You can use:
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = scaffoldState,
drawerContent = { Text("Drawer content") },
topBar = {
TopAppBar(
modifier = Modifier
.clip(RoundedCornerShape(bottomStart = 8.dp, bottomEnd = 8.dp))
) {
IconButton(
onClick = {
scope.launch { scaffoldState.drawerState.open() }
}
) {
Icon(Icons.Filled.Menu,"")
}
}
},
content = {
//bodyContent()
})

Categories

Resources