Compose. ModalBottomSheetLayout appears under the hiding keyboard - android

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 -> ...
}

Related

Compose Textfield cursor remainsfor a while after navigating to a new screen

After setting up a TextField in the TopAppBar. I encountered two problems:
Textfiled gains focused after folding keyboard. When textfield is not triggered, it looks like pic 1. And when I press it(it is focused), the keyboard shows up and tesxfield looks like pic 2. If I fold the keyboard manually, the textfiled keeps pic 2 look. I wish it can turn back to pic 1 look.
Editting cursor (the pin shape stuff) remains on the screen when I switch to a new screen. pic3 shows the cursor. The color is the same as the background. I'll change it. When I switch to another screen, it remains there like pic 4 shows. It only lasts for a split second and disappears. But it is there.
For the first problem, I must missed some parameter I gusess.
I have no idea for the second one. Is it a compose bug?
#Composable
fun SearchBar() {
var text by remember { mutableStateOf("") }
Box(
modifier = Modifier
.height(70.dp)
.fillMaxWidth()
.background(MaterialTheme.colors.primary),
contentAlignment = Alignment.BottomCenter
) {
TextField(
leadingIcon = {
Icon(imageVector = Icons.Default.Search, contentDescription = null)
},
placeholder = {
Text(
"find more...",
color = Color.White
)
},
shape = RectangleShape,
colors = TextFieldDefaults.textFieldColors(
textColor = Color.White,
backgroundColor = MaterialTheme.colors.primary,
focusedIndicatorColor = Color.White,
unfocusedIndicatorColor = Color.Gray,
disabledIndicatorColor = Color.Black
),
value = text,
singleLine = true,
onValueChange = { text = it },
modifier = Modifier
.padding( bottom = 10.dp)
.size(360.dp,60.dp),
)
}
}
Ok thanks for the extra details, try this!
For part 1) this code can be added to the TextField to remove focus:
val focusManager = LocalFocusManager.current
TextField(
...
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = {
keyboardController?.hide()
focusManager.clearFocus(force = true)
}),
)
Note the Done option will show a little check box in the keyboard you can use to close the keyboard and then this code will remove focus. I'm hoping onDone will also be called when manually closing the keyboard.
For part 2) I would reuse the approach. It sounds like a bug, but basically I would try calling LocalFocusManager.current.clearFocus(force = true) from where ever you are doing the navigation away from this screen, right before leaving, to force the thing to disappear.
Hope this helps!

How to show block of dynamic or static information at bottom of screen

I want to display a sticky bar at the bottom of the screen to show dynamic or static data similar to the bars in the image below. Can this be achieved with BottomSheetScaffold on screen load without user interaction? If not, how else can this be implemented?
EDIT: I want to achieve this in Jetpack Compose.
This UI collection might be a good starting point for you to see which elements/classes you need. It looks like you're depicting a BottomSheet element? Maybe an AndroidSweetSheet element - Both of these look like they'd suit your purpose. You don't need the animations or can turn off the resizable, if you want them static. I'd start there with that huge collection of curated UI elements/widgets anyway, see if those two, or perhaps some other of the many UI element better fits your purpose.
For example, this is the AndroidSweetSheet: (1)
And heres the BottomSheet (2):
Let the page load (lots of example images so may take a little while depending on your connection/CPU) but theres several that look like what you want. May have extras that you can turn off, such as the resizable nature of that AndroidSweetSheet, you can have it appear rather than slide up & scroll
Just having a look through that UI elements page (for my own app purposes) I noticed this one 3 which might suit your purposes - imagine having a star pop up instead of the play icon, and when the user presses the star, it reveals your "Go premium" panel instead of the music player widget:
OP commented that they're specifically looking for jetpack compose - what about this bottom-sheet?
Although this example looks very bare-bones, the structure looks pretty close to what you're describing in yout text/images:
You'd probably have the pop-up controlled so that it pops-up when you want it to, instead of the user clicking a button.
Edits: some more examples
This code tutorial also suggests building a bottom sheet using jetpack compose.
I'd think you'd probably want your "go premium" box to be modal, and this page does just that, again with jetpack compose - looks prettier, too!
You can consider this one where you can utilize LaunchedEffect with a Unit key to show the bottom sheet immediately on first composition, and closing it using an Icon placed at the end of the gray "header".
All these codes are copy-and-paste-able so you can run it with no issues.
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun MyScreenAutoShowBottomSheet() {
val contentBackground = Color(0xFFa593b8)
val sheetPeekHeight = 40.dp
val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed)
)
LaunchedEffect(key1 = Unit){
bottomSheetScaffoldState.bottomSheetState.expand()
}
val coroutineScope = rememberCoroutineScope()
BottomSheetScaffold(
scaffoldState = bottomSheetScaffoldState,
sheetBackgroundColor = contentBackground,
sheetElevation = 0.dp,
sheetPeekHeight = sheetPeekHeight,
sheetContent = {
Column(
modifier = Modifier
.padding(top = sheetPeekHeight)
.wrapContentHeight()
.fillMaxWidth()
.background(Color.DarkGray)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
modifier = Modifier.padding(8.dp),
text = "Go Premium"
)
Column(
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.End
) {
Icon(
modifier = Modifier
.clickable {
coroutineScope.launch {
bottomSheetScaffoldState.bottomSheetState.collapse()
}
},
imageVector = Icons.Default.Add,
contentDescription = null
)
}
}
Box(
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
.background(Color(0xFF4fc992))
)
}
},
floatingActionButton = {
FloatingActionButton(
backgroundColor = Color(0xFF4a6ebd),
shape = CircleShape,
onClick = {},
) {
Icon(imageVector = Icons.Filled.Add, contentDescription = "icon")
}
}
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(contentBackground)
)
}
}
A simpler solution without the use of sheets is to use a Box composable with the last element having a Modifier.align(Alignment.BottomCenter) modifier:
Box(
modifier = Modifier.fillMaxSize()
) {
//Main screen contents
Column() {...}
...
Row (
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
.height(50.dp)
.background(Color(0xFF4fc992))
.padding(10.dp),
verticalAlignment = Alignment.CenterVertically
) {
//Bottom bar contents
}
}

