Jetpack Compose Bottomsheet with empty sheet content is always expnaded - android

I'm trying to achieve modalBottomSheet by BottomSheetScaffold for some custom implementation.
here is my BottomSheetScaffold
BottomSheetScaffold(
sheetPeekHeight = 0.dp,
scaffoldState = bottomSheetState,
sheetBackgroundColor = Color.Transparent,
backgroundColor = Color.Transparent,
sheetElevation = 0.dp,
sheetShape = RoundedCornerShape(topStart = 36.dp, topEnd = 36.dp),
snackbarHost = snackBarHost,
sheetContent = { bottomSheet() }) {
Box(Modifier.fillMaxSize()) {
val coroutineScope = rememberCoroutineScope()
sheetContent()
Scrim(
color = Primary,
alpha = bottomSheetState.currentFraction * 0.5f,
onDismiss = {
coroutineScope.launch { bottomSheetState.bottomSheetState.collapse() }
},
visible = bottomSheetState.bottomSheetState.targetValue != BottomSheetValue.Collapsed && bottomSheetState.bottomSheetState.currentValue != BottomSheetValue.Collapsed
)
}
}
When this scaffold is called by some screen, the sheetContent() will be replaced as screen content. My problem here is when bottomSheet() is empty on that screen and thus there is no height, bottom sheet state think it is expanded while I just not put composable inside bottomSheet() and it just fill based on some condition with no default composable. Because of that the Scrim() function will be visible and when I click on it this exception will throw
java.lang.IllegalArgumentException: The target value must have an associated anchor.

It seems while sheetContent is necessary for BottomSheetScaffold there is no way to deal with empty value because BottomSheetState class that handle's swiping need anchor to get height and empty value cause's unexpected result

This is a bug introduced in latest release of compose 1.2.x. To stop showing bottom sheet at start, I only add sheetContent if data is not null(this was not possible in previous release1.1.x). Now I have another issue. The click event to expanding bottomSheet won't trigger at first try. I always need to click it twice.

Related

Jetpack Compose BottomSheetScaffold Stop half way

I have tried the following code to make my BottomSheetScaffold have a peek height of 92.dp and when pulled up stop halfway or full screen but I does not seem to be accepting the swipeable. FYI, 92.dp is the minimum height of my sheet when collapsed as to be above a bottom tool bar.
Here is a snippet of my code:
val scaffoldState = rememberBottomSheetScaffoldState()
val swipeableState = rememberSwipeableState(initialValue = 0f)
val coroutineScope = rememberCoroutineScope()
BottomSheetScaffold(
scaffoldState = scaffoldState,
sheetContent = {
Column(
Modifier
.fillMaxSize()
) {
Text("Sheet content")
}
},
sheetPeekHeight = 92.dp, // start collapsed
sheetShape = RoundedCornerShape(16.dp),
sheetBackgroundColor = Color.LightGray,
sheetGesturesEnabled = true,
modifier = Modifier
.fillMaxSize()
.swipeable(
state = swipeableState,
anchors = mapOf(
0f to 0f,
0.5f to 0.5f,
0.1f to 0.1f
), // define stopping points
orientation = Orientation.Vertical,
resistance = null
)
) {
Column(Modifier.fillMaxSize()) {
TopAppBar(
title = { Text("Bottom Sheet Example") }
)
Text("Content")
}
}
What I am ultimately trying to do is have the sheet partially visible when the user navigates to the screen and then be able to swipe it all the way down to a minimum height or all the way up to be full screen.
I originally tried this by having a peek height of 200 but then the user cannot swipe it down, only up to full screen.
So, I thought of creating three anchor points so I could have the middle point the initial value and then the user can swipe down to the minimum, or up to full screen.
In the above code I am setting three anchor points but when I swipe up the sheet it goes full screen.
How do you set multiple stops points, or how can I have the sheet visible when the composable appears but yet allow the user to swipe it closed or full screen?
Thanks for any help.

Compose. ModalBottomSheetLayout appears under the hiding keyboard

