Reputation: 7151
I am working on nested column in jetpack compose. I have one list which is huge amount of data coming from server. I was checked in Layout Inspector and I see that whenever my item is added in list it recompose and increase counts. So my doubt is if I add 100 item in list one by one, so my Nested Column
will be 100 times recompose
? If not can someone help me on this please?
ListViewComposableActivity.kt
class ListViewComposableActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppBarScaffold(
displayHomeAsUpEnabled = true,
titleId = R.string.activity
) {
ListViewItemStateful()
}
}
}
}
ListViewItemStateful
@Composable
fun ListViewItemStateful(
viewModel: ListViewModel = koinViewModel(),
) {
ItemViewListStateless(
uiState = viewModel.uiState,
isEnable = viewModel.isEnable,
scanDeviceList = viewModel.scanResultList,
)
}
ItemViewListStateless
@Composable
fun ItemViewListStateless(
uiState: State,
isEnable: Boolean,
scanDeviceList: SnapshotStateList<ScanResults>,
) {
when (uiState) {
INITIAL,
FIRST -> {
ListContent(isEnable, scanDeviceList)
}
}
}
ListContent
@Composable
fun ListContent(isEnable: Boolean, scanDeviceList: SnapshotStateList<ScanResults>) {
AnimatedVisibility(true) {
Column(
modifier = Modifier
.padding(16.dp)
.fillMaxSize()
.verticalScroll(rememberScrollState()),
) {
if (isEnable) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
DeviceList(
scanDeviceList,
modifier = Modifier.align(Alignment.Start),
)
}
}
}
}
}
DeviceList
@Composable
fun ColumnScope.DeviceList(
scanDeviceList: SnapshotStateList<ScanResults>,
modifier: Modifier = Modifier,
) {
Spacer(modifier = Modifier.height(32.dp))
AnimatedVisibility(
scanDeviceList.isNotEmpty(),
modifier = modifier
) {
Column {
Text(text = "Device List")
scanDeviceList.forEachIndexed { index, scanResults ->
Text(text = scanResults.device.name)
}
}
}
}
ListViewModel.kt
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.viewModelScope
import com.abc.app.common.BaseViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class ListViewModel : BaseViewModel() {
val scanResultList by lazy { mutableStateListOf<ScanResults>() }
var isEnable by mutableStateOf(false)
private set
var uiState by mutableStateOf<State>(State.INITIAL)
private set
init {
viewModelScope.launch {
(0..10).forEach {
delay(2000)
scanResultList.add(ScanResults(Device("item $it")))
}
}
isEnable = true
uiState = State.FIRST
}
}
data class ScanResults(val device: Device)
data class Device(val name: String)
enum class State {
INITIAL,
FIRST
}
I am adding few items in list to show in layout inspector
In above image you can see the DeviceList
is recompose 10 times.
I checked in Jetpack Compose: Debugging recomposition around 6:40 min he tried to solve recompose issue and there skipped recomposition count is clear. So why it's showing count in my component tree in recomposition and skipped section? Many thanks
UPDATE
When I was changed to @Thracian answer it still recomposition skip
@Composable
fun ColumnScope.DeviceList(
scanDeviceList:()-> SnapshotStateList<ScanResults>,
modifier: Modifier = Modifier,
) {
Spacer(modifier = Modifier.height(32.dp))
AnimatedVisibility(
scanDeviceList().isNotEmpty(),
modifier = modifier
) {
Column {
Text(text = "Device List")
scanDeviceList().forEachIndexed { index, scanResults ->
Item(scanResults.device)
}
}
}
}
@Composable
private fun Item(device: Device) {
Text(
modifier = Modifier.border(2.dp, getRandomColor()),
text = device.name
)
}
fun getRandomColor() = Color(
red = Random.nextInt(256),
green = Random.nextInt(256),
blue = Random.nextInt(256),
alpha = 255
)
Upvotes: 1
Views: 1982
Reputation: 66674
In your question when you add a new item to SnapshotStateList whole Column is composed because Column doesn't create a composition scope due to inline keyword. If you create a scope that scope is recomposed when the value it reads changes. You can refer this question and answer as well.
Jetpack Compose Smart Recomposition
Add an item where Text reads device
@Composable
private fun Item(device: Device) {
Text(
modifier = Modifier.border(2.dp, getRandomColor()),
text = device.name
)
}
Random color is something i use for displaying recomposition visually
fun getRandomColor() = Color(
red = Random.nextInt(256),
green = Random.nextInt(256),
blue = Random.nextInt(256),
alpha = 255
)
Your current setup
With Item Composable that creates scope.
@Composable
fun ColumnScope.DeviceList(
scanDeviceList: SnapshotStateList<ScanResults>,
modifier: Modifier = Modifier,
) {
Spacer(modifier = Modifier.height(32.dp))
AnimatedVisibility(
scanDeviceList.isNotEmpty(),
modifier = modifier
) {
Column {
Text(text = "Device List", color = getRandomColor())
scanDeviceList.forEachIndexed { index, scanResults ->
// Text(
// modifier = Modifier.border(2.dp, getRandomColor()),
// text = scanResults.device.name
// )
Item(scanResults.device)
}
}
}
}
And when you have many items, especially they don't fit viewport you can use LazyColumn instead of Column with verticalScroll to limit recomposition amount to number of items that are visible on viewport or visible area of LazyColumn
Upvotes: 2