TUGAS 2 PPB F : Aplikasi Profil Diri dengan Modal Sambutan (Smartphone, Dekstop, Tablet, Smartwatch, SmartTV)
TUGAS 2
Membuat Aplikasi Profil Diri + Modal Sambutan dengan Jetpack Compose
PPB (F)
Dalam tugas pertama, saya telah menggunakan Jetpack Compose untuk membuat aplikasi My Profile. Sebagai kelanjutan dari tugas pertama kemarin, saya hanya akan memberikan sedikit tambahan fitur pada aplikasi yang sudah ada. Kali ini, fokus kita adalah menambahkan sebuah fitur sederhana namun berkesan yaitu sebuah modal sambutan yang menyapa pengguna saat mereka pertama kali membuka aplikasi. Tujuan dari penambahan ini tidak hanya untuk meningkatkan interaksi pengguna dengan aplikasi tetapi juga untuk memperkenalkan cara baru dalam memanfaatkan Jetpack Compose untuk menciptakan pengalaman pengguna yang menarik dan hangat.
Langkah-Langkah Seperti Tugas Pertama:
Persiapan:
Sebelum memulai, pastikan Anda telah menginstal Android Studio dan setup lingkungan pengembangan Android Anda dengan benar. Juga, pastikan bahwa Anda menggunakan versi Android Studio dan Gradle yang mendukung Jetpack Compose.
Langkah 1: Setup Proyek
Buat proyek baru di Android Studio dan pilih template "Empty Compose Activity". Beri nama aplikasi Anda dan pastikan Anda memilih bahasa pemrograman Kotlin.
Langkah 2: Definisi Data
Pertama, kita perlu mendefinisikan data yang akan ditampilkan. Untuk aplikasi profil, kita mungkin ingin menampilkan nama, judul pekerjaan, lokasi, email, nomor telepon, tentang saya, dan daftar keterampilan. Kita bisa mendefinisikan sebuah data class `ProfileData` dan `Project` untuk menyimpan informasi ini:
data class ProfileData( | |
val name: String, | |
val jobTitle: String, | |
val location: String, | |
val email: String, | |
val phone: String, | |
val about: String, | |
val skills: List<String>, | |
val projects: List<Project> | |
) | |
data class Project( | |
val title: String, | |
val description: String, | |
val imageUrl: Int | |
) |
Langkah 3: Membangun UI
Kita akan menggunakan beberapa komponen Jetpack Compose untuk membangun UI aplikasi profil kita:
1. TopProfileSection:
Bagian ini menampilkan foto, nama, judul pekerjaan, dan lokasi. Kita menggunakan `Card` untuk memberikan tampilan yang menarik.
@Composable | |
fun TopProfileSection(profileData: ProfileData) { | |
Card( | |
modifier = Modifier | |
.fillMaxWidth() | |
.padding(12.dp), | |
shape = RoundedCornerShape(10.dp), | |
elevation = 4.dp | |
) { | |
Box { | |
// Membuat background gradient | |
Box( | |
modifier = Modifier | |
.matchParentSize() | |
.background( | |
brush = Brush.verticalGradient( | |
colors = listOf( | |
Color(0xFFE1BEE7), // Warna ungu di atas | |
Color.White // Transparan di bawah | |
), | |
startY = 10f // Atur nilai ini untuk menyesuaikan di mana warna mulai berubah menjadi transparan | |
) | |
) | |
) | |
Column( | |
modifier = Modifier | |
.fillMaxWidth() | |
.padding(16.dp), | |
horizontalAlignment = Alignment.CenterHorizontally | |
) { | |
Image( | |
painter = painterResource(id = R.drawable.profile_picture), | |
contentDescription = "Profile picture", | |
modifier = Modifier | |
.size(100.dp) | |
.clip(CircleShape) | |
.border(1.dp, Color(0xFFE1BEE7), CircleShape) | |
.align(Alignment.CenterHorizontally) | |
) | |
Spacer(modifier = Modifier.height(8.dp)) | |
Text( | |
text = profileData.name, | |
fontSize = 12.sp, | |
fontWeight = FontWeight.Bold, | |
color = MaterialTheme.colors.primary, | |
modifier = Modifier.wrapContentSize() | |
) | |
Text(text = profileData.jobTitle, style = MaterialTheme.typography.subtitle1) | |
Text(text = profileData.location, style = MaterialTheme.typography.caption) | |
// Menambahkan informasi kontak | |
Spacer(modifier = Modifier.height(8.dp)) | |
Text(text = "Email: ${profileData.email}", style = MaterialTheme.typography.body1) | |
Text(text = "Phone: ${profileData.phone}", style = MaterialTheme.typography.body1) | |
} | |
} | |
} | |
} |
2. AboutSection:
Menampilkan informasi "tentang saya".
@Composable | |
fun AboutSection(profileData: ProfileData) { | |
Card( | |
modifier = Modifier | |
.fillMaxWidth() | |
.padding(horizontal = 12.dp), | |
shape = RoundedCornerShape(10.dp), | |
elevation = 4.dp | |
) { | |
Column(modifier = Modifier.padding(16.dp)) { | |
Text("About Me", fontSize = 20.sp, fontWeight = FontWeight.Bold) | |
Spacer(modifier = Modifier.height(4.dp)) | |
Text(text = profileData.about, style = MaterialTheme.typography.body1) | |
} | |
} | |
} |
3. SkillsSection:
Menampilkan daftar keterampilan dalam grid dua kolom.
@Composable | |
fun SkillsSection(skills: List<String>) { | |
Card( | |
modifier = Modifier | |
.fillMaxWidth() | |
.padding(12.dp), | |
shape = RoundedCornerShape(10.dp), | |
elevation = 4.dp | |
) { | |
Column(modifier = Modifier.padding(16.dp)) { | |
Text("Skills", fontSize = 20.sp, fontWeight = FontWeight.Bold) | |
Spacer(modifier = Modifier.height(4.dp)) | |
// Assuming each row can fit 2 items for simplicity. Adjust based on your UI needs. | |
val numRows = (skills.size + 1) / 2 // Calculate the number of rows needed | |
Column { | |
for (i in 0 until numRows) { | |
Row( | |
modifier = Modifier.fillMaxWidth(), | |
horizontalArrangement = Arrangement.Start | |
) { | |
val startIndex = i * 2 | |
for (j in startIndex until startIndex + 2) { | |
if (j < skills.size) { | |
ChipView(skill = skills[j]) | |
Spacer(modifier = Modifier.width(8.dp)) // Space between chips | |
} | |
} | |
} | |
Spacer(modifier = Modifier.height(8.dp)) // Space between rows | |
} | |
} | |
} | |
} | |
} |
4. ProjectsSection:
Menampilkan proyek-proyek yang telah dikerjakan.
@Composable | |
fun ProjectsSection(projects: List<Project>) { | |
Column(modifier = Modifier.fillMaxWidth().padding(12.dp)) { | |
Text( | |
"Projects", | |
fontSize = 20.sp, | |
fontWeight = FontWeight.Bold, | |
modifier = Modifier.padding(start = 4.dp, bottom = 8.dp) | |
) | |
// Process projects in pairs | |
projects.chunked(2).forEachIndexed { index, rowProjects -> | |
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { | |
rowProjects.forEachIndexed { rowIndex, project -> | |
// Adjust modifier to add end padding to all cards except the last one in the row | |
val isLastInRow = rowIndex == rowProjects.size - 1 | |
val cardModifier = Modifier | |
.weight(1f) | |
.let { if (isLastInRow && rowProjects.size > 1) it.padding(end = 8.dp) else it } | |
ProjectCard(project = project, modifier = cardModifier) | |
} | |
// Fill the remaining space if there's an odd number of projects to keep the layout consistent | |
if (rowProjects.size == 1 && index != projects.chunked(2).lastIndex) { | |
Spacer(modifier = Modifier.weight(1f)) | |
} | |
} | |
// Add vertical spacing between rows if not the last row | |
if (index != projects.chunked(2).lastIndex) { | |
Spacer(modifier = Modifier.height(16.dp)) | |
} | |
} | |
} | |
} |
Langkah 4: Menampilkan Data
Setelah mendefinisikan komponen UI, kita perlu menampilkan data konkret:
val dummyProfile = ProfileData( | |
name = "Rahmat Faris Akbar", | |
jobTitle = "Frontend Developer", | |
location = "Surabaya, Indonesia", | |
email = "email@mail.com", | |
phone = "+1234567890", | |
about = "Passionate Frontend developer with experience in building, integrating, testing, and supporting web applications. Skilled in modern JavaScript frameworks and libraries, responsive design, and building user-centric interfaces.", | |
skills = listOf("CSS", "HTML", "JavaScript", "React", "Responsive Design", "NextJS", "C/C++", "Mathematics"), | |
projects = listOf( | |
Project( | |
title = "Project Aplikasi ToDo List App", | |
description = "Sebuah website yang memudahkan kita mencatat hal yang perlu dilakukan.", | |
imageUrl = R.drawable.project_1 | |
), | |
Project( | |
title = "Project Website Landing Adapter HIX", | |
description = "Sebuah website untuk branding dan berlangganan Adapter App.", | |
imageUrl = R.drawable.project_2 | |
), | |
Project( | |
title = "Project Aplikasi Adapter Satu Sehat", | |
description = "Sebuah aplikasi adapter untuk menjembatani proses ETL data RS ke Satu Sehat.", | |
imageUrl = R.drawable.project_3 | |
), | |
Project( | |
title = "Project Parking Lot Detector & Counter", | |
description = "Sebuah aplikasi pendeteksi dan penghitung area parkir yang masih kosong.", | |
imageUrl = R.drawable.project_4 | |
), | |
) | |
) |
Langkah 5: Penyusunan Layout Utama
Di `MainActivity`, gunakan `setContent` untuk menyusun komponen-komponen yang telah kita definisikan:
class MainActivity : ComponentActivity() { | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContent { | |
MaterialTheme { | |
Surface(color = MaterialTheme.colors.background) { | |
ProfileScreen(dummyProfile) | |
} | |
} | |
} | |
} | |
} |
Langkah Baru yang Perlu Dilakukan:
Langkah 1: Menampilkan Dialog pada ProfileScreen
Pertama-tama, kita memodifikasi ProfileScreen untuk memasukkan mekanisme dialog. Di sini, kita menggunakan state showDialog dengan nilai awal true. Hal ini menandakan bahwa dialog sambutan akan ditampilkan pada saat aplikasi pertama kali dijalankan. LazyColumn digunakan untuk menampilkan isi utama aplikasi, termasuk bagian profil, keterampilan, dan proyek.
@Composable | |
fun ProfileScreen(profileData: ProfileData) { | |
var showDialog by remember { mutableStateOf(true) } // Dialog ditampilkan saat pertama kali | |
LazyColumn(modifier = Modifier.fillMaxSize()) { | |
item { TopProfileSection(profileData) } | |
// Komponen lainnya... | |
} | |
// Kondisi untuk menampilkan dialog | |
if (showDialog) { | |
HelloWorldDialog { showDialog = false } | |
} | |
} |
Mekanisme ini memastikan bahwa dialog hanya akan muncul satu kali, yaitu ketika aplikasi dibuka untuk pertama kalinya. Setelah dialog ditutup, showDialog akan diatur menjadi false, sehingga dialog tidak akan muncul lagi sampai aplikasi dijalankan ulang.
Langkah 2: Desain dan Fungsionalitas HelloWorldDialog
Dialog disusun menggunakan AlertDialog dari Jetpack Compose, yang di dalamnya terdapat animasi, judul, teks, dan tombol konfirmasi. AnimatedVisibility digunakan untuk menambahkan animasi fade-in dan fade-out pada dialog, memberikan transisi yang halus saat dialog muncul dan ditutup.
@Composable | |
fun HelloWorldDialog(onDismissRequest: () -> Unit) { | |
AnimatedVisibility( | |
visible = true, | |
enter = fadeIn(initialAlpha = 0.4f), | |
exit = fadeOut() | |
) { | |
AlertDialog( | |
onDismissRequest = onDismissRequest, | |
title = { | |
Text( | |
text = "Welcome", | |
color = MaterialTheme.colors.primary, | |
fontWeight = FontWeight.Bold, | |
fontSize = 20.sp | |
) | |
}, | |
text = { | |
Text( | |
"Hello World! This is My Profile Apps", | |
style = MaterialTheme.typography.body1 | |
) | |
}, | |
confirmButton = { | |
Button( | |
onClick = onDismissRequest, | |
colors = ButtonDefaults.buttonColors(backgroundColor = MaterialTheme.colors.primary) | |
) { | |
Text( | |
"Read My Profile ⟫", | |
color = Color.White | |
) | |
} | |
}, | |
backgroundColor = Color.White, | |
contentColor = Color.Black, | |
) | |
} | |
} |
Di dalam dialog, onDismissRequest didefinisikan sebagai lambda function yang dipanggil ketika tombol konfirmasi diklik. Fungsi ini mengubah state showDialog menjadi false, yang mengakibatkan dialog ditutup. Judul "Welcome" disertai dengan pesan "Hello World! This is My Profile Apps" yang menampilkan pesan sambutan hangat kepada pengguna. Tombol "Read My Profile ⟫" berfungsi sebagai ajakan untuk mengeksplorasi lebih lanjut isi dari My Profile Apps ini.
Tampilan Aplikasi:
![]() |
Smartphone |
Comments
Post a Comment