3

I'm new to Jetpack Compose and I'm trying to figure out how to recompose a LazyColumn list when user clicks a FloatingActionButton.

As the picture shows, I have a basic Scaffold layout with a LazyColumn for content. Toward the bottom is a FloatingActionButton. I'd like to be able to click that FloatingActionButton, add "Molly" to my list of names, have the app recompose my list, and display the full list including Molly. Code below picture.

enter image description here


Here's my code:

package com.learning.lazylistexample

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.learning.lazylistexample.ui.theme.LazyListExampleTheme
import kotlinx.coroutines.launch

data class ListItem(val name: String)

private var listItems: MutableList<ListItem> = mutableListOf(
    ListItem("Al"),
    ListItem("Barb"),
    ListItem("Cheryl"),
    ListItem("Dave"),
    ListItem("Ed"),
    ListItem("Frank"),
    ListItem("Gloria"),
    ListItem("Henry"),
    ListItem("Ingrid"),
    ListItem("Jack"),
    ListItem("Kayla"),
    ListItem("Larry")
)

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            LazyListExampleTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    myApp()
                }
            }
        }
    }
}

@Composable
fun myApp() {

    val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))

    val coroutineScope = rememberCoroutineScope()

    Scaffold(
        scaffoldState = scaffoldState,
        topBar = { TopBar() },
        bottomBar = { BottomBar() },
        content = { DisplayList(itemsList = listItems) },
        floatingActionButton = {
            FloatingActionButton(

                onClick = {
                    // When clicked open Snackbar
                    coroutineScope.launch {
                        when (scaffoldState.snackbarHostState.showSnackbar(
                            // Message In the snack bar
                            message = "Snack Bar",
                            actionLabel = "Dismiss"
                        )) {
                            SnackbarResult.Dismissed -> {
                                // do something when snack bar is dismissed
                            }

                            SnackbarResult.ActionPerformed -> {
                                // do something when snack bar is activated
                                // ****** UPDATE LIST *******
                                val newListItem = ListItem(name = "Molly")
                                listItems.add(newListItem)
                                // ****** HOW TO RECOMPOSE LAZYCOLUMN? ******
                            }
                        }
                    }
                }) {
                // Text inside FloatingActionButton
                Text(text = "Add Molly")
            }
        }

    )

}

@Composable
fun TopBar() {
    TopAppBar(
        title = { Text(text = "Lazy List Example", color = Color.White) }
    )
}

@Composable
fun BottomBar() {
    BottomAppBar() {
        Text(text = "", color = Color.White)
    }
}

@Composable
fun DisplayList(itemsList: List<ListItem>) {
    LazyColumn(modifier = Modifier.fillMaxHeight()) {
        items(items = itemsList, itemContent = { item ->
            Text(text = item.name)
        } )
    }
}

I know this has something to do with state, but I can't figure out where to begin. Can anyone help me with this?

THANK YOU!

SqueezeOJ
  • 441
  • 7
  • 17

1 Answers1

5

Change

private var listItems: MutableList<ListItem> = mutableListOf(
    ListItem("Al"),
    ListItem("Barb"),
    ListItem("Cheryl"),
    ListItem("Dave"),
    ListItem("Ed"),
    ListItem("Frank"),
    ListItem("Gloria"),
    ListItem("Henry"),
    ListItem("Ingrid"),
    ListItem("Jack"),
    ListItem("Kayla"),
    ListItem("Larry")
)

to

val listItems = remember { mutableStateListOf(
     ListItem("Al"),
        ListItem("Barb"),
        ListItem("Cheryl"),
        ListItem("Dave"),
        ListItem("Ed"),
        ListItem("Frank"),
        ListItem("Gloria"),
        ListItem("Henry"),
        ListItem("Ingrid"),
        ListItem("Jack"),
        ListItem("Kayla"),
        ListItem("Larry")
) }

And you will have a SnapshotStateList, an instance of MutableList that is observable and can be snapshot, that will trigger recomposition when you add or remove any items.

Thracian
  • 43,021
  • 16
  • 133
  • 222
  • I changed the listItems declaration and moved it inside fun myApp(). I'm not sure I understand the second part of your solution about the "SnapshotStateList, [as] an instance of MutableList". Can you give me a little more on that? – SqueezeOJ Mar 30 '22 at 23:04
  • 1
    These two articles helped me with the second part a lot: [LazyColumn and mutable list](https://stackoverflow.com/questions/68046535/lazycolumn-and-mutable-list-how-to-update) and [Jetpack Compose: Update composable when list changes](https://stackoverflow.com/questions/67252538/jetpack-compose-update-composable-when-list-changes) – SqueezeOJ Mar 31 '22 at 00:25
  • I uploaded my final solution to [GitHub](https://github.com/squeezeoj/Lazy_List_Recompose_Example) – SqueezeOJ Mar 31 '22 at 00:38
  • It means that your list is a class that implements `StateObject` which will trigger recomposition when you add items to or remove items from this list. – Thracian Mar 31 '22 at 06:32