最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

android - Saving String List to Firestore - Stack Overflow

programmeradmin0浏览0评论

I am working to save times which is chosen by users. Chosen times should be saved to firestore. In my logs, Log.d("FirestoreSave", "Saving Place: $placeInfo") shows the times also creates availableTimes area in firestore but array is looking empty. Also other inputs from user shown without problem.

My Data Class

data class PlaceData(
        val name: String = "",
        val capacity: Int = 0,
        val id: String = "",
        val placeImageUrl: String = "",
        val availableTimes: List<String> = emptyList() // Firestore can handle this
    ):Serializable

Repository Implementation

override suspend fun addPlace(placeData: PlaceData): Flow<RootResult<Boolean>> = flow {
        emit(RootResult.Loading)
        try {
            val currentUser = firebaseAuth.currentUser
            val userId = currentUser?.uid
            if (userId != null) {
                val placeId = firestore.collection("users").document(userId).collection("places")
                    .document().id

                // Create a map of the data to save
                val placeInfo = mapOf(
                    "name" to placeData.name,
                    "capacity" to placeData.capacity,
                    "id" to placeId,
                    "placeImageUrl" to placeData.placeImageUrl,
                    "availableTimes" to placeData.availableTimes // Ensure this is a List<String>
                )

                Log.d("FirestoreSave", "Saving place: $placeInfo")

                // Save the data to Firestore
                firestore.collection("users").document(userId).collection("places")
                    .document(placeId).set(placeInfo, SetOptions.merge())
                    .addOnSuccessListener {
                        Log.d("FirestoreSave", "Place saved successfully!")
                    }
                    .addOnFailureListener { e ->
                        Log.e("FirestoreSave", "Failed to save place: ", e)
                    }

                emit(RootResult.Success(true))
            } else {
                emit(RootResult.Error("User ID is null"))
            }
        } catch (e: Exception) {
            emit(RootResult.Error(e.message ?: "Something went wrong"))
        }
    }.flowOn(Dispatchers.IO)

My Ui

