In our application, there are many custom views that all are card with 3 slots header, content and bottom part, so I thought we can handle it with a scaffold inside the card except having many if/else conditions
So I created this base composable function ->
@Composable
fun DynamicTile(
modifier: Modifier = Modifier,
headContent: @Composable () -> Unit,
mainContent: @Composable (PaddingValues) -> Unit = { },
bottomContent: @Composable () -> Unit = { }
) {
Card( modifier = modifier) {
Scaffold(
topBar = headContent,
content = mainContent,
bottomBar = bottomContent
)
}
}
then different implementation for different purposes, I mention here two purposes e.g. showing specific image, animation, map and ... ->
for this one you should add your local drawable to image to compile it
@Composable
fun TileTeaser( modifier: Modifier = Modifier) {
with(entity) {
DynamicTile(
// modifier = modifier.then(Modifier.height(250.dp)),
headContent = {
Text(
text = "headline",
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, top = 8.dp)
)
},
mainContent = {
val painter = rememberImagePainter(
data = painterResource(R.drawable.ds_ic_add))
Image(
contentScale = ContentScale.Crop,
painter = painter,
modifier = modifier,
contentDescription = null
)
},
bottomContent = {
Box(modifier = Modifier.fillMaxWidth()) {
Button(
onClick = { }, modifier = Modifier
.align(Alignment.Center)
.wrapContentSize()
) {
Text(text = "Button")
}
}
}
}
)
}
}
And this one for animation with Lottie, you should add local raw to compile it
@Composable
fun TileAnimation(modifier: Modifier = Modifier) {
DynamicTile(
modifier = modifier.then(Modifier.height(300.dp)),
headContent = {
Text(
text = "headline",
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, top = 8.dp)
)
},
mainContent = {
val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.sth))
Card(
modifier = Modifier
.height(183.dp)
.then(modifier),
shape = RoundedCornerShape(0.dp)
) {
LottieAnimation(
composition = composition,
modifier = modifier
.fillMaxSize(),
contentScale = ContentScale.Crop,
)
}
},
bottomContent = {
Box(modifier = Modifier.fillMaxWidth()) {
Button(
onClick = { }, modifier = Modifier
.align(Alignment.Center)
.wrapContentSize()
) {
Text(text = "Button")
}
}
}
)
}
Then I load them in column like this
@Composable
fun LoadScreen() {
Column(
modifier = Modifier
.padding(16.dp)
.verticalScroll(rememberScrollState())
) {
Text(text = "Teaser")
TileTeaser(
modifier = Modifier.padding(top = 16.dp)
)
Text(
text = "Animation",
modifier = Modifier.padding(top = 16.dp)
)
TileAnimation(modifier = Modifier.padding(vertical = 16.dp))
}
}
As you see if I comment the Modifier.height of the card, it crashes with this error ->
java.lang.IllegalArgumentException: Can't represent a size of 2147483563 in Constraints
at androidx.compose.ui.unit.Constraints$Companion.bitsNeedForSize(Constraints.kt:408)
at androidx.compose.ui.unit.Constraints$Companion.createConstraints-Zbe2FdA$ui_unit_release(Constraints.kt:368)
Kotlin version 1.6.10 and compose 1.1.0 and this is lottie library ->
implementation "com.airbnb.android:lottie-compose:4.2.2"
BTW, you can download Lottie file from here
Thank you in advance for your help
TL;DR Don't put a
Scaffoldinside aCardinside a scrollable content. :)If we take a deeper look into
Scaffoldcode we will see that it's actually creating aScaffoldLayoutthat will create aSubcomposeLayoutthat uses constraints and more specifically the following width and height:Now, if no predefined values was set to the above
layoutWidthandlayoutHeightthey will be equal toInt.MAX_VALUEwhich is 2147483647 which is what you see in yourIllegalArgumentException(give or take).When you use Scaffold the right way, usually as the root of your UI, Android do the magic for you and calculate the correct size.
My suggestion, replace the Scaffold with another type of layout or if it's not enough a custom layout:
https://developer.android.com/jetpack/compose/layouts/custom