Trang Profile
Hướng dẫn xây dựng màn hình Profile với header, avatar, và settings trong Compose Multiplatform.
Profile Screen
Phần tiêu đề “Profile Screen”@Composablefun ProfileScreen( user: User, onEditProfile: () -> Unit, onSettings: () -> Unit, onLogout: () -> Unit) { Scaffold( topBar = { TopAppBar( title = { Text("Profile") }, actions = { IconButton(onClick = onSettings) { Icon(Icons.Default.Settings, "Settings") } } ) } ) { padding -> LazyColumn( modifier = Modifier .fillMaxSize() .padding(padding) ) { // Profile Header item { ProfileHeader( user = user, onEditProfile = onEditProfile ) }
// Stats item { ProfileStats( posts = 42, followers = 1234, following = 567 ) }
// Menu items item { ProfileMenuSection(onLogout = onLogout) } } }}Profile Header
Phần tiêu đề “Profile Header”@Composablefun ProfileHeader( user: User, onEditProfile: () -> Unit) { Column( modifier = Modifier .fillMaxWidth() .padding(24.dp), horizontalAlignment = Alignment.CenterHorizontally ) { // Avatar Box(contentAlignment = Alignment.BottomEnd) { Box( modifier = Modifier .size(100.dp) .background( MaterialTheme.colorScheme.primary, CircleShape ), contentAlignment = Alignment.Center ) { if (user.avatarUrl != null) { // AsyncImage(user.avatarUrl) Text( user.name.first().uppercase(), style = MaterialTheme.typography.headlineLarge, color = Color.White ) } else { Text( user.name.first().uppercase(), style = MaterialTheme.typography.headlineLarge, color = Color.White ) } }
// Edit avatar button Box( modifier = Modifier .size(32.dp) .background( MaterialTheme.colorScheme.surface, CircleShape ) .border(2.dp, MaterialTheme.colorScheme.surface, CircleShape), contentAlignment = Alignment.Center ) { Icon( Icons.Default.CameraAlt, contentDescription = "Change avatar", modifier = Modifier.size(16.dp) ) } }
Spacer(Modifier.height(16.dp))
// Name Text( user.name, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold )
// Email Text( user.email, style = MaterialTheme.typography.bodyMedium, color = Color.Gray )
Spacer(Modifier.height(8.dp))
// Bio user.bio?.let { bio -> Text( bio, style = MaterialTheme.typography.bodyMedium, textAlign = TextAlign.Center, modifier = Modifier.padding(horizontal = 32.dp) ) }
Spacer(Modifier.height(16.dp))
// Edit button OutlinedButton( onClick = onEditProfile, shape = RoundedCornerShape(20.dp) ) { Icon(Icons.Default.Edit, contentDescription = null, Modifier.size(16.dp)) Spacer(Modifier.width(8.dp)) Text("Edit Profile") } }}Profile Stats
Phần tiêu đề “Profile Stats”@Composablefun ProfileStats( posts: Int, followers: Int, following: Int) { Row( modifier = Modifier .fillMaxWidth() .padding(vertical = 16.dp), horizontalArrangement = Arrangement.SpaceEvenly ) { StatItem(value = posts, label = "Posts") StatItem(value = followers, label = "Followers") StatItem(value = following, label = "Following") }}
@Composableprivate fun StatItem( value: Int, label: String) { Column(horizontalAlignment = Alignment.CenterHorizontally) { Text( text = formatNumber(value), style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold ) Text( text = label, style = MaterialTheme.typography.bodySmall, color = Color.Gray ) }}
private fun formatNumber(num: Int): String { return when { num >= 1_000_000 -> "${num / 1_000_000}M" num >= 1_000 -> "${num / 1_000}K" else -> num.toString() }}Profile Menu Section
Phần tiêu đề “Profile Menu Section”@Composablefun ProfileMenuSection( onLogout: () -> Unit) { Column(modifier = Modifier.padding(16.dp)) { Text( "Settings", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold, modifier = Modifier.padding(bottom = 8.dp) )
Card( modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(12.dp) ) { Column { ProfileMenuItem( icon = Icons.Default.Person, title = "Account", subtitle = "Manage your account", onClick = { } )
HorizontalDivider()
ProfileMenuItem( icon = Icons.Default.Notifications, title = "Notifications", subtitle = "Notification preferences", onClick = { } )
HorizontalDivider()
ProfileMenuItem( icon = Icons.Default.Lock, title = "Privacy", subtitle = "Privacy settings", onClick = { } )
HorizontalDivider()
ProfileMenuItem( icon = Icons.Default.Help, title = "Help & Support", subtitle = "Get help or report issues", onClick = { } ) } }
Spacer(Modifier.height(16.dp))
// Logout button OutlinedButton( onClick = onLogout, modifier = Modifier.fillMaxWidth(), colors = ButtonDefaults.outlinedButtonColors( contentColor = MaterialTheme.colorScheme.error ), border = BorderStroke(1.dp, MaterialTheme.colorScheme.error), shape = RoundedCornerShape(12.dp) ) { Icon(Icons.Default.Logout, contentDescription = null) Spacer(Modifier.width(8.dp)) Text("Logout") }
Spacer(Modifier.height(16.dp))
// App version Text( "Version 1.0.0", modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center, style = MaterialTheme.typography.bodySmall, color = Color.Gray ) }}
@Composablefun ProfileMenuItem( icon: ImageVector, title: String, subtitle: String, onClick: () -> Unit) { Row( modifier = Modifier .fillMaxWidth() .clickable(onClick = onClick) .padding(16.dp), verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = icon, contentDescription = null, tint = MaterialTheme.colorScheme.primary )
Spacer(Modifier.width(16.dp))
Column(modifier = Modifier.weight(1f)) { Text(title, fontWeight = FontWeight.Medium) Text( subtitle, style = MaterialTheme.typography.bodySmall, color = Color.Gray ) }
Icon( Icons.Default.ChevronRight, contentDescription = null, tint = Color.Gray ) }}Edit Profile Screen
Phần tiêu đề “Edit Profile Screen”@Composablefun EditProfileScreen( user: User, onSave: (User) -> Unit, onBack: () -> Unit) { var name by remember { mutableStateOf(user.name) } var email by remember { mutableStateOf(user.email) } var bio by remember { mutableStateOf(user.bio ?: "") } var phone by remember { mutableStateOf(user.phone ?: "") }
Scaffold( topBar = { TopAppBar( title = { Text("Edit Profile") }, navigationIcon = { IconButton(onClick = onBack) { Icon(Icons.Default.Close, "Close") } }, actions = { TextButton(onClick = { onSave(user.copy(name = name, email = email, bio = bio, phone = phone)) }) { Text("Save") } } ) } ) { padding -> Column( modifier = Modifier .fillMaxSize() .padding(padding) .padding(16.dp) .verticalScroll(rememberScrollState()) ) { // Avatar Box( modifier = Modifier .align(Alignment.CenterHorizontally) .padding(16.dp) ) { Box( modifier = Modifier .size(100.dp) .background(Color.Gray, CircleShape) .clickable { /* Change photo */ }, contentAlignment = Alignment.Center ) { Text(name.firstOrNull()?.uppercase() ?: "?", style = MaterialTheme.typography.headlineLarge, color = Color.White) }
Icon( Icons.Default.CameraAlt, contentDescription = "Change photo", modifier = Modifier .align(Alignment.BottomEnd) .background(MaterialTheme.colorScheme.primary, CircleShape) .padding(8.dp) .size(16.dp), tint = Color.White ) }
Spacer(Modifier.height(24.dp))
// Fields OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") }, modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(12.dp) )
Spacer(Modifier.height(16.dp))
OutlinedTextField( value = email, onValueChange = { email = it }, label = { Text("Email") }, modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(12.dp), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email) )
Spacer(Modifier.height(16.dp))
OutlinedTextField( value = phone, onValueChange = { phone = it }, label = { Text("Phone") }, modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(12.dp), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone) )
Spacer(Modifier.height(16.dp))
OutlinedTextField( value = bio, onValueChange = { bio = it }, label = { Text("Bio") }, modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(12.dp), minLines = 3, maxLines = 5 ) } }}📝 Tóm tắt
Phần tiêu đề “📝 Tóm tắt”| Component | Mục đích |
|---|---|
| ProfileHeader | Avatar, name, bio |
| ProfileStats | Số liệu thống kê |
| ProfileMenuItem | Menu items dạng list |
| EditProfile | Form chỉnh sửa |
Best Practices
Phần tiêu đề “Best Practices”- Avatar với fallback - Hiển thị chữ cái đầu nếu không có ảnh
- Grouped settings - Nhóm settings liên quan
- Clear CTAs - Edit, Logout buttons rõ ràng
- Version info - Hiện version ở cuối
Tiếp theo
Phần tiêu đề “Tiếp theo”Học về Animation trong Compose Multiplatform.