Android compose: Surface can have only one direct measurable child - android

I'm new to Jetpack compose, I have create a composable like this.
Column(
Modifier.clickable(onClick = onclick)
.fillMaxWidth().background(pastelGray)
.padding(16.dp)
) {
Card(backgroundColor = Color.Cyan) {
// Image(asset = vectorResource(id = R.drawable.ic_coupon_back), modifier = Modifier.fillMaxWidth())
Column(modifier = Modifier.padding(8.dp)) {
Text(text = coupon.couponTitle, color = Color.Red, fontSize = 20.sp)
Text(text = coupon.couponSubTitle, color = Color.Black, fontSize = 13.sp)
Text(text = coupon.couponDateTitle, color = Color.Gray, fontSize = 11.sp)
}
}
}
When I add that commented image I got this error:
Surface can have only one direct measurable child!
I could not find the reason till now.

Because Card() composable used in your code uses Surface under the hood:
#Composable
fun Card(
modifier: Modifier = Modifier,
shape: Shape = MaterialTheme.shapes.medium,
backgroundColor: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(backgroundColor),
border: BorderStroke? = null,
elevation: Dp = 1.dp,
content: #Composable () -> Unit
) {
Surface(
modifier = modifier,
shape = shape,
color = backgroundColor,
contentColor = contentColor,
elevation = elevation,
border = border,
content = content
)
}
source: Official Card Implementation code
And Surface is a kind like ScrollView doesn't accept more than one direct child. So you need to wrap your code inside Card() into a single parent that is a direct child of Card or in other words direct child of Surface, example:
Card(backgroundColor = Color.Cyan) {
CardContent()
}
private fun CardContent() {
Column() {
// Image(asset = vectorResource(id = R.drawable.ic_coupon_back), modifier = Modifier.fillMaxWidth())
Column(modifier = Modifier.padding(8.dp)) {
Text(text = coupon.couponTitle, color = Color.Red, fontSize = 20.sp)
Text(text = coupon.couponSubTitle, color = Color.Black, fontSize = 13.sp)
Text(text = coupon.couponDateTitle, color = Color.Gray, fontSize = 11.sp)
}
}
}
}

Related

How to make a chip retain its shape after click-event in compose?

I am currently trying to make it so that when the user has clicked the chip it still retains it's initial form/shape, in this case round. How can I achieve this?
This is how it operates currently: https://gyazo.com/bdbe867adb5c9e75381f7ac923134709
The Chip code:
#Composable
fun TextChip(
isSelected: Boolean,
text: String,
onChecked: (Boolean) -> Unit,
selectedColor: Color = DarkGray,
shape: Shapes,
) {
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.wrapContentSize()
.border(
width = 1.dp,
color = if (isSelected) selectedColor else LightGray,
shape = RoundedCornerShape(20.dp)
)
.background(
color = if (isSelected) selectedColor else Transparent,
)
.clickable {
onChecked(!isSelected)
}
.padding(top = 3.dp, bottom = 3.dp, start = 17.dp, end = 17.dp)
) {
Text(
text = text,
fontSize = 21.sp,
fontWeight = FontWeight.Bold,
color = if (isSelected) Color.White else Unspecified, // Text inside, when clicked, gives color to the text!
)
}
}
#Composable
fun FirstChip() {
// Chip View
val textChipRememberOneState = remember { mutableStateOf(false) }
TextChip(
isSelected = textChipRememberOneState.value,
shape = Shapes(medium = RoundedCornerShape(15.dp)),
text = "Action",
selectedColor = LightGreen,
onChecked = {
textChipRememberOneState.value = it
},
)
}
You should set the shape for your Modifier.background(color, shape) but it won't clip your click ripple and it will be as in gif with a Rectangle shape. You can use Modifier.clip() to clip background and ripple as
modifier = Modifier
.wrapContentSize()
.border(
width = 1.dp,
color = if (isSelected) selectedColor else LightGray,
shape = RoundedCornerShape(20.dp)
)
.clip(RoundedCornerShape(20.dp))
.background(
color = if (isSelected) selectedColor else Transparent,
)
.clickable {
onChecked(!isSelected)
}
.padding(top = 3.dp, bottom = 3.dp, start = 17.dp, end = 17.dp)
You should use existing chip #Composable function. There are 4 chip types - Assist chip, filter chip, input chip, suggestion chip. FilterChip is best for you to use. You can simply call FilterChip function:
#Composable
fun FirstChip() {
// Chip View
val textChipRememberOneState = remember { mutableStateOf(false) }
FilterChip(
selected = textChipRememberOneState.value,
onClick = { textChipRememberOneState.value = ! textChipRememberOneState.value},
label = { Text("Action") }
)
}