How to use both "ModalDrawer" and "ModalBottomSheetLayout" at the same time

My app needs ModalDrawer & ModalBottomSheetLayout at the same time.
I searched but couldn't find the officially recommended way to accomplish it.
So I just put ModalDrawer inside ModalBottomSheetLayout. Please check the below code.
#Composable
fun MainScreen()
{
ModalBottomSheetLayout(
sheetState = recipeViewModel.bottomSheetState,
sheetContent = { #AnotherComposable },
sheetShape = RoundedCornerShape(topStart = 50.dp, topEnd = 50.dp)
) {
// "Rest-Content Area"
Column(
modifier = Modifier
.padding(10.dp)
.fillMaxWidth()
) {
Buttons()
}
// Drawer
ModalDrawerSample()
}
}
ModalDrawerSample() working fine without any issue.
However, the UIs in the "Rest-Content Area" is not working. For example, button clicking it not working at all.
When we press the button, the color of the button area should be changed (gray like colored) but it's not.
Is there any official document that recommend the best practice or any other solution for it?

Layout not moving up when keyboard is open in Jetpack Compose

I have a layout in Jetpack Compose where I have a couple of composables (text view and input) that I need to stay at the top of the screen, and then another composable (button) that I need to stay attached to the bottom of the screen. When the keyboard is opened to type into the text input, I need the button attached to the bottom of the screen to move up with it, however this isn't happening.
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
Column(
verticalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.fillMaxSize()
.statusBarsPadding()
.padding(0.dp, 32.dp, 0.dp, 0.dp)
.navigationBarsWithImePadding()
.verticalScroll(rememberScrollState()),
) {
Column {
CustomHeading6(
modifier = Modifier.padding(0.dp, 0.dp, 0.dp, 32.dp),
title = "Enter Email"
)
CustomBody1(
modifier = Modifier.padding(0.dp, 0.dp, 0.dp, 12.dp),
content = "Email:"
)
CustomEmailTextField(
modifier = Modifier.padding(0.dp, 0.dp, 0.dp, 20.dp),
text = viewModel.emailAddress,
onValueChanged = { //removed }
)
}
Column {
CustomButton(
modifier = Modifier.padding(0.dp, 32.dp),
title = "Submit",
onButtonClick = { //removed }
)
}
}
}
I tried to achieve the attach to top/bottom by wrapping each set of items in their own columns and then using Arrangement.SpaceBetween on the wrapper column, but perhaps this isn't correct. I also tried without this and instead adding .weight(1f) to the column with the composables attached to the top. Neither of these allowed the layout to move up with the keyboard.
I also have this in the main activity:
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
ProvideWindowInsets {
Theme {
// my content
}
}
}
and this in the manifest for my activity:
android:windowSoftInputMode="adjustResize"

Jetpack Compose Bottomsheet with empty sheet content is always expnaded

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.

Categories

Resources