Jetpack Compose - Composable's height resizing with AnimatedVisibility - android

I'm trying to build a custom Drop down menu and I encountered some issues in animating its state. The animation is both laggy and sketchy, even on a real device and even on a release build (APK). The Compose version I'm using is 1.1.1.
Observe the flicker (and the lag?).
The code:
Column(modifier = Modifier.fillMaxWidth()) {
var visible by remember { mutableStateOf(false) }
//header
Column {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { visible = !visible }
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = "Click me",
style = MaterialTheme.typography.h6
)
Icon(
modifier = Modifier.rotate(animateFloatAsState(if (visible) 180f else 0f).value),
imageVector = Icons.Default.KeyboardArrowDown,
contentDescription = null
)
}
Divider(
modifier = Modifier.fillMaxWidth(),
color = Color.Black.copy(ContentAlpha.disabled)
)
}
//the 4 items
Column {
(1..4).forEach {
AnimatedVisibility(
visible = visible,
enter = expandVertically(
spring(
stiffness = Spring.StiffnessLow,
visibilityThreshold = IntSize.VisibilityThreshold
),
),
exit = shrinkVertically(),
) {
Column {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = "Hello",
style = MaterialTheme.typography.h6
)
Icon(
imageVector = Icons.Default.KeyboardArrowRight,
contentDescription = null
)
}
Divider(
modifier = Modifier.fillMaxWidth(),
color = Color.Black
)
}
}
}
}
}
If I add some bottom padding to the bigger Column or if I make it occupy the whole screen's height, there's no more flicker, but I feel like that is a workaround and also I'm not sure whether the animation is lagging or not, so this wouldn't be a solution to all my problems. The parent Column wraps around its content and as the content size increases, it tries to "keep up" with the new size, but it doesn't do a perfect job. Am I using AnimatedVisibility improperly? How else could I create a custom Drop down menu?

Related

Compose elements not overlapping

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)

Adding image to scaffold top bar in jetpack compose