@Composable
fun AddPlaceDialog(
    onDismiss: () -> Unit,
    onSave: (PlaceData) -> Unit,
    onImagePick: () -> Unit,
    selectedImageUri: Uri?,
    context: Context
) {
    var expanded by remember { mutableStateOf(false) }
    var customPlaceName by remember { mutableStateOf("") }
    val availableTimes = remember { mutableStateListOf<String>() }
    val timePickerDialog = remember { mutableStateOf(false) }

    AlertDialog(
        containerColor = Color.Black,
        onDismissRequest = onDismiss,
        title = {
            Text(
                text = "Add Place",
                style = TextStyle(color = Color.White, fontSize = 25.sp)
            )
        },
        text = {
            Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
                // Image Picker
                Box(
                    modifier = Modifier.fillMaxWidth()
                        .height(128.dp)
                        .clip(RoundedCornerShape(8.dp))
                        .background(Color.White)
                        .clickable { onImagePick() }
                        .align(Alignment.CenterHorizontally)
                        .border(1.dp, Color.Black, RoundedCornerShape(8.dp))
                ) {
                    selectedImageUri?.let { uri ->
                        Image(
                            painter = rememberAsyncImagePainter(uri),
                            contentDescription = "Selected Image",
                            modifier = Modifier.fillMaxSize(),
                            contentScale = ContentScale.Crop,
                        )
                    } ?: run {
                        Icon(
                            imageVector = Icons.Filled.Add,
                            contentDescription = "Select Image",
                            modifier = Modifier.align(Alignment.Center).size(50.dp),
                            tint = Color.Red
                        )
                    }
                }

                Spacer(modifier = Modifier.height(16.dp))

                // Place Name Input
                TextField(
                    colors = TextFieldDefaults.colors(
                        unfocusedContainerColor = Color.White,
                        focusedContainerColor = Color.White,
                        focusedTextColor = Color.Black,
                        focusedIndicatorColor = Color.Black,
                        unfocusedIndicatorColor = Color.Black,
                        unfocusedLabelColor = Color.Black,
                        focusedLabelColor = Color.Black,
                        cursorColor = Color.Black,
                        unfocusedTextColor = Color.Black
                    ),
                    value = customPlaceName,
                    onValueChange = { customPlaceName = it },
                    label = { Text("Enter Place Name", fontSize = 13.sp, color = Color.Black) },
                    modifier = Modifier.fillMaxWidth()
                )

                Spacer(modifier = Modifier.height(16.dp))

                // Add Time Button
                Button(
                    onClick = { timePickerDialog.value = true },
                    colors = ButtonDefaults.buttonColors(containerColor = Color.White),
                    modifier = Modifier.fillMaxWidth()
                ) {
                    Icon(imageVector = Icons.Filled.Add, contentDescription = "Add Time", tint = Color.Black)
                    Spacer(modifier = Modifier.width(8.dp))
                    Text("Add Available Time", color = Color.Black)
                }

                Spacer(modifier = Modifier.height(16.dp))

                // Display Selected Times
                availableTimes.forEachIndexed { index, time ->
                    Row(
                        modifier = Modifier.fillMaxWidth()
                            .padding(vertical = 4.dp)
                            .background(Color.Gray.copy(alpha = 0.2f), RoundedCornerShape(8.dp))
                            .padding(8.dp),
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        Text(text = time, color = Color.White, modifier = Modifier.weight(1f))
                        IconButton(onClick = { availableTimes.removeAt(index) }) {
                            Icon(imageVector = Icons.Filled.Delete, contentDescription = "Remove", tint = Color.Red)
                        }
                    }
                }
            }
        },
        confirmButton = {
            AuthButtonComponent(
                value = "Save",
                onClick = {
//                    if (selectedImageUri == null) {
//                        Toast.makeText(context, "Please choose an image.", Toast.LENGTH_SHORT).show()
//                        return@AuthButtonComponent
//                    }
//                    if (customPlaceName.isBlank()) {
//                        Toast.makeText(context, "Please enter a place name.", Toast.LENGTH_SHORT).show()
//                        return@AuthButtonComponent
//                    }
//                    if (availableTimes.isEmpty()) {
//                        Toast.makeText(context, "Please add at least one available time.", Toast.LENGTH_SHORT).show()
//                        return@AuthButtonComponent
//                    }


                    val placeData = PlaceData(
                        name = customPlaceName,
                        placeImageUrl = selectedImageUri.toString(),
                        availableTimes = availableTimes.toList()
                    )

                    Log.d("PlaceData", "Saving PlaceData: $placeData")
                    onSave(placeData)
                    onDismiss()
                },
                modifier = Modifier.width(70.dp),
                fillMaxWidth = false,
                heightIn = 40.dp,
                firstColor = Color.Black,
                secondColor = Color.Black
            )
        },
        dismissButton = {
            AuthButtonComponent(
                value = "Cancel",
                onClick = { onDismiss() },
                modifier = Modifier.width(80.dp),
                fillMaxWidth = false,
                heightIn = 40.dp,
                firstColor = Color.Black,
                secondColor = Color.Black
            )
        }
    )

    // Time Picker Dialog
    if (timePickerDialog.value) {
        val timePicker = TimePickerDialog(
            context,
            { _, hour, minute ->
                val selectedTime = String.format("%02d:%02d", hour, minute)
                val times = availableTimes.add(selectedTime)
               val list =  times.toString()
                timePickerDialog.value = false
                Log.d("AvailableTimes", availableTimes.toString())
            },
            24, 0, true
        )
        timePicker.show()
    }
}

I dont understand why it is not working please help mee

I am working to save times which is chosen by users. Chosen times should be saved to firestore. In my logs, Log.d("FirestoreSave", "Saving Place: $placeInfo") shows the times also creates availableTimes area in firestore but array is looking empty. Also other inputs from user shown without problem.

