4

I created an event listener to catch when a physical button is pressed, and it works well. But I would want to update a list used in a LazyColumn

class MainActivity : ComponentActivity() {
   @OptIn(ExperimentalComposeUiApi::class)
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContent {
           Theme {
               Surface(
                   modifier = Modifier                   .fillMaxSize(),
                   color = MaterialTheme.colors.background
               ) {
                   Column(modifier = Modifier.fillMaxSize()) {
                       Greeting("Android")
                   }
               }
           }
       }
   }

   @SuppressLint("RestrictedApi")
   override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
      // Handling event to get a text (type String)
      // ......

      //then updating my list
      myList+=newValue
   }


var myList: List<String> = mutableListOf()

@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
@Composable
fun Greeting(name: String, paramBarcode: String) {
   var mutableList by remember {mutableStateOf(myList)}
   
   Button(onClick = {
      myList+= "new item"
      mutableList = myList
   }) {
      Text(text = "Add")
   }

   LazyColumn(Modifier.fillMaxSize()            .padding(16.dp)
   ) {
       stickyHeader {Row(Modifier.fillMaxSize()                  .background(Color.Green)
           ) {
               TableCell(text = "Code", width = 264)
           }
       }
       itemsIndexed(items = mutableList, itemContent = {
          index, item ->
           Row(Modifier.fillMaxSize(),
           ) {
               TableCell(text = item, width = 256)
           }
       })
   }
}

If I try to add or remove an element of the list from my composable, everything is fine, but I can't get the same behaviour from my event.

I also tried to pass the list as a parameter to my composable, but it didn't help at all.

z.g.y
  • 5,512
  • 4
  • 10
  • 36
Joe Mama Jr.
  • 117
  • 1
  • 9

2 Answers2

1

Try using a SnapshotStateList instead of an ordinary list as mutable state.

So instead of this

var myList: List<String> = mutableListOf()

try this,

var myList = mutableStateListOf("Item1")

and instead of using your ordinary list setting it with a new one every time you add an item, you can simply add new elements to a SnapshotStateList.

I modified your code and any changes coming from outside Greeting and inside of it reflects to LazyColumn

@Composable
fun Greeting(
     list: SnapshotStateList<String>
) {
    Button(onClick = {
        list.add("Item inside greeting")
    }) {
        Text(text = "Add")
    }

    LazyColumn(
        Modifier
            .fillMaxSize()
            .padding(16.dp)
    ) { ... }
}

Usage

setContent {
     Column {
          Button(onClick = {
                  myList.add("New Item Outside greeting")
          }) {}

          Greeting(myList)
     }
}
z.g.y
  • 5,512
  • 4
  • 10
  • 36
  • 1
    thanks a lot, I still had to add this line above onCreate : var myList: SnapshotStateList = SnapshotStateList() – Joe Mama Jr. Nov 10 '22 at 11:32
  • Actually, it didn't work as expected because I lost the remember property : re-composition deletes the current list if I rotate the screen. – Joe Mama Jr. Nov 10 '22 at 14:25
  • If I understand your question correctly, it was about updating the `LazyColumn` outside of its composable scope not about surviving configuration changes, I tested your code and made a suggested fix. So If that's you're issue now then I suggest looking for a reference about `rememberSaveable` – z.g.y Nov 10 '22 at 14:27
  • Interesting, I'll try this maybe. I found something that works on recompose, I will just add it as a reply – Joe Mama Jr. Nov 10 '22 at 14:53
0

I've got a very good answer from @x.y

but then it lost value at recomposition. Here is the updated code to handle my first problem while keeping the state of the list :


var MainList: SnapshotStateList<String> = SnapshotStateList()

class MainActivity : ComponentActivity() {
    var myList: SnapshotStateList<String> = MainList

    @OptIn(ExperimentalComposeUiApi::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Theme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    Column(modifier = Modifier.fillMaxSize()) {
                        Greeting("Android", myList)
                    }
                }
            }
        }
    }
}

    @SuppressLint("RestrictedApi")
    override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
       MainList.add("$barcode")
    }
}

@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
@Composable
fun Greeting(name: String, paramBarcode: String, theList: SnapshotStateList<ScanItem>) {
    var mutableList by remember { mutableStateOf(theList) }

    Column() {
        Button(onClick = {
            MainList.add("ABC0000000000001")
        }) {
            Text(text = "Add item")
        }
        Row() {
            Button(onClick = { 
            MainList.add("add00001")
            }) { 
                Text(text = "Add")
            }
            Button(
                onClick = { MainList.clear() }
            ) {
                Text("Empty list")
            }
        }
    }
}

And I can pass mutableList as the parameter in itemsIndexed, and it will keep the state on recompose.

Joe Mama Jr.
  • 117
  • 1
  • 9