Transparent background in Outlined Button in Jetpack Compose

I want to create button where I have only text and icon and all background and borders are transparent. I create something like that:
OutlinedButton(
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Transparent),
border = BorderStroke(0.dp, Color.Transparent),
modifier = modifier,
onClick = onClick
) {
icon?.invoke()
Text(
text = value,
fontSize = 12.sp
)
}
and everything is ok, but I lost default colors(should be blue, and I have black icon and text). How can I remove all background and borders from button but still have theme colors?
Could you try this?
#Composable
fun TiledButton(
onClick: () -> Unit,
#DrawableRes backgroundDrawableId: Int,
modifier: Modifier = Modifier,
enabled: Boolean = true,
shape: Shape = MaterialTheme.shapes.small,
border: BorderStroke? = null,
contentColor: Color = MaterialTheme.colors.primary,
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
content: #Composable RowScope.() -> Unit
) {
Button(
onClick = onClick,
contentPadding = PaddingValues(0.dp),
enabled = enabled,
shape = shape,
border = border,
elevation = null,
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Transparent,
contentColor = contentColor,
disabledBackgroundColor = Color.Transparent,
disabledContentColor = contentColor.copy(alpha = ContentAlpha.disabled),
),
modifier = modifier
) {
Box(
contentAlignment = Alignment.Center,
) {
TileAndroidImage(
drawableId = backgroundDrawableId,
contentDescription = "...",
modifier = Modifier.matchParentSize()
)
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(contentPadding),
content = content,
)
}
}
}
TiledButton(
onClick = { },
backgroundDrawableId = R.drawable.tile,
border = BorderStroke(1.dp, Color.Blue),
) {
Text("Button")
}
Just use a TextButton instead of a OutlinedButton:
TextButton(
onClick = { }
) {
Icon(Icons.Default.Add,"")
Text(
text = value,
fontSize = 12.sp
)
}
If you want to use a OutlinedButton just use ButtonDefaults.outlinedButtonColors instead of ButtonDefaults.buttonColors:
OutlinedButton(
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = Color.Transparent),
border = BorderStroke(0.dp, Color.Transparent),
modifier = modifier,
onClick = onClick
) {
Icon(Icons.Default.Add,"")
Text(
text = value,
fontSize = 12.sp
)
}

Compose rounded Button with transparent background

I am trying to display a Button with rounded corners and a 50% transparent background. My current attempt looks like this:
MaterialTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = Color.Yellow
) {
Column(modifier = Modifier.padding(10.dp)) {
Button(
modifier = Modifier
.clip(CircleShape),
onClick = { },
colors = ButtonDefaults.buttonColors(backgroundColor = Color.White.copy(alpha = 0.5f))
) {
Text(
text = "My Button",
textAlign = TextAlign.Center
)
}
}
}
}
The result is not very pretty:
It looks like the issue is with with the shading, but I'm not sure how to remove it and just show the same color within the whole shape.
Turns out the shadow will disappear when the elevation is removed.
Button(
modifier = Modifier
.clip(CircleShape),
onClick = { },
elevation = null,
colors = ButtonDefaults.buttonColors(backgroundColor = Color.White.copy(alpha = 0.5f))
) { ... }
Button is just a Surface wrapping the content that you provide. You could check the source. So, I just tweaked it a little
#Composable
fun HollowButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
elevation: ButtonElevation? = ButtonDefaults.elevation(),
shape: Shape = MaterialTheme.shapes.small,
border: BorderStroke? = null,
colors: ButtonColors = ButtonDefaults.buttonColors(),
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
content: #Composable RowScope.() -> Unit
) {
val contentColor by colors.contentColor(enabled)
Surface(
modifier = modifier,
shape = shape,
color = colors.backgroundColor(enabled).value.copy(0.5f), //Basically I refactored the alpha modification to here
contentColor = contentColor.copy(alpha = 1f),
border = border,
elevation = elevation?.elevation(enabled, interactionSource)?.value ?: 0.dp,
onClick = onClick,
enabled = enabled,
role = Role.Button,
interactionSource = interactionSource,
indication = rememberRipple()
) {
CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
ProvideTextStyle(
value = MaterialTheme.typography.button
) {
Row(
Modifier
.defaultMinSize(
minWidth = ButtonDefaults.MinWidth,
minHeight = ButtonDefaults.MinHeight
)
.padding(contentPadding),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
content = content
)
}
}
}
}
Works like a charm.

Gradient backround for FAB in Jetpack compose