I implemented ModalBottomSheetLayout which appears under the keyboard and when I hide the keyboard, it appears for a moment and disappears. I understand why it is there, but it hides later than the keyboard and it is noticeable.
Has anyone solved such a problem?
I tried to process the back button and postpone hiding the keyboard for 100 milliseconds and at this time hide the ModalBottomSheetLayout first, and then the keyboard. But Composer's BackHander is not called when the keyboard is open.
I'm also trying to make a custom ModalBottomSheetLayout now, but I decided to ask if anyone ran into such a problem.
I don't know, maybe it helps
val modalBottomSheetState = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Hidden,
animationSpec = TweenSpec(durationMillis = 400),
skipHalfExpanded = true,
)
ModalBottomSheetLayout(
sheetContent = {
ChatBottomSheet(
scope = scope,
modalBottomSheetState = modalBottomSheetState,
channel = channel.value,
poundRequestButtonClick = callbacks.poundRequestButtonClick,
)
},
sheetState = modalBottomSheetState,
sheetShape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
) {
Scaffold(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(),
) { padding -> ...
}

Kotlin Compose global footer view

I am trying to show an Ad banner at the bottom which is globally displayed. That means that I want to stick there when I navigate throughout the app.
So far I've tried adding the NavHost and the AdView inside a Column, hoping that the navigation with the linked pages will just take the remaining space and the bottom view will stick there. However the NavHost takes the whole screen and the AdView has zero height. When I navigate the second view, the AdView is still inside the view hierarchy but still with zero height.
I've tried changing all kinds of setup, adding .weight(1f) to the Ad view, changing Column Arrangement, .fillMaxSize(), .fillMaxHeight(), .minHeight(), fixed height, etc.
I might need to look into different layout for achieving this result, but not sure in which direction to search. The Ad works well, I tried displaying without the column and is shown correctly in the center of the screen.
Another variant I've tried is to display it without column, add bottom spacing to the NavHost then position the AdView to the bottom somehow. With this variant I haven't been able to position it to bottom.
Any help would be appreciated!
Column() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = NavigationRoutes.Home
) {
composable(NavigationRoutes.Home) { HomeView(navController = navController) }
composable(
NavigationRoutes.Books,
arguments = listOf(navArgument("mode") { type = NavType.IntType })
) { backStackEntry ->
val rawMode = backStackEntry.arguments?.getInt("mode") ?: 0
var mode: ContentMode = if (rawMode == 0) {
ContentMode.READING
} else {
ContentMode.QUIZ
}
BooksView(navController = navController, mode = mode)
}
}
AndroidView(
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Max)
.weight(1f),
factory = { context ->
AdView(context).apply {
setAdSize(AdSize.BANNER)
adUnitId = AdIdentifiers.GlobalBanner.adID
loadAd(AdRequest.Builder().build())
}
}
)
}
It might be the issue of any child Composable of NavHost having Modifier.fillmaxSize(). You can set a Modifier for NavHost which let's you define how much space it will cover
Column(modifier = Modifier.fillMaxSize()){
NavHost(modifier = Modifier.fillmaxWidth).weight(1f){}
AdView(modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Max)
)
}
You can put AndroidView in Box and give androidView height as bottom padding values in screens.
NavHost(
//
){
//
}
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.BottomCenter
){
AndroidView()
}

Jetpack-Compose ModalBottomSheetLayout throws java.lang.IllegalArgumentException with initial state "Hidden"

