Jetpack Compose BottomSheetScaffold: Smooth closing - android-jetpack-compose

I'm using Jetpack Compose with the BottomSheetScaffold. To be able to show and hide the bottom sheet from both within and outside the composable, I used a showBottomSheet: MutableState<Boolean> variable. The peek height within the composable is then determined like this:
val baseBottomSheetPeekHeight by remember { mutableStateOf(60.dp) }
val bottomSheetPeekHeight = if (showBottomSheet.value) baseBottomSheetPeekHeight else 0.dp
Later, in the BottomSheetScaffold, I use the variable like this:
BottomSheetScaffold(
...
sheetPeekHeight = bottomSheetPeekHeight,
...
)
(Full reproducer project here: https://github.com/dbrgn/compose-repro)
This generally works as intended, I can set showBottomSheet.value to false to hide the bottom sheet. However, the hiding looks janky, because not all sub-composables are hidden at the same time.
It's a bit hard to see in the animation above due to the GIF conversion, but when closing the bottom sheet peek pane, the other content (below it) is visible for a short moment, before the bottom sheet disappears.
Is there a way to avoid this janky hiding behavior? Or even better, is there a way to smoothly animate the hiding of the pane?

In my case for Smooth closing BottomSheetScaffold I used to: scaffoldState.bottomSheetState.animateTo(Collapsed, tween(duration))
-- (when duration is any Int you want).
The same for Expanding:
scaffoldState.bottomSheetState.animateTo(Expanded, tween(duration))
If you look at the source code of collapse() or expand() function, you will see there just calling animateTo(Expanded) and animateTo(Collapsed). You can customize animateTo() as you wish.
See animateTo() documentation.

Related

Unable use a TextField at the bottom of the screen in a LazyColumn

I have this code
setContent {
val items = mutableListOf<Int>().apply {
(1..100).forEach { add(it) }
}
LazyColumn {
items(items) { item ->
TextField("$item", {})
}
}
}
With android:windowSoftInputMode="adjustResize" in my AndroidManifest.xml.
If I click on a TextField at the top of the list, I can enter text fine.
If I click on a TextField near the bottom of the screen, the keyboard appears momentarily, then disappears quickly after, and prevents me from entering text.
How can I enter text when the TextField is at the bottom of the screen? Thanks!
Your keyboard disappears after having appeared for a brief, shiny moment. Here's the reason:
You tap the TextField, it calls it's built-in focus requestor, and requests the focus from the OS by calling appropriate (or inappropriate, who's to say) internal methods, as a result of which, the keyboard pops out (again, built-in mechanism.) and you can type all the more you want... AS LONG AS THE FIELD IS VISIBLE; or in Compose terms, long as the Composable holding the requestor, is in composition.
It should be clear, that once the Composable that owns the focus requestor goes off the screen, it is destroyed (well not every time, there are only certain specific cases when it is destroyed..., but yours is one of them), and when the Composable is destroyed, the focus requestor is destroyed. No focus requestor, no focus owner - the keyboard vanishes.
Now this is really important because you are using a LazyColumn, a lazy Composable, infamous for its merciless slaughtering of the Composables that are no longer visible to the user. HENCE, as long as it is the top (or other "visible") textfield that is in concern, it stays and works as expected. However, the bottom textfield, as the keyboard pops up, goes so much out of the visible bounds, that it gets slaughtered (recycled is the technical term, but what fun is that?), taking away the purpose of the keyboard.
Now, the lazy column, like any criminal, left a clue behind, and didn't notice it, which lead us to catch him red-handed. It is quite interesting, really, to see the column in action.
Now, workarounds include using a column, instead of a lazy column; creating your own lazy column using a custom lazy Composable, and manually handling the heap size for the Composable so the final field doesn't go out of composition.
That's it, you're cured.
Just leaving another answer, since OP didn't verify other purpose of him specifying adjustResize,if whether removing or changing it will be fine, but another solution aside from the mentioned comment about different API version is specifying adjustPan, which worked in my case, either
via AndroidManifest
android:windowSoftInputMode="adjustPan"
or programmatically
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
Same code base but with adjustPan
This issue is fixed in the current alpha version 1.4.0-alpha05
https://issuetracker.google.com/issues/179203700