I want to add a Floating Action Button with a gradient background in Jetpack Compose. I have the following snippet to do so:
FloatingActionButton(
onClick = {
coroutineScope.safeLaunch {
navController.navigate("AddTodoPage") {
launchSingleTop = true
}
}
},
shape = RoundedCornerShape(14.dp),
backgroundColor = Color.Transparent,
modifier = Modifier
.constrainAs(addFab) {
bottom.linkTo(parent.bottom)
end.linkTo(parent.end)
}
.offset(x = (-16).dp, y = (-24).dp)
.background(
brush = Brush.verticalGradient(
colors = BluePinkGradient()
),
shape = RoundedCornerShape(14.dp)
)
) {
Icon(
painter = painterResource(id = R.drawable.ic_add),
contentDescription = "Add icon",
tint = Color.White
)
}
fun BluePinkGradient(inverse: Boolean = false) = when (inverse) {
true -> listOf(
MutedBlue,
MutedPink
)
false -> listOf(
MutedPink,
MutedBlue
)
}
val MutedBlue = Color(0xFF26A69A)
val MutedPink = Color(0xFFEC407A)
But from the image below, the button has a "Whitish" shade on the plus icon. How can I remove that shade or a better way to set the FAB background to a gradient?
Fab Image
'"Whitish" shade on the plus icon' is the result of elevation parameter. You can zero it, but it doesn't looks like you need FAB in the first place.
As you need to custom the button that much, you can use IconButton instead:
IconButton(
onClick = {
},
modifier = Modifier
.background(
brush = Brush.verticalGradient(
colors = BluePinkGradient()
),
shape = RoundedCornerShape(14.dp)
)
) {
Icon(
painter = painterResource(id = R.drawable.ic_undo),
contentDescription = "Add icon",
tint = Color.White
)
}
FloatingActionButton is only applying some Material defaults to the content, it doesn't make it really floating, it has to be done with the container.
I have developed the following solution, which I have confirmed as working:
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun CrazyFloatingActionButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
gradient: List<Color>,
contentColor: Color = contentColorFor(gradient[0]),
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
content: #Composable () -> Unit
) {
Surface(
modifier = modifier,
shape = shape,
contentColor = contentColor,
elevation = elevation.elevation(interactionSource).value,
onClick = onClick,
role = Role.Button,
interactionSource = interactionSource,
indication = rememberRipple()
) {
CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
ProvideTextStyle(MaterialTheme.typography.button) {
Box(
modifier = Modifier
.defaultMinSize(minWidth = 56.dp, minHeight = 56.dp)
.background(brush = Brush.verticalGradient(gradient)),
contentAlignment = Alignment.Center
) { content() }
}
}
}
}
Just prepend Crazy to your Composable and you should be good to go.

Why jetpack compose elevation clip my shadow?

I have a problem with jetpack compose elevation render. I'm trying to add elevation on Surface but my UI seems to with clipped shadow. Also, how can I add a colorful shadow on my Surface?
See the below on the screenshot
#Composable
fun DiscoverItem() {
Surface(
contentColor = Color(0xFFFFFFFF),
modifier = Modifier.preferredWidthIn(min = 145.dp).preferredHeight(56.dp),
shape = CircleShape,
elevation = 8.dp,
) {
Row(
modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Surface(
modifier = Modifier.preferredSize(40.dp),
shape = CircleShape,
color = Color(0xFFFFC3D8)
) {
Image(
imageResource(R.drawable.pin_icon),
modifier = Modifier.size(width = 18.dp, height = 24.dp),
contentScale = ContentScale.Fit
)
}
Spacer(modifier = Modifier.padding(start = 10.dp))
Text(
"YOUR AREA",
style = MaterialTheme.typography.body2,
color = Color(0xFFFC1055)
)
}
}
}
#Composable
#Preview
fun DiscoverItemPreview() {
DiscoverItem()
}
You don't have enough content on bottom of your layout. You can add spacer to view your shadow.
#Composable
#Preview
fun DiscoverItemPreview() {
Column{
DiscoverItem()
Spacer(modifier = Modifier.height(20.dp))
}
}
And about colorful shadow, compose min sdk is Android Lollpop and skia version for lollipop doesnot supports colorful shadow/elevation. Leland Richardson had talked about this issue in his youtube video on Compose dogfooding. here
I use surface to wrap the shadowed card and it gives me expected result.
Surface(
elevation = 4.dp,
color = MaterialTheme.colors.surface,
shape = Shapes.medium,
) {
Card(
modifier = Modifier
.fillMaxWidth()
.animateContentSize(
animationSpec = tween(
durationMillis = 300,
easing = LinearOutSlowInEasing
)
),
elevation = 8.dp,
shape = Shapes.medium,
onClick = {
isExpanded = !isExpanded
}
) {}}
enter image description here

Categories

Resources