Dayem Saeed
Dayem Saeed

Reputation: 380

LazyColumn UI not updating after successfully adding new item to list

I'm working on an app that tracks cars at a valet. My issue is that upon successfully adding a new car to the list, it navigates back to the list view but the added car is not shown in the list. Below is a simplified version of my code using dummy data. I'd appreciate any insight into this.

Data class

data class Car(
    val id: String,
    val model: String,
    val status: String // "Parked" or "Requested"
)

ViewModel

@HiltViewModel
class CarViewModel : ViewModel() {
    val carList = mutableStateListOf<Car>()

    init {
        // Initially populate the list with some dummy data
        carList.addAll(listOf(
            Car("1", "Car Model 1", "Parked"),
            Car("2", "Car Model 2", "Requested")
        ))
    }

    // function to add a new car
    fun addCar(car: Car) {
        carList.add(car)
    }
}

Car List

@Composable
fun CarList(viewModel: CarViewModel = hiltViewModel()) {
    Column {
        // List to display cars
        LazyColumn {
            items(items = viewModel.carList, key = { car -> car.id }) { car ->
                Text(text = "${car.model} - ${car.status}")
            }
        }
    }
}

Check In Screen

@Composable
fun CheckInScreen(navController: NavHostController, viewModel: CarViewModel = hiltViewModel()) {
    // Simplified form state
    var carModel by remember { mutableStateOf("") }

    Column(modifier = Modifier.padding(16.dp)) {
        Text(text = "Add New Car", style = MaterialTheme.typography.h6)
        Spacer(modifier = Modifier.height(16.dp))

        // Simple text field for car model
        OutlinedTextField(
            value = carModel,
            onValueChange = { carModel = it },
            label = { Text("Car Model") }
        )
        Spacer(modifier = Modifier.height(16.dp))

        // Button to add car
        Button(onClick = {
            if (carModel.isNotEmpty()) {
                val newCar = Car(id = UUID.randomUUID().toString(), model = carModel, status = "Parked")
                viewModel.addCar(newCar)
                navController.popBackStack() // navigates back to the car list
            }
        }) {
            Text("Add Car")
        }
    }
}

The flow looks like this: car list -> check in screen (add car) -> car list. Thank you!

Upvotes: 0

Views: 63

Answers (1)

tyg
tyg

Reputation: 15763

You have multiple instances of CarViewModel. One is displayed in CarList and each time CheckInScreen is called (which might occur much more often than just when you navigate to it) another one is created. Since they are sparate instances, modifying carList in one instance doesn't affect carList in the other instances.

What you want is that your composables all share the same instance of the view model.

There are several ways to achieve this:

  • Hoist the view model out of your screens to the activity where you can get one with val viewModel: CarViewModel by viewModels().
  • Explicitly define the ViewModelStoreOwner by setting set first parameter of viewModel() to the activity (or something else that should share the same view model).
  • Using a dependency injection framework to obtain your view models. Using Hilt you could use val viewModel: CarViewModel = hiltViewModel() in each composable where you need it. It is good practice, though, to hoist it at least to the screen level and only pass down the properties and functions that each composable actually needs.

I would prefer the last option, though you would need to set up Hilt first.

Upvotes: 1

Related Questions