Jetpack Compose中如何校验跨页面多个TextField邮箱值一致
跨页面邮箱一致性校验实现方案
你现有项目已接入Hilt共享ViewModel,优先选择状态收敛到ViewModel统一管理的方案,改动最小、稳定性最高,不需要额外处理传参序列化、状态丢失问题。
方案实现步骤
1. 修复现有代码的基础问题
首先修正ScreenA的变量名错误:你在ScreenA中定义的输入状态名为email,下方输入框却引用了未定义的emailState,会直接导致编译失败,后续统一替换为ViewModel中的全局状态即可。
另外你onSignUpEmail方法中查询Firestore时使用了未定义的email变量,后续替换为方法入参即可。
2. 扩展ViewModel的状态与校验逻辑
把三个输入框的状态、校验规则全部收敛到ViewModel中,避免每个页面单独维护本地状态导致不同步:
// 在MyViewModel中增加以下代码 // ScreenA输入的初始邮箱 val screenAEmail = mutableStateOf(TextFieldValue()) // ScreenB的邮箱输入 val screenBEmail = mutableStateOf(TextFieldValue()) // ScreenB的确认邮箱输入 val screenBConfirmEmail = mutableStateOf(TextFieldValue()) // 校验错误信息 val emailCheckError = mutableStateOf<String?>(null) // 从ScreenA跳转到ScreenB前调用,同步初始值 fun prepareNavigateToScreenB() { val inputEmail = screenAEmail.value.text.trim() if (inputEmail.isEmpty()) { emailCheckError.value = "请输入邮箱后再继续" return } // 给ScreenB两个输入框赋默认值,和ScreenA保持一致 screenBEmail.value = screenAEmail.value screenBConfirmEmail.value = screenAEmail.value emailCheckError.value = null } // 实时校验三个邮箱值是否一致 fun checkEmailConsistency(): Boolean { val originEmail = screenAEmail.value.text.trim() val inputBEmail = screenBEmail.value.text.trim() val inputConfirmEmail = screenBConfirmEmail.value.text.trim() return when { inputBEmail != originEmail -> { emailCheckError.value = "邮箱需与上一页输入内容一致" false } inputConfirmEmail != originEmail -> { emailCheckError.value = "确认邮箱需与上一页输入内容一致" false } else -> { emailCheckError.value = null true } } }
3. 修改ScreenA代码
移除本地remember的输入状态,直接使用ViewModel中的全局状态,跳转前做前置校验:
@Composable fun ScreenA( navController: NavController, model: MyViewModel ) { Column( Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally ) { OutlinedTextField( value = model.screenAEmail.value, colors = TextFieldDefaults.textFieldColors( backgroundColor = white, focusedIndicatorColor = Grey, unfocusedIndicatorColor = Grey, focusedLabelColor = Grey, unfocusedLabelColor = Grey, cursorColor = custom, textColor = custom, ), onValueChange = { model.screenAEmail.value = it }, label = { Text(text = "Email") }, placeholder = { Text(text = "Email") }, singleLine = true, modifier = Modifier.fillMaxWidth(0.8f) ) // 跳转按钮 Button( onClick = { model.prepareNavigateToScreenB() if (model.emailCheckError.value == null) { navController.navigate("screenB") } }, modifier = Modifier.padding(top = 16.dp) ) { Text("下一步") } // 错误提示 model.emailCheckError.value?.let { Text(text = it, color = Color.Red, modifier = Modifier.padding(top = 8.dp)) } } }
4. 修改ScreenB代码
同样使用ViewModel中的全局状态,输入时实时触发校验,提交前做最终一致性判断:
@Composable fun ScreenB( navController: NavController, model: MyViewModel ) { Column( Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { OutlinedTextField( value = model.screenBEmail.value, colors = TextFieldDefaults.textFieldColors( backgroundColor = white, focusedIndicatorColor = Grey, unfocusedIndicatorColor = Grey, focusedLabelColor = Grey, unfocusedLabelColor = Grey, cursorColor = custom, textColor = custom, ), onValueChange = { model.screenBEmail.value = it model.checkEmailConsistency() }, label = { Text(text = "E-mail") }, singleLine = true, modifier = Modifier.fillMaxWidth(0.8f), isError = model.emailCheckError.value != null ) OutlinedTextField( value = model.screenBConfirmEmail.value, colors = TextFieldDefaults.textFieldColors( backgroundColor = white, focusedIndicatorColor = Grey, unfocusedIndicatorColor = Grey, focusedLabelColor = Grey, unfocusedLabelColor = Grey, cursorColor = custom, textColor = custom, ), onValueChange = { model.screenBConfirmEmail.value = it model.checkEmailConsistency() }, label = { Text(text = "Confirm E-mail") }, placeholder = { Text(text = "Confirm E-mail") }, singleLine = true, modifier = Modifier.fillMaxWidth(0.8f), isError = model.emailCheckError.value != null ) // 错误提示 model.emailCheckError.value?.let { Text(text = it, color = Color.Red, modifier = Modifier.padding(top = 8.dp)) } // 提交按钮 Button( onClick = { if (model.checkEmailConsistency()) { model.onSignUpEmail( emailState = model.screenBEmail.value.text.trim(), confirmEmailState = model.screenBConfirmEmail.value.text.trim() ) } }, modifier = Modifier.padding(top = 16.dp), enabled = model.emailCheckError.value == null ) { Text("完成注册") } } }
可选优化
- 如果业务要求ScreenB的邮箱不允许用户修改,直接给两个
OutlinedTextField加上readOnly = true属性,从根源上避免输入不一致的问题 - 备选方案:如果不想把状态存在ViewModel中,也可以在ScreenA跳转时将邮箱字符串作为导航参数传递给ScreenB,ScreenB拿到参数后给输入框赋初始值,校验时直接对比传入的参数值即可。但这种方案需要处理路由参数的序列化适配,不如共享ViewModel方案简便。
内容的提问来源于stack exchange,提问作者SpaceDevs
相关产品推荐
相关产品推荐