I'm trying to add an image from a database to my top bar but it shows up as a white filled in rectangle - the image doesn't show at all. here's the below code. the way I have the icon set up works elsewhere in my app, it's just in this top bar for the scaffold it doesn't work. any insights?
TopBar.kt
TopAppBar (
title = {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "Test
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End
) {
Icon(
painter = rememberAsyncImagePainter(test.firstImage),
contentDescription = "Image")
}
}
}
)
Class.kt
Scaffold(topBar = {TopBar()}) { innerPadding ->
Column(modifier = Modifier
The Icon applies a default tint.
Use tint= Color.Unspecified to avoid it:
Icon(
painter = rememberAsyncImagePainter(test.firstImage),
contentDescription = "Image",
tint = Color.Unspecified
)

Koltin Jetpack compose, ModalBottomSheetLayout stop in the middle of the screen

I'am new to jetpack compose and i really liked it. But ran into a problem : I to have a modal that take all my screen size, following multiples tutorial i was able to do it using ModalBottomSheetLayout, Here is my code :
#ExperimentalMaterialApi
#Composable
fun DetailsScreen() {
val coroutineScope = rememberCoroutineScope()
val sheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)
ModalBottomSheetLayout(
sheetContent = {
Box(
modifier = Modifier
.navigationBarsWithImePadding()
.fillMaxSize()
.wrapContentHeight() //changed here to have full screen size, but it was only half screen, i had to slide with my finger to make the modal to full screen
//.height(200.dp) initial value i have a little modal on the bottom
//.fillMaxWidth()
.background(Color.White)
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(10.dp)
) {
Text(
text = "Bottom Sheet",
textAlign = TextAlign.Center,
style = MaterialTheme.typography.h5,
fontWeight = FontWeight.Bold,
modifier = Modifier.fillMaxWidth()
)
Text(
text = "Item 1",
style = MaterialTheme.typography.h6,
modifier = Modifier.padding(top = 10.dp)
)
Text(
text = "Item 2",
style = MaterialTheme.typography.h6,
modifier = Modifier.padding(top = 10.dp)
)
Text(
text = "Item 3",
style = MaterialTheme.typography.h6,
modifier = Modifier.padding(top = 10.dp)
)
}
}
},
sheetState = sheetState,
sheetBackgroundColor = Color.White
) {
Scaffold(
topBar = {
TopAppBar(
title = {
Text(
text = "Modal Bottom Sheet",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
)
}
) {
MainContent(
onClick = {
coroutineScope.launch {
//sheetState.show() // changed here make the modal full screen at the beginning
sheetState.animateTo(ModalBottomSheetValue.Expanded)
}
},
modifier = Modifier.padding(it)
)
}
}
}
#Composable
fun MainContent(
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Box(
contentAlignment = Alignment.Center,
modifier = modifier.fillMaxSize()
) {
Button(
onClick = onClick,
shape = RoundedCornerShape(10.dp)
) {
Text(
text = "Click Me!",
textAlign = TextAlign.Center,
style = MaterialTheme.typography.h6.copy(color = Color.White)
)
}
}
}
Problem i encounter : at first the modal was just at the bottom so i made my box to fillMaxSize to make it full screen, but it only open to half screen and i slide to swipe up to make it full screen.
Then i found that i could change sheetState.show() to sheetState.animateTo(ModalBottomSheetValue.Expanded) to make my modal full screen from the moment it display. Perfect it's what i wanted. here is a screen of the modal :
The problem is that now when i slide down to dismiss the modal it's stop half scree as bellow:
From there i have to swipe down an other time to make it disappear. Also when it's on the middle i can swipe up to make it back to full screen. It's seem that there is 3 states: fullscreen, halfscreen, and gone. And the modal can be full screen or halfscreen. I would like to remove the half screen. So when i open the modal it's fullscreen and when i swipe down to remove it, it disappear without stoping in the middle. So i would have only one gesture to remove it.
I think you can get this behavior with skipHalfExpanded property:
val bottomSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Expanded, skipHalfExpanded = true)

Jetpack Compose Smart Recomposition

I'm doing experiments to comprehend recomposition and smart recomposition and made a sample
Sorry for the colors, they are generated with Random.nextIn() to observe recomposition visually, setting colors has no effect on recomposition, tried without changing colors either.
What's in gif is composed of three parts
Sample1
#Composable
private fun Sample1() {
Column(
modifier = Modifier
.background(getRandomColor())
.fillMaxWidth()
.padding(4.dp)
) {
var counter by remember { mutableStateOf(0) }
Text("Sample1", color = getRandomColor())
Button(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = {
counter++
}) {
Text("Counter: $counter", color = getRandomColor())
}
}
}
I have no questions here since smart composition works as expected, Text on top is not reading changes in counter so recomposition only occurs for Text inside Button.
Sample2
#Composable
private fun Sample2() {
Column(
modifier = Modifier.background(getRandomColor())
) {
var update1 by remember { mutableStateOf(0) }
var update2 by remember { mutableStateOf(0) }
println("ROOT")
Text("Sample2", color = getRandomColor())
Button(
modifier = Modifier
.padding(start = 8.dp, end = 8.dp, top = 4.dp)
.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = {
update1++
},
shape = RoundedCornerShape(5.dp)
) {
println("๐Ÿ”ฅ Button1๏ธ")
Text(
text = "Update1: $update1",
textAlign = TextAlign.Center,
color = getRandomColor()
)
}
Button(
modifier = Modifier
.padding(start = 8.dp, end = 8.dp, top = 2.dp)
.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = { update2++ },
shape = RoundedCornerShape(5.dp)
) {
println("๐Ÿ Button 2๏ธ")
Text(
text = "Update2: $update2",
textAlign = TextAlign.Center,
color = getRandomColor()
)
}
Column(
modifier = Modifier.background(getRandomColor())
) {
println("๐Ÿš€ Inner Column")
var update3 by remember { mutableStateOf(0) }
Button(
modifier = Modifier
.padding(start = 8.dp, end = 8.dp, top = 2.dp)
.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = { update3++ },
shape = RoundedCornerShape(5.dp)
) {
println("โœ… Button 3๏ธ")
Text(
text = "Update2: $update2, Update3: $update3",
textAlign = TextAlign.Center,
color = getRandomColor()
)
}
}
Column() {
println("โ˜•๏ธ Bottom Column")
Text(
text = "Sample2",
textAlign = TextAlign.Center,
color = getRandomColor()
)
}
}
}
It also works as expected each mutableState is updating only the scope they have been observed in. Only Text that observes update2 and update3 is changed when either of these mutableStates are updated.
Sample3
#Composable
private fun Sample3() {
Column(
modifier = Modifier.background(getRandomColor())
) {
var update1 by remember { mutableStateOf(0) }
var update2 by remember { mutableStateOf(0) }
println("ROOT")
Text("Sample3", color = getRandomColor())
Button(
modifier = Modifier
.padding(start = 8.dp, end = 8.dp, top = 4.dp)
.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = {
update1++
},
shape = RoundedCornerShape(5.dp)
) {
println("๐Ÿ”ฅ Button1๏ธ")
Text(
text = "Update1: $update1",
textAlign = TextAlign.Center,
color = getRandomColor()
)
}
Button(
modifier = Modifier
.padding(start = 8.dp, end = 8.dp, top = 2.dp)
.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = { update2++ },
shape = RoundedCornerShape(5.dp)
) {
println("๐Ÿ Button 2๏ธ")
Text(
text = "Update2: $update2",
textAlign = TextAlign.Center,
color = getRandomColor()
)
}
Column {
println("๐Ÿš€ Inner Column")
var update3 by remember { mutableStateOf(0) }
Button(
modifier = Modifier
.padding(start = 8.dp, end = 8.dp, top = 2.dp)
.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = { update3++ },
shape = RoundedCornerShape(5.dp)
) {
println("โœ… Button 3๏ธ")
Text(
text = "Update2: $update2, Update3: $update3",
textAlign = TextAlign.Center,
color = getRandomColor()
)
}
}
// ๐Ÿ”ฅ๐Ÿ”ฅ Reading update1 causes entire composable to recompose
Column(
modifier = Modifier.background(getRandomColor())
) {
println("โ˜•๏ธ Bottom Column")
Text(
text = "Update1: $update1",
textAlign = TextAlign.Center,
color = getRandomColor()
)
}
}
}
Only difference between Sample2 and Sample3 is Text at the bottom is reading update1 mutableState which causing entire composable to be recomposed. As you can see in gif changing update1 recomposes or changes entire color schema for Sample3.
What's the reason for recomposing entire composable?
Column(
modifier = Modifier.background(getRandomColor())
) {
println("โ˜•๏ธ Bottom Column")
Text(
text = "Update1: $update1",
textAlign = TextAlign.Center,
color = getRandomColor()
)
}
}
To have smart recomposition scopes play a pivotal role. You can check Vinay Gaba's What is โ€œdonut-hole skippingโ€ in Jetpack Compose? article.
Leland Richardson explains in this tweet as
The part that is "donut hole skipping" is the fact that a new lambda
being passed into a composable (ie Button) can recompose without
recompiling the rest of it. The fact that the lambda are recompose
scopes are necessary for you to be able to do this, but not
sufficient
In other words, composable lambda are "special" :)
We wanted to do this for a long time but thought it was too
complicated until #chuckjaz had the brilliant realization that if the
lambdas were state objects, and invokes were reads, then this is
exactly the result
You can also check other answers about smart recomposition here, and here.
https://dev.to/zachklipp/scoped-recomposition-jetpack-compose-what-happens-when-state-changes-l78
When a State is read it triggers recomposition in nearest scope. And a scope is a function that is not marked with inline and returns Unit. Column, Row and Box are inline functions and because of that they don't create scopes.
Created RandomColorColumn that take other Composables and its scope content: #Composable () -> Unit
#Composable
fun RandomColorColumn(content: #Composable () -> Unit) {
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
.background(getRandomColor())
.padding(4.dp)
) {
content()
}
}
And replaced
Column(
modifier = Modifier.background(getRandomColor())
) {
println("โ˜•๏ธ Bottom Column")
Text(
text = "Update1: $update1",
textAlign = TextAlign.Center,
color = getRandomColor()
)
}
}
with
RandomColorColumn() {
println("โ˜•๏ธ Bottom Column")
/*
๐Ÿ”ฅ๐Ÿ”ฅ Observing update(mutableState) does NOT causes entire composable to recompose
*/
Text(
text = "๐Ÿ”ฅ Update1: $update1",
textAlign = TextAlign.Center,
color = getRandomColor()
)
}
}
Only this scope gets updated as expected and we have smart recomposition.
What causes Text, or any Composable, inside Column to not have a scope, thus being recomposed when a mutableState value changes is Column having inline keyword in function signature.
#Composable
inline fun Column(
modifier: Modifier = Modifier,
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
content: #Composable ColumnScope.() -> Unit
) {
val measurePolicy = columnMeasurePolicy(verticalArrangement, horizontalAlignment)
Layout(
content = { ColumnScopeInstance.content() },
measurePolicy = measurePolicy,
modifier = modifier
)
}
If you add inline to RandomColorColumn function signature you will see that it causes whole Composable to recompose.
Compose uses call sites defined as
The call site is the source code location in which a composable is
called. This influences its place in Composition, and therefore, the
UI tree.
If during a recomposition a composable calls different composables
than it did during the previous composition, Compose will identify
which composables were called or not called and for the composables
that were called in both compositions, Compose will avoid recomposing
them if their inputs haven't changed.
Consider the following example:
#Composable
fun LoginScreen(showError: Boolean) {
if (showError) {
LoginError()
}
LoginInput() // This call site affects where LoginInput is placed in Composition
}
#Composable
fun LoginInput() { /* ... */ }
Call site of a Composable function affects smart recomposition, and having inline keyword in a Composable sets its child Composables call site same level, not one level below.
For anyone interested here is the github repo to play/test recomposition

