I am trying to overlap two different compose elements. I want to show a toast kind of message at the top whenever there is an error message. I don't want to use a third party lib for such an easy use case. I plan to use the toast in every other composable screen for displaying error message. Below is the layout which i want to achieve
So I want to achieve the toast message saying "Invalid PIN, please try again".
#Composable
fun MyToast(title: String) {
Card(
modifier = Modifier
.absoluteOffset(x = 0.dp, y = 40.dp)
.background(
color = MaterialTheme.colors.primaryVariant,
shape = RoundedCornerShape(10.dp)
), elevation = 20.dp
) {
Row(
modifier = Modifier
.background(color = MaterialTheme.colors.primaryVariant)
.padding(12.dp),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(id = R.drawable.error_circle),
contentDescription = title
)
Text(
text = title,
fontFamily = FontFamily(Font(R.font.inter_medium)),
fontSize = 12.sp,
color = MaterialTheme.colors.primary,
modifier = Modifier.padding(horizontal = 10.dp)
)
}
}
}
and my screen composable is as follows
#Composable
fun Registration(navController: NavController, registrationViewModel: RegistrationViewModel) {
Scaffold() {
Box(){
MyToast(
title = "Invalid pin, please try again"
)
Column() {
//my other screen components
}
}
}
I will add the AnimatedVisibility modifier later to MyToast composable. First I need to overlap MyToast over all the other elements and somehow MyToast is just not visible
If you want the child of a Box to overlay/overlap its siblings behind, you should put it at the last part in the code
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier.background(Color.Red).size(150.dp)
)
// your Toast
Box(
modifier = Modifier.background(Color.Green).size(80.dp)
)
}
So if I put the green box before the bigger red box like this
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
// your Toast
Box(
modifier = Modifier.background(Color.Green).size(80.dp)
)
Box(
modifier = Modifier.background(Color.Red).size(150.dp)
)
}
the green box will hide behind the red one
You have to solutions, you can either put the Toast in the bottom of your code because order matters in compose:
#Composable
fun Registration(navController: NavController, registrationViewModel: RegistrationViewModel) {
Scaffold() {
Box() {
Column() {
//my other screen components
}
MyToast(
title = "Invalid pin, please try again"
)
}
}
}
Or you can keep it as it is, but add zIndex to the Toast:
#Composable
fun MyToast(title: String) {
Card(
modifier = Modifier
.absoluteOffset(x = 0.dp, y = 40.dp)
.zIndex(10f) // add z index here
.background(
color = MaterialTheme.colors.primaryVariant,
shape = RoundedCornerShape(10.dp)
), elevation = 20.dp
) {
Row(
modifier = Modifier
.background(color = MaterialTheme.colors.primaryVariant)
.padding(12.dp),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(id = R.drawable.error_circle),
contentDescription = title
)
Text(
text = title,
fontFamily = FontFamily(Font(R.font.inter_medium)),
fontSize = 12.sp,
color = MaterialTheme.colors.primary,
modifier = Modifier.padding(horizontal = 10.dp)
)
}
}
}
Note: elevation in Card composable is not the same as elevation in XML so it's not going to make the composable in the top, it will just add a shadow but if you want to give the composable a higher z order use Modifier.zIndex(10f)
Related
The dragging part is possible on whole rectangle (Yellow in video) and I want it to be only allowed on the Grey icon
I can drag any part of the yellow part, whether up or down, I want to allow that behavior of dragging only on the Grey part
Left Video is the same as right video, except I made the right's sheetBackgroundColor transparent
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun HomeScreen(modifier: Modifier = Modifier) {
BottomSheetScaffold(
topBar = { AppBar() },
sheetElevation = ZERO_DP,
sheetPeekHeight = BOTTOM_ICON_CONTAINER_SIZE,
sheetBackgroundColor = Color.Transparent,
sheetContent = {
BottomSheetContent(modifier)
}
) {
HomeContent()
}
#Composable
fun BottomSheetContent(
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.fillMaxWidth()
.fillMaxHeight(0.8f)
) {
Box(
modifier = modifier
.padding(end = SPACING_QUADRUPLE)
.align(Alignment.End)
.clip(
RoundedCornerShape(
topStart = TRIPLE_CORNER_DP,
topEnd = TRIPLE_CORNER_DP
)
)
.size(BOTTOM_ICON_CONTAINER_SIZE)
.background(MaterialTheme.colors.secondary)
,
contentAlignment = Alignment.BottomCenter
)
{
Icon(
modifier = modifier,
painter = painterResource(id = R.drawable.ic_qr_code),
contentDescription = stringResource(
id = R.string.bottom_sheet_puller
),
tint = Color.Unspecified
)
}
Text(
modifier = modifier
.fillMaxWidth()
.background(MaterialTheme.colors.surface)
.padding(
start = SPACING_DOUBLE,
end = SPACING_DOUBLE,
bottom = SPACING_NORMAL
),
text = "Scan Serial With QR",
style = MaterialTheme.typography.h3,
)
Box(
modifier = modifier
.fillMaxSize()
.background(color = Color.DarkGray)
)
}
}
Wrong Behavior:
Correct Intended Behavior:
I tried replacing a rectangle composables with simple box, but bottom sheet was still considering the full width of the composable
There must be a better solution to intercept a drag gesture and leave all of it within the green box only, but this might suffice.
I made some changes to your BottomSheetContent, intercepting a drag gesture from a Row weighted transparent component and leaving it empty, you can try this one, and the drag gesture is only accepted by the green box,
#Composable
fun BottomSheetContent(
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.fillMaxWidth()
.fillMaxHeight(0.8f)
) {
Row {
Box(
modifier = Modifier
.weight(1f)
.draggable(
orientation = Orientation.Vertical,
state = rememberDraggableState {
Toast.makeText(context, "Non Draggable Area", Toast.LENGTH_SHORT).show()
}
).fillMaxWidth().height(150.dp).background(Color.Transparent)) {
}
Box(
modifier = modifier
.padding(end = 8.dp)
.clip(
RoundedCornerShape(
topStart = 12.dp,
topEnd = 12.dp
)
)
.size(150.dp)
.background(MaterialTheme.colors.secondary),
contentAlignment = Alignment.BottomCenter
) {
Icon(
modifier = modifier,
imageVector = Icons.Default.Add,
contentDescription = "",
)
}
}
Text(
modifier = modifier
.fillMaxWidth()
.background(MaterialTheme.colors.surface)
.padding(
start = 8.dp,
end = 8.dp,
bottom = 4.dp
),
text = "Scan Serial With QR",
style = MaterialTheme.typography.h3,
)
Box(
modifier = modifier
.fillMaxSize()
.background(color = Color.DarkGray)
)
}
}
I can't show my pointer click here, but the toast shows when the left area outside of the green box is being dragged.
I am trying to have a splash screen in our application and I got stuck Abit with the jetpack Compose design. So I want to have a background gradient, I am now using the image which does not fit well in the phone and want to center my logo and text in the middle of the screen how can I achieve that. Mostly centering since I have tried the gradient part and I have no success. Here is my code
Here is my background component
#Composable
fun BackgroundComponents(
#DrawableRes backgroundDrawableRes: Int,
contentDescription: String?,
modifier: Modifier = Modifier,
painter: Painter,
alignment: Alignment = Alignment.Center,
) {
Box(
modifier = modifier
) {
Image(
painter = painterResource(id = backgroundDrawableRes),
contentDescription = contentDescription,
modifier = modifier.matchParentSize()
)
Box(
contentAlignment = Alignment.Center
) {
Image(
painter = painter,
contentDescription = contentDescription,
alignment = alignment
)
}
Text(
modifier =
modifier.padding(top = 36.dp),
text = "Hello and welcome to our app",
color = (colorResource(id = R.color.white)),
fontSize = 16.sp,
)
}
}
This is how I am calling it on the Screen
#Composable
fun Splash(modifier: Modifier = Modifier) {
Column(modifier = modifier.fillMaxSize()) {
BackgroundComponents(
backgroundDrawableRes = R.drawable.ic_launcher_foreground,
contentDescription = "",
modifier = modifier.fillMaxSize(),
painter = painterResource(id = coil.base.R.drawable.notification_bg)
)
}
}
I would like to push the icon and text in the middle and centered, also instead of using an image can I draw that gradient?
You can apply the gradient to the parent Box as background modifier and then just apply the expected alignment to the composable inside the Box.
Something like:
Box(
modifier = Modifier
.fillMaxSize()
.background(
Brush.horizontalGradient(
colors = listOf(
Color.Blue,
Teal200
)
)
),
contentAlignment = Alignment.Center
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Image(
painter = painterResource(id = R.drawable.xx),
contentDescription = "contentDescription",
)
Spacer(Modifier.height(36.dp))
Text(
text = "Hello and welcome to our app",
color = White,
fontSize = 16.sp,
)
}
}
I have comment box and need to put icon on specific position ( bottom right ). I need to make something like position absolute where my icon button need to be bottom right inside comment box. Here is image what I am trying to achieve. Any help or idea?
You can do it by using Modifier.offset{} and putting your Icon inside a Box with Modifier.align(Alignment.BottomEnd)
#Composable
private fun Test() {
Column(modifier = Modifier.padding(10.dp)) {
Box(
modifier = Modifier
.width(200.dp)
.background(Color.LightGray.copy(alpha = .5f), RoundedCornerShape(8.dp))
.padding(4.dp)
) {
Column(
modifier = Modifier.fillMaxWidth()
) {
Text("Title", fontSize = 20.sp)
Text("Comment")
}
val offsetInPx = with(LocalDensity.current) {
16.dp.roundToPx()
}
Icon(
imageVector = Icons.Default.Settings,
contentDescription = null,
modifier = Modifier
.offset {
IntOffset(-offsetInPx, offsetInPx)
}
.shadow(2.dp, RoundedCornerShape(40))
.background(Color.White)
.padding(horizontal = 10.dp, vertical = 4.dp)
.size(30.dp)
.align(Alignment.BottomEnd),
tint = Color.LightGray
)
}
Spacer(modifier = Modifier.height(4.dp))
Text("Reply", color = Color.Blue)
}
}
I want to align the button in the BottomEnd of the Box in Row (In the BottonEnd of the bellow Card). I have Card with Row who is devided in two parts - Card and Box, and I want the Box to fill max of the rest of the row. I cannot implement it how I would wanted. Bellow I attached the visualization of the current code.
.
#Composable
fun ProductItem(product: ProductModel, onItemClick: () -> Unit, onAddToCardButton: () -> Unit) {
Card(
modifier = Modifier
.fillMaxSize()
.padding(7.dp)
.clickable { onItemClick() },
shape = MaterialTheme.shapes.large,
elevation = 4.dp
) {
Row(
modifier = Modifier.fillMaxSize()
) {
Card(
modifier = Modifier
.weight(1f),
shape = MaterialTheme.shapes.small,
elevation = 2.dp
) {
Image(
painter = painterResource(id = R.drawable.ic_splash_screen),
contentDescription = "Image of ${product.name}",
)
}
Box(
modifier = Modifier
.weight(2f)
.fillMaxHeight()
.padding(6.dp)
.background(Color.Green)
) {
Column(modifier = Modifier.align(Alignment.TopStart)) {
Text(
text = "${product.number}. ${product.name}",
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.h4,
)
Text(
text = product.ingredients, fontStyle = FontStyle.Italic
)
}
Button(
modifier = Modifier.align(Alignment.BottomEnd),
onClick = {
onAddToCardButton()
},
shape = RoundedCornerShape(8.dp),
) {
if (product.type == "pizza") {
Text(text = "od ${String.format("%.2f", product.price[0])} zł")
} else {
Text(text = "${String.format("%.2f", product.price[0])} zł")
}
}
}
}
}
}
I expect you display this item in LazyColumn or inside a vertical scrollable.
Modifier.fillMaxHeight doesn't work in this case, because parent height constraint is equal to infinity.
To solve this you ofc can use a static value, but in this case intrinsic measurements can be used to wrap content size.
Add Modifier.height(IntrinsicSize.Max) to your Row.
The code looks good to me at first sight, even tried it and the button is on the bottom right corner. Where do you define the height of your ProductItem card?
Maybe you can try to define it in the code you provided by changing the
.fillMaxSize()
modifier to
.fillMaxWidth()
.requiredHeight(**.dp)
at your first Card composable.
So it would look something like this:
#Composable
fun ProductItem(product: ProductModel, onItemClick: () -> Unit, onAddToCardButton: () -> Unit) {
Card(
modifier = Modifier
.fillMaxWidth()
.requiredHeight(**.dp)
.padding(7.dp)
.clickable { onItemClick() },
shape = MaterialTheme.shapes.large,
elevation = 4.dp
) {
...
}
As Phil Dukhov answered Modifier.height(IntrinsicSize.Max) works for me.
Just set the modifier to the Root element in your Row. In your case it's Card (change .fillMaxWidth() to .height(IntrinsicSize.Max)):
Card(
modifier = Modifier
.height(IntrinsicSize.Max)
.padding(7.dp)
.clickable { onItemClick() },
shape = MaterialTheme.shapes.large,
elevation = 4.dp
)
There is no ripple effect when I click on MyBox() I've added MyTheme(){} to main #Composable screen but it doesn't work. Is something missing?
#Composable
private MyBox(onClickInvoked: () -> Unit) {
MyAppTheme(isSystemInDarkTheme()) {
Box(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.clip(RoundedCornerShape(10.dp))
.background(MaterialTheme.colors.onBackground)
.clickable(onClick = { onClickInvoked.invoke() })
.padding(horizontal = 10.dp, vertical = 15.dp)
) {
Text(
text = "My text",
modifier = Modifier
.align(Alignment.CenterStart)
.padding(end = 95.dp)
.wrapContentWidth()
.wrapContentHeight(),
color = MaterialTheme.colors.primary
)
Image(
painter = painterResource(R.drawable.icon),
modifier = Modifier
.align(Alignment.CenterEnd)
.padding(end = 20.dp)
.size(60.dp)
)
}
}
}
With compose 1.0.5, I see the default indication after set clickable is
LocalIndication.current. And LocalIndication.current is PlatformRipple. Therefore, after you set clickable to your Box, it will have ripple effect.
In your case, I think the ripple effect won't display because your Box background is too dark (normally MaterialTheme.colors.onBackground is black on Light theme)
I think you can change the ripple effect color to make it easy to see.
Surface(
onClick = { onClickInvoked.invoke() },
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(10.dp),
color = MaterialTheme.colors.onBackground, // normally it is black on Light theme
indication = rememberRipple(color = Color.White) // color for your ripple, you can use other suitable MaterialTheme.colors for your case to support Light/Dark mode
) {
Box(modifier = Modifier.padding(horizontal = 10.dp, vertical = 15.dp)) {
// your Box content
...
}
}
There is a Modifier.indication but after testing I see it not working with Modifier.clickable so I use Surface
I don't really know if there is a problem with your theme. I try to pass the material theme, and the ripples will display normally
MaterialTheme() {
Box(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.clip(RoundedCornerShape(10.dp))
.background(MaterialTheme.colors.secondary)
.clickable(onClick = { t = "My text" })
.padding(horizontal = 10.dp, vertical = 15.dp)
) {
Text(
text = t,
modifier = Modifier
.align(Alignment.CenterStart)
.padding(end = 95.dp)
.wrapContentWidth()
.wrapContentHeight(),
color = MaterialTheme.colors.primary
)
Image(
painter = painterResource(R.drawable.ic_launcher_foreground),
modifier = Modifier
.align(Alignment.CenterEnd)
.padding(end = 20.dp)
.size(60.dp),
contentDescription = ""
)
}
}