Reputation: 808
Ive been trying out Jetpack Compose and ran into something with the LazyColumn
list and remember()
.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApp{
MyScreen()
}
}
}
}
@Composable
fun MyApp(content: @Composable () -> Unit){
ComposeTestTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
content()
}
}
}
@Composable
fun MyScreen( names: List<String> = List(1000) {"Poofy #$it"}) {
NameList( names, Modifier.fillMaxHeight())
}
@Composable
fun NameList( names: List<String>, modifier: Modifier = Modifier ){
LazyColumn( modifier = modifier ){
items( items = names ) { name ->
val counter = remember{ mutableStateOf(0) }
Row(){
Text(text = "Hello $name")
Counter(
count = counter.value,
updateCount = { newCount -> counter.value = newCount } )
}
Divider(color = Color.Black)
}
}
}
@Composable
fun Counter(count: Int, updateCount: (Int) -> Unit) {
Button( onClick = {updateCount(count+1)} ){
Text("Clicked $count times")
}
}
This runs and creates a list of 1000 rows where each row says "Hello Poofy #N" followed by a button that says "Clicked N times".
It all works fine but if I click a button to update its count that count will not persist when it is scrolled offscreen and back on.
The LazyColumn
"recycling" recomposes the row and the count. In the above sample the counter is hoisted up into NameList()
but I have tried it unhoisted in Counter()
. Neither works.
What is the proper way to remember the count? Must I store it in an array in the activity or something?
Upvotes: 6
Views: 10771
Reputation: 87794
The representations for items
are recycled, and with the new index
the value of remember
is reset. This is expected behavior, and you should not expect this value to persist.
You don't need to keep it in the activity, you just need to move it out of the LazyColumn
. For example, you can store it in a mutable state list, as shown here:
val counters = remember { names.map { 0 }.toMutableStateList() }
LazyColumn( modifier = modifier ){
itemsIndexed(items = names) { i, name ->
Row(){
Text(text = "Hello $name")
Counter(
count = counters[i],
updateCount = { newCount -> counters[i] = newCount } )
}
Divider(color = Color.Black)
}
}
Or in a mutable state map:
val counters = remember { mutableStateMapOf<Int, Int>() }
LazyColumn( modifier = modifier ){
itemsIndexed(items = names) { i, name ->
Row(){
Text(text = "Hello $name")
Counter(
count = counters[i] ?: 0,
updateCount = { newCount -> counters[i] = newCount } )
}
Divider(color = Color.Black)
}
}
Note that remember
will also be reset when screen rotates, consider using rememberSaveable
instead of storing the data inside a view model.
Read more about state in Compose in documentation
Upvotes: 5