如何在Jetpack Compose中检查Firestore数据库中用户名是否存在
解决Firestore异步检查用户名存在的问题
你的核心问题是Firestore的查询是异步操作,原userExists函数在回调执行前就直接返回了初始的false,导致判断逻辑完全失效。下面是具体的修复方案:
1. 重构userExists为挂起函数
把同步函数改成挂起函数,用await()替代回调,确保等待查询完成后再返回结果:
import kotlinx.coroutines.tasks.await suspend fun userExists(fieldName: String, fieldValue: String): Boolean { val db = FirebaseFirestore.getInstance() val dbUsers = db.collection("user") val querySnapshot = dbUsers.whereEqualTo(fieldName, fieldValue).get().await() // 文档不为空说明用户名已存在 return querySnapshot.documents.isNotEmpty() }
2. 在Composable中用协程执行异步检查
Jetpack Compose的点击事件是普通lambda,不能直接调用挂起函数,需要用rememberCoroutineScope启动协程,同时建议添加加载状态避免重复点击:
修改SignUpScreen的代码:
@OptIn(ExperimentalMaterial3Api::class) @Composable fun SignUpScreen(navigation: NavController, auth: FirebaseAuth) { var userName by remember { mutableStateOf("") } var userEmail by remember { mutableStateOf("") } var password by rememberSaveable { mutableStateOf("") } var passwordVisible by rememberSaveable { mutableStateOf(false) } // 添加加载状态,防止重复点击 var isLoading by remember { mutableStateOf(false) } val context = LocalContext.current val sharedPrefMail = context.getSharedPreferences("userEmail", Context.MODE_PRIVATE) val sharedPrefName = context.getSharedPreferences("userName", Context.MODE_PRIVATE) val sharedPrefScore = context.getSharedPreferences("highScore", Context.MODE_PRIVATE) // 获取Composable安全的协程Scope val coroutineScope = rememberCoroutineScope() val buttonSize = with(LocalDensity.current) { (300f).toDp() } Column( modifier = Modifier .fillMaxSize() .padding(vertical = buttonSize), horizontalAlignment = Alignment.CenterHorizontally ) { // ... 保留原有的输入UI代码 ... Button( onClick = { coroutineScope.launch { if (isLoading) return@launch // 加载中不重复执行 isLoading = true when { userEmail.isEmpty() || password.isEmpty() || userName.isEmpty() -> { Toast.makeText(context, "请输入邮箱、用户名和密码", Toast.LENGTH_LONG).show() } userExists("userName", userName) -> { Toast.makeText(context, "用户名已被使用", Toast.LENGTH_LONG).show() } else -> { auth.createUserWithEmailAndPassword(userEmail, password) .addOnSuccessListener { val user = UserObj(userEmail, userName, "0") FirebaseFirestore.getInstance().collection("user") .add(user) .addOnSuccessListener { Toast.makeText(context, "用户创建成功", Toast.LENGTH_LONG).show() sharedPrefMail.edit().putString("userMail", userEmail).apply() sharedPrefName.edit().putString("userName", userName).apply() sharedPrefScore.edit().putString("highScore", "0").apply() navigation.navigate("FirstScreen") } .addOnFailureListener { Toast.makeText(context, "用户创建失败", Toast.LENGTH_LONG).show() } } .addOnFailureListener { Toast.makeText(context, "无法创建用户:${it.localizedMessage}", Toast.LENGTH_LONG).show() } } } isLoading = false // 结束加载状态 } }, modifier = Modifier.size(buttonSize, buttonSize/3), enabled = !isLoading // 加载中禁用按钮 ) { Text(if (isLoading) "处理中..." else "注册") } } }
关键修复点说明
- 用
await()替代addOnSuccessListener:挂起函数会等待Firestore查询完成,确保返回正确的用户名存在状态。 - 用
rememberCoroutineScope启动协程:在Composable中安全执行异步操作,避免内存泄漏。 - 添加加载状态:防止用户重复点击,同时给用户明确的操作反馈。
- 调整逻辑顺序:确保用户名检查完成后,再执行后续的注册流程。
内容的提问来源于stack exchange,提问作者Yunus Emrah Uluçay
相关产品推荐
相关产品推荐

