Search code examples
androidkotlinandroid-recyclerviewandroid-jetpack-composeadapter

How to communicate between adapter vs jetpack compose (lazy column)?


In classic Android programming (before composites) to show list on recyclerview I need adapter and if I want to give any action like clickable element, where logic will be provide in fragment/viewmodel I had to through argument create such parameter like

(Int) -> Unit

how does it look in jetpack compose when I create lazy column? Should it look the same and just create paramaters in Screens/UI then configure logic in the same way in fragment/viewmodel or there is some other approachs?


Solution

  • Getting rid of all the bloat of recyclerview is one of my favorite advantages of compose. Let's assume you'd like to include the following list in your screen:

    @Composable
    private fun TestList(
        myItems: List<String>,
        onClick: (String) -> Unit,
        modifier: Modifier = Modifier,
    ) {
        LazyColumn(modifier.fillMaxSize()) {
            items(myItems) { item ->
                TestItemView(
                    text = item,
                    onClick = onClick
                )
            }
        }
    }
    
    @Composable
    private fun TestItemView(
        text: String,
        onClick: (String) -> Unit,
        modifier: Modifier = Modifier,
    ) {
        Surface(modifier.fillMaxWidth()) {
            Button(
                onClick = { onClick(text) },
                content = { Text(text) }
            )
        }
    }
    

    Option A: keep the state in the composable.

    @Composable
    private fun TestScreenA() {
        val myItems = remember { mutableStateOf(listOf("A", "B", "C", "D")) }
    
        TestList(
            myItems = myItems.value,
            onClick = { clickedItem ->
                // for demonstration purposes we remove item on click
                myItems.value = myItems.value.filterNot { it == clickedItem }
            }
        )
    }
    

    Option B: Keep the state in the viewmodel (like before)

    class TestViewModelB: ViewModel() {
        private val _myItems = MutableStateFlow(listOf("A", "B", "C", "D"))
        val myItems = _myItems.asStateFlow()
    
        fun onItemClicked(clickedItem: String){
             // for demonstration purposes we remove item on click
            _myItems.update { items -> items.filterNot { it == clickedItem } }
        }
    }
    
    @Composable
    private fun TestScreenB(
        // inject viewModel here using your favorite DI-Framework
        viewModel: TestViewModelB 
    ) {
        TestList(
            // you might want to use collectAsStateWithLifecycle in the future
            // see https://medium.com/androiddevelopers/consuming-flows-safely-in-jetpack-compose-cde014d0d5a3
            myItems = viewModel.myItems.collectAsState().value,
            onClick = viewModel::onItemClicked
        )
    }
    

    Both options are viable (especially when using rememberSavable). However, i suggest to use optionB for people just migrating to compose, as its more similar to what they're used. I personally use OptionA for simple states, and OptionB for more complex ones (like a list)