androidandroid-studioandroid-jetpack-composeandroid-jetpack-compose-listcompose-recomposition

Jetpack Compose Recomposition every state changes


Here is my problem;

How can I fix this ? I know it is not a big problem here but just imagine we have a scrollable Column here and we are trying to pass ScrollState.value to My Text component. Because of this situation, our list becomes so laggy.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
             Screen()
        }
    }
}

@Composable
fun Screen(){
    var counter by remember {
        mutableStateOf(0)
    }
    Log.i("RECOMPOSE","VALUE1")
    Column() {
        Text(text = "Just a text")
        Log.i("RECOMPOSE","VALUE2")
        Button(onClick = { counter = counter.plus(1) }) {
            Text(text = counter.toString())
            Log.i("RECOMPOSE","VALUE3")
        }
        MyText(counter)
    }
}
@Composable
fun MyText(counter:Int){
    Text(text = counter.toString())
}

EDIT There is main problem, with Scrollable Column;

@Composable
fun Screen(){
    val scrollState = rememberScrollState()
    Box() {
        Column(modifier = Modifier
            .verticalScroll(scrollState)
            .padding(top = 50.dp)) {
            //Some Static Column Elements with images etc.
        }
        MyText(scrollStateValue = scrollState.value) //Doing some UI staff in this component
    }
}
@Composable
fun MyText(scrollStateValue:Int){
    Text(text = scrollStateValue.toString())
}

Solution

  • This behaviour is totally expected.

    Compose is trying to reduce number of recompositions as much as possible. When you comment out MyText, the only view that depends on counter is Button content, so this is the only view that needs to be recomposed.

    By the same logic you shouldn't see VALUE1 logs more than once, but the difference here is that Column is inline function, so if it content needs to be recomposed - it gets recomposed with the containing view.

    Using this knowledge, you can easily prevent a view from being recomposed: you need to move part, which doesn't depends on the state, into a separate composable. The fact that it uses scrollState won't make it recompose, only reading state value will trigger recomposition.

    @Composable
    fun Screen(){
        val scrollState = rememberScrollState()
        Box() {
            YourColumn(scrollState)
            MyText(scrollStateValue = scrollState.value) //Doing some UI staff in this component
        }
    }
    
    @Composable
    fun YourColumn(scrollState: ScrollState){
        Column(modifier = Modifier
            .verticalScroll(scrollState)
            .padding(top = 50.dp)) {
            //Some Static Column Elements with images etc.
        }
    }