By playing with a ModalBottomSheet in Compose, I get the following issue:
java.lang.IllegalArgumentException: The initial value must have an associated anchor.
My composable function has:
a ModalBottomSheetState and a CoroutinScope,
a ModalBottomSheetLayout with,
a Scaffold as bottom sheet content.
// 1.
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val coroutineScope = rememberCoroutineScope()
// 2.
ModalBottomSheetLayout(
sheetContent = {
/* sheetContent */
},
sheetState = sheetState,
// modifier = Modifier.fillMaxSize() --> it doesn't change the result
) {
// 3.
Scaffold {
/* scaffold content */
}
}
By setting the initial state of the bottom sheet to ModalBottomSheetValue.Expanded, the issue disappears. Note the exception is also thrown for ModalBottomSheetValue.HalfExpanded and without any initial value (the default is Hidden, so it seems logic).
Is there a known workaround or a version of the library where it is working (version of compose I use: 1.0.0 and I tried with 1.0.0-rc2) ?
UPDATE
After some investigation, it seems that the issue is due to a dynamic content in the sheet content. I have there a Column/LazyColumn that recomposes when data are available. By having a fixed content, the issue disappear for any ModalBottomSheetValue.
FIX
With "null" content (understand a content with a height of 0 dp), the composable function has probably not enough information to compose the modal bottom sheet. It is the case with dynamic column content (starting with no content, so height = 0 dp).
To fix this, set a minimal height of 1 dp somewhere in the sheet content hierarchy:
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val coroutineScope = rememberCoroutineScope()
ModalBottomSheetLayout(
sheetContent = {
Box(modifier.defaultMinSize(minHeight = 1.dp)) {
/* sheet content */
}
},
sheetState = sheetState,
// modifier = Modifier.fillMaxSize() --> it doesn't change the result
) {
Scaffold {
/* scaffold content */
}
}
With "null" content (understand a content with a height of 0 dp), the composable function has probably not enough information to compose the modal bottom sheet. It is the case with dynamic column content (starting with no content, so height = 0 dp).
To fix this, set a minimal height of 1 dp somewhere in the sheet content hierarchy:
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val coroutineScope = rememberCoroutineScope()
ModalBottomSheetLayout(
sheetContent = {
Box(modifier.defaultMinSize(minHeight = 1.dp)) {
/* sheet content */
}
},
sheetState = sheetState,
// modifier = Modifier.fillMaxSize() --> it doesn't change the result
) {
Scaffold {
/* scaffold content */
}
}
you can add Spacer in sheetContent to avoid above expecption
sheetContent = {
Spacer(modifier = Modifier.height(1.dp))
Box() {
/* sheet content */
}
}
For me the approved solution didn't work.
In my case it was failing when I had a bottomSheet content with an unspecified height (lazyColumn) and I wanted the bottomSheet to wrap the height of the content by using this line when opening it:
sheetState.animateTo(ModalBottomSheetValue.HalfExpanded)
For me the solution was to wrap the sheetContent with:
Column(Modifier.fillMaxSize()).
I hope this solution can help someone else out, here is the pseudo code:
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val coroutineScope = rememberCoroutineScope()
ModalBottomSheetLayout(
sheetContent = {
Column(modifier.fillMaxSize()) { ------> The solution
/* sheet content */
}
},
sheetState = sheetState,
) {
Scaffold {
/* scaffold content */
}
}

Jetpack Compose Bottom Sheet initialization error

In Jetpack compose 1.0.0-beta01, I am calling the BottomSheetScaffold like this:
BottomSheetScaffold(
scaffoldState = bottomSheetScaffoldState,
sheetContent = { Text("") },
sheetShape = Shapes.large,
backgroundColor = AppTheme.colors.uiBackground,
modifier = modifier
) { (content) }
... and getting the following error:
java.lang.IllegalArgumentException: The initial value must have an associated anchor.
Any tips on fixing this?
Don't forget to add the following atribute:
sheetPeekHeight = 0.dp
So your code should be like this:
BottomSheetScaffold(
scaffoldState = bottomSheetScaffoldState,
sheetContent = { Text("") },
sheetShape = Shapes.large,
sheetPeekHeight = 0.dp, // <--- new line
backgroundColor = AppTheme.colors.uiBackground,
modifier = modifier
) { (content) }
When bottomSheetState is expand, sheetContent must have real content to show.
You need check this.
I got the same issue when using ModalBottomSheetLayout, and my compose material version is 1.2.0-rc02
androidx.compose.material:material:1.2.0-rc02
I want to show the bottom modal when one item is selected, and if no item is selected, the modal should be empty.
ModalBottomSheetLayout(
sheetState = modalBottomSheetState,
sheetContent = {
EditProgressContent(book = book)
}
) { ... }
#Composable
fun EditProgressContent(book: Book?) {
if (book == null) return
Text(book.title)
}
When book is null, I got the same crash. There is no peekHeight parameter for ModalBottomSheetLayout, so I have to add a pixel when book is null
#Composable
fun EditProgressContent(book: Book?) {
if (book == null) {
Box(modifier = Modifier.size(1.dp)
return
}
Text(book.title)
}
The code looks silly, hope it can be fixed from Google.
In case you end up on this page because you use the BackdropScaffold, the suggested solution does the trick as well. Simply set the peekHeight.
For example like this:
BackdropScaffold(
appBar = {},
backLayerContent = {},
frontLayerContent = {},
peekHeight = 0.dp
)
Then Preview works like a charm again.
Fun fact though: Don't set it to 56.dp which is the default init value it normally should be initialised with (value of BackdropScaffoldDefaults.PeekHeight). 56.dp results in the anchor rendering problem in my current setup.
(Using compose version '1.1.1')
On new version, this error happens when the initial State is Expanded, where try to open the Modal before launched. Try launcher via ModalBottomSheetState from LaunchedEffect (maybe can need a delay)

Categories

Resources