Jetpack Compose - Change default Visible % in ModalBottomSheetLayout

I have a ModalBottomSheetLayout with a list of items in my Compose view, which can be showed by some UI interaction.
By default, when bottomSheetState.show() is called, the visible ratio for the BottomSheet is 50%. However, this is not ideal from a UX perspective as the user will have to physically pull up the bottom sheet to see all the contents in the list.
Extremely frustrating is the fact that bottomSheetState.show() does not take in any parameters, and that the 50% value seems to be hard coded in. According to the declaration in androix.compose.material:
suspend fun show() {
val targetValue =
if (isHalfExpandedEnabled) HalfExpanded
else Expanded
animateTo(targetValue = targetValue)
}
I would like to instead show a custom value, say 75%, when the bottom sheet is showed, but so far I haven't found a way to do so. Is there a workaround to this?
I am not sure if you can make it 75% visible but you can show it expanded
bottomSheetState.animateTo(ModalBottomSheetValue.Expanded)

Modal Bottom Sheet in Jetpack Compose for Activity

I have bottom navigation bar. So my composable functions do not take up the full screen size.
When I use ModalBottomSheetLayout, it opens in the composable fun, not in the activity, like when i was using it before when using BottomSheetDialogFragment.
I don't want to write my ModalBottomSheetLayout on the upper level, I think that this is not the right approach.
And i don't want to use the old BottomSheetDialogFragment, since compose is everywhere in a project.

Jetpack Compose - MotionLayout with scrollableView

I have a Compose MotionLayout with :
a title at the middle of the screen first
a bellow content which can be a LazyColumn or a Column with verticalScroll()
When I dragUp the content, the title must go to the top of the screen (it works) and the content must remain bellow (it works too).
I use NestedScrollConnection to detect when there is a DragUp on my List and apply it to my swipeableState (swipeableState.performDrag(...)).
The problem is that my LazyColumn/scrollable column scrolls inside itself during the MotionLayout top animation.
I do not remember in XML I had this issue.
How to handle it?

Firemonkey: TSpeedButton IsPressed no visible Effect?

How do I set a TSpeedButton (on a TToolbar) to look pressed down? It's only highlighted when clicking but gets normal when the mouse leaves it. I set the property Stayspressed to True and indeed the property IsPressed becomes and stays True, but this state is not visible.
What style are you using? Some of the included ones do not have every effect included.
First, revert back to the windows style. If that works, then it's definitely a styling issue, rather than a code issue.
If you want to add an effect (actually an animation) to another style:
Right click on a button and select Edit Default Style.
Find the Fill.Color property of the Rectangle you want to show the anmimation.
Click the animation icon and add a TColorAnimation
Set: the StartValue (the existing value).
The StopValue
The Trigger: IsPressed=True
Repeat the above to add a second animation with the Start and StopValues reversed and Trigger of IsPressed=False
This is a tough one - the standard TButton style includes effects which work - the button will look pressed when "IsPressed" is true. However, other styles (and especially your own) can be much more problematic. I have found the best solution is to have 2 styles and change the style when the button is pressed.
I found it pretty impossible to get "mouseover" animation to work with "pressed" animation in buttons - normally you want to highlight the button slightly when the mouse passes over and then add an effect when you press - especially when you don't want the default shadow effect but a colour effect (which I prefer, I don't like the inner shadow effect of a default button press), but I always seemed to end up with issues. Either one effect wouldn't fire OR the button state wouldn't return to what you'd expect (visually I mean).
My advice - add a little bit of extra code to change the style for the pressed button, you might go mad otherwise :-)

Resources