My Data Class

data class PlaceData(
        val name: String = "",
        val capacity: Int = 0,
        val id: String = "",
        val placeImageUrl: String = "",
        val availableTimes: List<String> = emptyList() // Firestore can handle this
    ):Serializable

Repository Implementation

override suspend fun addPlace(placeData: PlaceData): Flow<RootResult<Boolean>> = flow {
        emit(RootResult.Loading)
        try {
            val currentUser = firebaseAuth.currentUser
            val userId = currentUser?.uid
            if (userId != null) {
                val placeId = firestore.collection("users").document(userId).collection("places")
                    .document().id

                // Create a map of the data to save
                val placeInfo = mapOf(
                    "name" to placeData.name,
                    "capacity" to placeData.capacity,
                    "id" to placeId,
                    "placeImageUrl" to placeData.placeImageUrl,
                    "availableTimes" to placeData.availableTimes // Ensure this is a List<String>
                )

                Log.d("FirestoreSave", "Saving place: $placeInfo")

                // Save the data to Firestore
                firestore.collection("users").document(userId).collection("places")
                    .document(placeId).set(placeInfo, SetOptions.merge())
                    .addOnSuccessListener {
                        Log.d("FirestoreSave", "Place saved successfully!")
                    }
                    .addOnFailureListener { e ->
                        Log.e("FirestoreSave", "Failed to save place: ", e)
                    }

                emit(RootResult.Success(true))
            } else {
                emit(RootResult.Error("User ID is null"))
            }
        } catch (e: Exception) {
            emit(RootResult.Error(e.message ?: "Something went wrong"))
        }
    }.flowOn(Dispatchers.IO)

My Ui