Jetpack compoes lazycolumn skipping frames (lagging)

Hi i'm trying to implement a lazycolumn of a list of posts, I tested it on the emulator api 21 and 29 and it looks kinda smooth on the api 29 it's a little bit laggy, when I tested it on a physical device it was lagging, It looks like it's skipping frames or something..
I tried to remove some views that uses imageVector to see if that was the problem and still the same problem.
This is my composable view:
#Composable
fun HomePostView(
category: String,
imagesUrl: List<String> = listOf(imageHolder),
doctorProfileImage: String = imageUrl,
title: String,
subTitle: String
) {
Card(
shape = PostCardShape.large, modifier = Modifier
.padding(horizontal = 3.dp)
.fillMaxWidth()
) {
Column {
PostTopView(
category = category,
onOptionsClicked = { /*TODO option click*/ },
onBookmarkClicked = {/*TODO bookmark click*/ })
CoilImage(
data = imagesUrl[0],
fadeIn = true,
contentDescription = "post_image",
modifier = Modifier
.fillMaxWidth()
.requiredHeight(190.dp)
.padding(horizontal = contentPadding),
contentScale = ContentScale.Crop
)
Spacer(modifier = Modifier.height(10.dp))
PostDoctorContent(
doctorProfileImage = doctorProfileImage,
title = title,
subTitle = subTitle
)
Spacer(modifier = Modifier.height(contentPadding))
PostBottomView(likesCount = 293, commentsCount = 22)
Spacer(modifier = Modifier.height(contentPadding))
}
}
Spacer(modifier = Modifier.height(10.dp))
}
#Composable
private fun PostDoctorContent(doctorProfileImage: String, title: String, subTitle: String) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = contentPadding)
) {
CoilImage(data = doctorProfileImage,
contentScale = ContentScale.Crop,
contentDescription = null,
fadeIn = true,
modifier = Modifier
.size(30.dp)
.clip(CircleShape)
.clickable {
/*Todo on doctor profile clicked*/
})
Column {
Text(
text = title, fontSize = 14.sp, maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(horizontal = contentPadding)
)
Text(
text = subTitle,
fontSize = 11.sp,
color = LightTextColor,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(horizontal = contentPadding)
)
}
}
}
#Composable
private fun PostBottomView(likesCount: Long, commentsCount: Long) {
Row(
modifier = Modifier.padding(horizontal = contentPadding),
verticalAlignment = Alignment.CenterVertically
) {
Row(
Modifier
.clip(RoundedCornerShape(50))
.clickable { /*Todo on like clicked*/ }
.padding(5.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_heart),
contentDescription = "Like"
)
Spacer(modifier = Modifier.width(5.dp))
Text(text = likesCount.toString(), fontSize = 9.sp)
}
Spacer(Modifier.width(20.dp))
Row(
Modifier
.clip(RoundedCornerShape(50))
.clickable { /*Todo on comment clicked*/ }
.padding(5.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_comment),
contentDescription = "Comment"
)
Spacer(modifier = Modifier.width(5.dp))
Text(text = commentsCount.toString(), fontSize = 9.sp)
}
}
}
#Composable
private fun PostTopView(
category: String,
onOptionsClicked: () -> Unit,
onBookmarkClicked: () -> Unit
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = onOptionsClicked) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_threedots),
contentDescription = "Options",
tint = Color.Unspecified
)
}
Text(text = category, fontSize = 16.sp, color = LightTextColor)
}
IconButton(onClick = onBookmarkClicked) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_bookmark),
contentDescription = "Bookmark"
)
}
}
}
and the lazyColumn:
LazyColumn(contentPadding = paddingValues , state = state ) {
item {
Spacer(modifier = Modifier.height(10.dp))
DoctorsList(
viewModel.doctorListData.value,
onCardClicked = {})
}
items(30) { post ->
HomePostView(
category = "Public Health ",
title = "Food Importance",
subTitle = "you should eat every day it's healthy and important for you, and drink water every 2 hours and what you should do is you should run every day for an hour"
)
}
}
Note: I'm still not using a viewmodel i'm just testing the view with fake data
This may not work for anyone else but on an earlier version (1.0.0-beta01) I saw a very large improvement in performance when I switched
lazy(items) { item ->
...
}
to
items.forEach { item ->
lazy {
...
}
}
I have no idea why and I'm not sure if this is still the case in later versions but its worth checking. So for the example given in the question, that would mean changing
items(30) {
...
}
to
repeat(30) {
item {
...
}
}
TLDR: Make sure your new LazyColumn compose element is not within a RelativeLayout or LinearLayout.
After some investigation the solution to this issue for us was the view in which the LazyColumn was constrained.
Our project uses a combination of Jetpack Compose and the older XML layout files. Our new compose elements were embedded within a RelativeLayout of an existing XML file. This was where the problem was. The compose element would be given the entire screen and then the onMeasure function of the compose element was called to re-configure the view and add our bottom nav bar...this onMeasure was called over and over again, which also in the case of a LazyColumn the re-measuring was throwing out the cache as the height had changed.
The solution for us was to change the RelativeLayout that contained both the new compose element and the bottom nav bar and replace it with a ConstraintLayout. This prevented the onMeasure from being called more than twice and gave a massive performance increase.
Try to build release build with turn off debug logs. should be works fine.
Ok, So far I know that there is an issue with the API when it comes to performance ...But what I found is this
Actually, In my case, I was just loading the image with 2980*3750 pixels image. I just crunched my resources to shorter pixels by some other tools
Now the lag is not present...
In my case, after I set the height of ComposeView to a specific value, it make LazyColumn scroll smooth.
Therefore, I create a XML file like
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.compose.ui.platform.ComposeView
android:id="#+id/compose"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle ? ) {
super.onViewCreated(view, savedInstanceState)
view.doOnLayout {
compose.layoutParams.height = view.height
compose.requestLayout()
}
}
I know ComposeView height already match_parent so set the height for it again on Fragment seem useless. However, without setting the height, LazyColumn will lagging when scroll.
I am not sure if it will work in all device but work well on my Pixel and Xiomi.
If you are using JPGs or PNGs in your project then check for their size, images having larger size causes a lots of lagging issues on low end devices.
I was having the same issue with my simple LazyColumn list and turned out I was using a JPG having size larger than 2MBs.

Categories

Resources