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
Related
I want to use
When the user arrives on the page, the bottomsheet pops at the top of the screen before beeing dragged down to the bottom of the screen (where it is supposed to be)
Here is a code that reproduces the issue :
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun testBottomsheet(){
val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
drawerState = rememberDrawerState(DrawerValue.Open),
bottomSheetState = BottomSheetState(BottomSheetValue.Expanded),
)
BottomSheetScaffold(
scaffoldState = bottomSheetScaffoldState,
sheetContent = {
Box(
Modifier
.fillMaxWidth()
.height(200.dp)
) {
Text(text = "Hello from sheet")
}
}, sheetPeekHeight = 0.dp
) {
Button(onClick = {
}) {
Text(text = "Expand/Collapse Bottom Sheet")
}
}
}
Im working with Jetpack Compose and having a ModalBottomSheetLayout with a TextField. While Closing the bottom sheet, the keyboard it not going off.
Keyboard should hide, when ModalBottomSheetLayout dismisses.
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun ModalBottomSheetSample() {
val state = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden,)
val scope = rememberCoroutineScope()
BackHandler(state.isVisible) {
scope.launch {state.hide()}
}
ModalBottomSheetLayout(sheetState = state, sheetContent = {
TextField(value = "",
onValueChange = {},
placeholder = { Text(text = "Search") },
modifier = Modifier
.padding(10.dp)
.fillMaxWidth()
)
LazyColumn {items(50) {...}}
}) {
Button(onClick = { scope.launch { state.show() } }) {
Text("Click to show sheet")
}
}
}
have referred this question, but this is not applicable for this scenario. As this is based on Composable.
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
)
}
}
Let say i have a two screen;
#Composable
fun Screen1(
toScreen2:() -> Unit
) {
Box(
modifier = Modifier.fillMaxSize().border(4.dp, Color.Gray),
contentAlignment = Alignment.Center
) {
Text(text = "Screen 1")
Button(onClick = { toScreen2() }) {
Text(text = "To Screen 2")
}
}
}
#Composable
fun Screen2(
toScreen1:() -> Unit
) {
Box(
modifier = Modifier.fillMaxSize().border(4.dp, Color.Gray),
contentAlignment = Alignment.Center
) {
Text(text = "Screen 2")
Button(onClick = { toScreen1() }) {
Text(text = "To Screen 1")
}
}
}
and top bar that I can control the visibility;
#Composable
fun MyTopBar(
currentRoute: String?
) {
val isVisible = currentRoute == "Screen1"
AnimatedVisibility(
visible = isVisible,
enter = fadeIn(),
exit = fadeOut()
) {
TopAppBar(backgroundColor = Color.Red){}
}
}
Sccafold
val scaffoldState = rememberScaffoldState()
val navController = rememberNavController()
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
Scaffold(
modifier = Modifier
.fillMaxSize(),
scaffoldState = scaffoldState,
topBar = { MyTopBar(currentRoute) },
backgroundColor = Color.White
){
NavHost(
navController = navController,
startDestination = "Screen1"
){
composable("Screen1"){
Screen1(toScreen2 = { navController.navigate("Screen2") })
}
composable("Screen2"){
Screen2(toScreen1 = { navController.navigate("Screen1") })
}
}
}
How can i prevent this delayed effect;
i want to instant efect that simultaneously with the disappearance speed of the bottom bar. You can see that the border of the screen lags behind the topbar disappearance speed.
Remove the AnimatedVisibility from the MyTopBar.
You have animations for the TopBar visibility. So it won't be instantaneous.
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
}
)
}