@Composable
fun AddPlaceDialog(
    onDismiss: () -> Unit,
    onSave: (PlaceData) -> Unit,
    onImagePick: () -> Unit,
    selectedImageUri: Uri?,
    context: Context
) {
    var expanded by remember { mutableStateOf(false) }
    var customPlaceName by remember { mutableStateOf("") }
    val availableTimes = remember { mutableStateListOf<String>() }
    val timePickerDialog = remember { mutableStateOf(false) }

    AlertDialog(
        containerColor = Color.Black,
        onDismissRequest = onDismiss,
        title = {
            Text(
                text = "Add Place",
                style = TextStyle(color = Color.White, fontSize = 25.sp)
            )
        },
        text = {
            Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
                // Image Picker
                Box(
                    modifier = Modifier.fillMaxWidth()
                        .height(128.dp)
                        .clip(RoundedCornerShape(8.dp))
                        .background(Color.White)
                        .clickable { onImagePick() }
                        .align(Alignment.CenterHorizontally)
                        .border(1.dp, Color.Black, RoundedCornerShape(8.dp))
                ) {
                    selectedImageUri?.let { uri ->
                        Image(
                            painter = rememberAsyncImagePainter(uri),
                            contentDescription = "Selected Image",
                            modifier = Modifier.fillMaxSize(),
                            contentScale = ContentScale.Crop,
                        )
                    } ?: run {
                        Icon(
                            imageVector = Icons.Filled.Add,
                            contentDescription = "Select Image",
                            modifier = Modifier.align(Alignment.Center).size(50.dp),
                            tint = Color.Red
                        )
                    }
                }

                Spacer(modifier = Modifier.height(16.dp))

                // Place Name Input
                TextField(
                    colors = TextFieldDefaults.colors(
                        unfocusedContainerColor = Color.White,
                        focusedContainerColor = Color.White,
                        focusedTextColor = Color.Black,
                        focusedIndicatorColor = Color.Black,
                        unfocusedIndicatorColor = Color.Black,
                        unfocusedLabelColor = Color.Black,
                        focusedLabelColor = Color.Black,
                        cursorColor = Color.Black,
                        unfocusedTextColor = Color.Black
                    ),
                    value = customPlaceName,
                    onValueChange = { customPlaceName = it },
                    label = { Text("Enter Place Name", fontSize = 13.sp, color = Color.Black) },
                    modifier = Modifier.fillMaxWidth()
                )

                Spacer(modifier = Modifier.height(16.dp))

                // Add Time Button
                Button(
                    onClick = { timePickerDialog.value = true },
                    colors = ButtonDefaults.buttonColors(containerColor = Color.White),
                    modifier = Modifier.fillMaxWidth()
                ) {
                    Icon(imageVector = Icons.Filled.Add, contentDescription = "Add Time", tint = Color.Black)
                    Spacer(modifier = Modifier.width(8.dp))
                    Text("Add Available Time", color = Color.Black)
                }

                Spacer(modifier = Modifier.height(16.dp))

                // Display Selected Times
                availableTimes.forEachIndexed { index, time ->
                    Row(
                        modifier = Modifier.fillMaxWidth()
                            .padding(vertical = 4.dp)
                            .background(Color.Gray.copy(alpha = 0.2f), RoundedCornerShape(8.dp))
                            .padding(8.dp),
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        Text(text = time, color = Color.White, modifier = Modifier.weight(1f))
                        IconButton(onClick = { availableTimes.removeAt(index) }) {
                            Icon(imageVector = Icons.Filled.Delete, contentDescription = "Remove", tint = Color.Red)
                        }
                    }
                }
            }
        },
        confirmButton = {
            AuthButtonComponent(
                value = "Save",
                onClick = {
//                    if (selectedImageUri == null) {
//                        Toast.makeText(context, "Please choose an image.", Toast.LENGTH_SHORT).show()
//                        return@AuthButtonComponent
//                    }
//                    if (customPlaceName.isBlank()) {
//                        Toast.makeText(context, "Please enter a place name.", Toast.LENGTH_SHORT).show()
//                        return@AuthButtonComponent
//                    }
//                    if (availableTimes.isEmpty()) {
//                        Toast.makeText(context, "Please add at least one available time.", Toast.LENGTH_SHORT).show()
//                        return@AuthButtonComponent
//                    }


                    val placeData = PlaceData(
                        name = customPlaceName,
                        placeImageUrl = selectedImageUri.toString(),
                        availableTimes = availableTimes.toList()
                    )

                    Log.d("PlaceData", "Saving PlaceData: $placeData")
                    onSave(placeData)
                    onDismiss()
                },
                modifier = Modifier.width(70.dp),
                fillMaxWidth = false,
                heightIn = 40.dp,
                firstColor = Color.Black,
                secondColor = Color.Black
            )
        },
        dismissButton = {
            AuthButtonComponent(
                value = "Cancel",
                onClick = { onDismiss() },
                modifier = Modifier.width(80.dp),
                fillMaxWidth = false,
                heightIn = 40.dp,
                firstColor = Color.Black,
                secondColor = Color.Black
            )
        }
    )

    // Time Picker Dialog
    if (timePickerDialog.value) {
        val timePicker = TimePickerDialog(
            context,
            { _, hour, minute ->
                val selectedTime = String.format("%02d:%02d", hour, minute)
                val times = availableTimes.add(selectedTime)
               val list =  times.toString()
                timePickerDialog.value = false
                Log.d("AvailableTimes", availableTimes.toString())
            },
            24, 0, true
        )
        timePicker.show()
    }
}

I dont understand why it is not working please help mee

Share Improve this question asked Feb 6 at 14:39 ponyboyltcponyboyltc 11 bronze badge New contributor ponyboyltc is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct. 2
  • 1 You are emitting even before the Task started by document#set() finishes -- it is an asynchronous operation. You should move the emit calls inside the addOnSuccessListener() and addOnFailureListener(). – Jay Commented Feb 6 at 14:43
  • 1 Sounds like an answer, Jay
发布评论

评论列表(0)

  1. 暂无评论