Search code examples
google-cloud-firestoreandroid-jetpack-composedagger-hiltandroidx-lifecycle

LazyColumn duplicate display


Duplicate Display Screenshot Firestore Queries Screenshot

I am using Jetpack Compose + HiltViewModel + Firestore database

When I perform add action somehow the last entry is duplicated on LazyColumn.

The data is added correctly to the Firestore database [9604], but somehow the display is totally wrong [showing 2804 twice] until I restart the emulator or I rotate the device.

Below are the code that I think might be relevant.

@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
fun PatientsScreen(
    modifier: Modifier = Modifier,
    viewModel: PatientsViewModel = hiltViewModel()
) {
    val patients = viewModel.patients.collectAsStateWithLifecycle(emptyList())

    Column {
        LazyColumn {
            items(patients.value) { patientItem ->
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.SpaceBetween
                ) {
                    Text(patientItem.pid, fontSize = 24.sp)
                    Text(patientItem.location, fontSize = 24.sp)
                    Button(
                        onClick = { viewModel.onAcknowledge(patient = patientItem) },
                        enabled = patientItem.status.equals("Activated"),
                        shape = RoundedCornerShape(30)
                    ) {
                        if (patientItem.status.equals("Activated")) {
                            Text("Acknowledge")
                        } else {
                            Text(patientItem.status)
                        }
                    }
                }
            }
        }
        Button(
            onClick = { viewModel.registerPatient() },
            shape = CircleShape
        )
        {
            Text("+", fontSize = 36.sp)
        }
    }
}
@HiltViewModel
class PatientsViewModel @Inject constructor (
    private val storageService: StorageService,
        ) : ViewModel() {
    val patients = storageService.patients

    fun onAcknowledge(patient: Patient) {
        viewModelScope.launch {
            storageService.update(patient.copy(status = "Acknowledged"))
        }
    }

    fun registerPatient() {
        val patient = mutableStateOf(Patient())
        val newId = Random.nextInt(0, 9999).toString().padStart(4, '0')
        patient.value = patient.value.copy(pid = newId)
        patient.value = patient.value.copy(status = "Unused")

        viewModelScope.launch {
            val newPatient = patient.value
            storageService.add(newPatient)
        }
    }
}
class StorageServiceImpl
@Inject
constructor(private val firestore: FirebaseFirestore) : StorageService {
    override val patients: Flow<List<Patient>>
        get() =
            currentCollection().snapshots().map { snap ->
                snap.toObjects()
            }

    override suspend fun getPatient(patientId: String): Patient? =
        currentCollection().document(patientId).get().await().toObject()

    override suspend fun add(patient: Patient): String =
        currentCollection().add(patient).await().id

    override suspend fun update(patient: Patient) {
        currentCollection().document(patient.id).set(patient).await()
    }

    override suspend fun delete(patientId: String) {
        currentCollection().document(patientId).delete().await()
    }

    private fun currentCollection(): CollectionReference =
        firestore.collection(PATIENT_COLLECTION)

    companion object {
        private const val PATIENT_COLLECTION = "patients"
    }

}

The LazyColumn displaying the new entry registerPatient()


Solution

  • In case others encounter the same issue, this is due to my LazyColumn doesn't pass the key. After adding the key, the issue is solved. items(items = patients.value, key = { patientItem -> patientItem.pid })