Jetpack Compose Profile Screen内容不显示问题求助(附代码)
问题:Jetpack Compose应用的Profile Screen无内容显示
我开发的基于Jetpack Compose的Android应用中,Profile Screen无法显示任何内容。已确认导航路由配置正确,推测是函数调用环节存在异常,现提供ViewModel、ProfileScreen组件及MainActivity的完整代码,恳请协助定位并解决该问题。
ViewModel代码
以下是我的ViewModel实现
@HiltViewModel class galBaatviewModel @Inject constructor( val auth: FirebaseAuth, var db : FirebaseFirestore, val storage : FirebaseStorage ) : ViewModel() { var inProcess = mutableStateOf(false) val eventMutableState = mutableStateOf<Event<String>?>(null) var signIn = mutableStateOf(false) val userData = mutableStateOf<UserData?>(null) init { val currentUser = auth.currentUser signIn.value = currentUser!= null currentUser?.uid?.let { getUserData(it) } } fun signUp(name: String, number: String, email: String, Password: String) { inProcess.value = true if(name.isEmpty() or number.isEmpty() or email.isEmpty() or Password.isEmpty()){ HandleException(customMessage = "Please fill all fields") return } inProcess.value = true db.collection(USER_NODE).whereEqualTo("number",number).get().addOnSuccessListener { if(it.isEmpty){ auth.createUserWithEmailAndPassword(email, Password).addOnCompleteListener { if (it.isSuccessful) { signIn.value = true createOrUpdateProfile(name,number) } else { HandleException(it.exception, customMessage = "Sign Up failed") } } } else{ HandleException(customMessage = "number already exist") inProcess.value = false } } } fun LogIn(email: String,Password: String){ if(email.isEmpty() or Password.isEmpty()){ HandleException(customMessage = "Please fill all the fields") } else{ inProcess.value = true auth.signInWithEmailAndPassword(email,Password).addOnCompleteListener { if(it.isSuccessful){ signIn.value = true inProcess.value = false auth.currentUser?.uid?.let { getUserData(it) } }else{ HandleException(exception = it.exception, customMessage = "Login failed") } } } } fun uploadProfileImage(uri : Uri){ uploadImage(uri){ createOrUpdateProfile(imageUrl = it.toString()) } } fun uploadImage(uri: Uri,onSuccess: (Uri)->Unit){ inProcess.value = true val storageRef = storage.reference val uuid = UUID.randomUUID() val imageRef = storageRef.child("images/$uuid") val uploadTask = imageRef.putFile(uri) uploadTask.addOnSuccessListener { val result = it.metadata?.reference?.downloadUrl result?.addOnSuccessListener(onSuccess) inProcess.value = false } .addOnFailureListener{ HandleException(it) } } fun createOrUpdateProfile(name: String?=null,number: String?=null,imageUrl:String?=null){ var uid=auth.currentUser?.uid val userData =UserData( userId = uid, name = name?:userData.value?.name, number = number?: userData.value?.number, imageUrl = imageUrl?: userData.value?.imageUrl ) uid?.let{ inProcess.value = true db.collection(USER_NODE).document(uid).get().addOnSuccessListener { if(it.exists()){ //update user data } else{ db.collection(USER_NODE).document(uid).set(userData) inProcess.value = false getUserData(uid ) } } .addOnFailureListener{ HandleException(it, "cannot retrieve user") } } } private fun getUserData(uid: String) { inProcess.value = true db.collection(USER_NODE).document(uid).addSnapshotListener{ value, error-> if(error!= null){ HandleException(error,"can not retrieve user") } if(value != null){ var user = value.toObject<UserData>() userData.value = user inProcess.value = false } } } fun HandleException(exception: Exception? = null, customMessage: String = "") { Log.e("GalBaat", "GalBaat exception: ", exception) exception?.printStackTrace() val errorMsg = exception?.localizedMessage ?: "" val message = if (customMessage.isNullOrEmpty()) errorMsg else customMessage eventMutableState.value = Event(message) inProcess.value = false } }
ProfileScreen组件代码
以下是我的ProfileScreen实现
package com.example.galbaat.screen @Composable fun ProfileScreen(navController: NavController,vm : galBaatviewModel) { val inProgress = vm.inProcess.value if(inProgress){ CommonProgressBar() }else{ Column { ProfileContent( modifier = Modifier .weight(1f) .verticalScroll(rememberScrollState()) .padding(8.dp), vm =vm, name = "", number = "", onNameChange = {""}, onNumberChange = {""}, onSave = {}, onBack = {}, onLogOut ={} ) BottomNavigationMenu(selectedItem = BottomNavigationItem.PROFILE, navController = navController) } } } @OptIn(ExperimentalMaterial3Api::class) @Composable fun ProfileContent( vm: galBaatviewModel, name: String, number:String, onNameChange:(String)->Unit, onNumberChange:(String)->Unit, onBack : ()->Unit, onSave: ()-> Unit, onLogOut: () -> Unit, modifier: Modifier,) { val imageUrl = vm.userData.value?.imageUrl Column { Row( modifier = Modifier .fillMaxWidth() .padding(8.dp), horizontalArrangement = Arrangement.SpaceBetween, ) { Text(text = "BACK", Modifier.clickable { onBack.invoke() }) Text(text = "SAVE", Modifier.clickable { onSave.invoke() }) CommonDivider() ProfileImage(imageUrl = imageUrl, vm = vm) CommonDivider() Row( modifier = Modifier .fillMaxWidth() .padding(4.dp), verticalAlignment = Alignment.CenterVertically ) { Text(text = "Name", modifier = Modifier.width(100.dp)) TextField( value = name, onValueChange = onNameChange, colors = TextFieldDefaults.textFieldColors( focusedTextColor = Color.Black, containerColor = Color.Transparent ) ) } Row( modifier = Modifier .fillMaxWidth() .padding(4.dp), verticalAlignment = Alignment.CenterVertically ) { Text(text = "Number", modifier = Modifier.width(100.dp)) TextField( value = number, onValueChange = onNumberChange, colors = TextFieldDefaults.textFieldColors( focusedTextColor = Color.Black, containerColor = Color.Transparent ) ) } CommonDivider() Row( modifier = Modifier .fillMaxWidth() .padding(16.dp), horizontalArrangement = Arrangement.Center ) { Text(text = "Logout", modifier = Modifier.clickable { onLogOut.invoke() }) } } } } @Composable fun ProfileImage(imageUrl:String?,vm: galBaatviewModel){ val launcher = rememberLauncherForActivityResult(contract = ActivityResultContracts.GetContent() ) { uri -> uri?.let { vm.uploadProfileImage(uri) } } Box(modifier = Modifier.height(intrinsicSize = IntrinsicSize.Min)){ Column(modifier = Modifier .padding(8.dp) .fillMaxWidth() .clickable { launcher.launch("image/*") }, horizontalAlignment = Alignment.CenterHorizontally) { Card(shape = CircleShape, modifier = Modifier .padding(8.dp) .size(100.dp)) { CommonImage(data = imageUrl) } Text(text = "Change Profile Picture") } if(vm.inProcess.value) CommonProgressBar() } }
MainActivity代码
以下是我的MainActivity实现
package com.example.galbaat sealed class DestinationScreens(var route: String){ object SignUp : DestinationScreens("signup") object Login : DestinationScreens("login") object profile : DestinationScreens("ProfileScreen") object ChatList : DestinationScreens("chatList") object SingleChat : DestinationScreens("singleChat/{chatId}"){ fun createRoute(id: String) = "SingleChat/$id" } object StatusList : DestinationScreens("statusList") object SingleStatus : DestinationScreens("singleStatus/{UserId}"){ fun createRoute(Userid: String) = "SingleChat/$Userid" } } @AndroidEntryPoint class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { GALBAATTheme { // A surface container using the 'background' color from the theme Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { chatAppNavigation() } } } } @Composable fun chatAppNavigation(){ val navController = rememberNavController() var vm = hiltViewModel<galBaatviewModel>() NavHost(navController = navController, startDestination = DestinationScreens.SignUp.route){ composable(DestinationScreens.SignUp.route){ SignUp(navController,vm) } composable(DestinationScreens.Login.route){ loginScreen(navController=navController,vm = vm) } composable(DestinationScreens.ChatList.route){ chatScreen(navController = navController , vm = vm) } composable(DestinationScreens.StatusList.route){ StatusScreen(navController = navController , vm = vm) } composable(DestinationScreens.profile.route){ ProfileScreen(navController = navController , vm = vm) } } } }
问题定位与解决方案
1. ProfileContent布局嵌套错误
ProfileContent中所有子元素都被包裹在同一个Row内,导致元素挤压重叠甚至超出屏幕,视觉上无内容显示。需将元素拆分到Column的直接子层级:
@OptIn(ExperimentalMaterial3Api::class) @Composable fun ProfileContent( vm: galBaatviewModel, name: String, number:String, onNameChange:(String)->Unit, onNumberChange:(String)->Unit, onBack : ()->Unit, onSave: ()-> Unit, onLogOut: () -> Unit, modifier: Modifier,) { val imageUrl = vm.userData.value?.imageUrl Column(modifier = modifier) { // 顶部返回/保存按钮行 Row( modifier = Modifier .fillMaxWidth() .padding(8.dp), horizontalArrangement = Arrangement.SpaceBetween, ) { Text(text = "BACK", Modifier.clickable { onBack.invoke() }) Text(text = "SAVE", Modifier.clickable { onSave.invoke() }) } CommonDivider() // 头像区域 ProfileImage(imageUrl = imageUrl, vm = vm) CommonDivider() // 姓名输入行 Row( modifier = Modifier .fillMaxWidth() .padding(4.dp), verticalAlignment = Alignment.CenterVertically ) { Text(text = "Name", modifier = Modifier.width(100.dp)) TextField( value = name, onValueChange = onNameChange, colors = TextFieldDefaults.textFieldColors( focusedTextColor = Color.Black, containerColor = Color.Transparent ) ) } // 手机号输入行 Row( modifier = Modifier .fillMaxWidth() .padding(4.dp), verticalAlignment = Alignment.CenterVertically ) { Text(text = "Number", modifier = Modifier.width(100.dp)) TextField( value = number, onValueChange = onNumberChange, colors = TextFieldDefaults.textFieldColors( focusedTextColor = Color.Black, containerColor = Color.Transparent ) ) } CommonDivider() // 登出行 Row( modifier = Modifier .fillMaxWidth() .padding(16.dp), horizontalArrangement = Arrangement.Center ) { Text(text = "Logout", modifier = Modifier.clickable { onLogOut.invoke() }) } } }
2. ProfileScreen未绑定用户数据
当前ProfileScreen传入ProfileContent的name和number是固定空字符串,需绑定ViewModel中的userData:
@Composable fun ProfileScreen(navController: NavController,vm : galBaatviewModel) { val inProgress = vm.inProcess.value val userData = vm.userData.value if(inProgress){ CommonProgressBar() }else{ Column(modifier = Modifier.fillMaxSize()) { ProfileContent( modifier = Modifier .weight(1f) .verticalScroll(rememberScrollState()) .padding(8.dp), vm =vm, name = userData?.name ?: "", number = userData?.number ?: "", onNameChange = { /* 后续实现修改逻辑 */ }, onNumberChange = { /* 后续实现修改逻辑 */ }, onSave = { /* 后续实现保存逻辑 */ }, onBack = { navController.popBackStack() }, onLogOut = { vm.auth.signOut() vm.signIn.value = false navController.navigate(DestinationScreens.Login.route) { popUpTo(DestinationScreens.SignUp.route) { inclusive = true } } } ) BottomNavigationMenu(selectedItem = BottomNavigationItem.PROFILE, navController = navController) } } }
3. ViewModel中createOrUpdateProfile的更新逻辑缺失
当用户数据已存在时,代码未执行更新操作,导致修改后的数据无法同步到Firestore:
fun createOrUpdateProfile(name: String?=null,number: String?=null,imageUrl:String?=null){ val uid=auth.currentUser?.uid ?: return val updatedUserData = UserData( userId = uid, name = name?:userData.value?.name, number = number?: userData.value?.number, imageUrl = imageUrl?: userData.value?.imageUrl ) inProcess.value = true db.collection(USER_NODE).document(uid).get().addOnSuccessListener { docSnapshot -> if(docSnapshot.exists()){ db.collection(USER_NODE).document(uid).set(updatedUserData) .addOnSuccessListener { inProcess.value = false getUserData(uid) } .addOnFailureListener { HandleException(it, "Failed to update profile") } } else{ db.collection(USER_NODE).document(uid).set(updatedUserData) .addOnSuccessListener { inProcess.value = false getUserData(uid) } .addOnFailureListener { HandleException(it, "Failed to create profile") } } } .addOnFailureListener{ HandleException(it, "cannot retrieve user") } }
内容的提问来源于stack exchange,提问作者Tejaswi bhagat
相关产品推荐
相关产品推荐

