Pertemuan 11 - Market Siswa
Nama : Christoforus Indra Bagus Pratama
NRP : 5025231124
Mata Kuliah : Pemrograman Perangkat Bergerak (C)
Tanggal : 6 Mei 2026
Pertemuan : 11
Link : Github
Market Siswa — Aplikasi Marketplace untuk Pelajar
Market Siswa adalah aplikasi Android yang dibuat sebagai tugas mata kuliah Mobile Programming. Aplikasi ini dirancang sebagai platform jual-beli sederhana yang ditujukan untuk siswa di lingkungan sekolah. Dengan Market Siswa, siswa bisa menampilkan produk yang mereka jual, menambahkan produk baru, hingga mengelola daftar produk mereka — semua dari satu aplikasi yang ringan dan mudah digunakan.
Aplikasi ini dibangun menggunakan Jetpack Compose, yaitu toolkit modern dari Google untuk membangun tampilan aplikasi Android secara deklaratif. Selain itu, aplikasi ini menerapkan prinsip-prinsip Material Design 3 (Material You) untuk menghasilkan tampilan yang bersih, konsisten, dan nyaman digunakan.
Fitur-Fitur Aplikasi
1. Halaman Beranda (Home Screen)
Halaman utama aplikasi menampilkan seluruh daftar produk yang sudah ditambahkan. Di bagian atas terdapat
greeting card berwarna biru yang menyapa pengguna dan menampilkan jumlah produk yang sedang aktif.
Di bawahnya, produk-produk ditampilkan satu per satu dalam bentuk card yang memuat nama produk,
deskripsi singkat, kategori, dan harga. Tampilan ini menggunakan LazyColumn agar daftar produk
bisa di-scroll dengan lancar meski jumlahnya banyak.
Jika belum ada produk sama sekali, halaman beranda akan menampilkan tampilan empty state — sebuah card khusus yang memberitahu pengguna bahwa daftar produk masih kosong dan mengajak mereka untuk mulai menambahkan produk.
| Screenshot | Keterangan |
|---|---|
| Halaman Beranda dengan daftar produk dan greeting card di bagian atas. | |
| Tampilan empty state saat belum ada produk yang ditambahkan. |
2. Product Card — Tampilan Produk
Setiap produk ditampilkan dalam sebuah card tersendiri. Di sisi kiri card terdapat kotak berwarna biru yang menampilkan inisial (huruf pertama) dari nama produk sebagai avatar. Di sebelah kanannya terdapat nama produk, deskripsi singkat, label kategori, dan harga yang ditampilkan dengan warna biru terang agar mudah dilihat.
Di pojok kanan card terdapat ikon titik tiga (more options). Jika ditekan, akan muncul dialog konfirmasi sebelum produk benar-benar dihapus. Ini dilakukan untuk mencegah pengguna tidak sengaja menghapus produk.
| Screenshot | Keterangan |
|---|---|
| Tampilan card produk dengan informasi nama, deskripsi, kategori, dan harga. | |
| Dialog konfirmasi yang muncul sebelum produk dihapus. |
3. Tambah Produk (Add Product Screen)
Halaman ini adalah tempat pengguna mengisi detail produk yang ingin dijual. Terdapat tiga field yang wajib diisi, yaitu nama produk, harga, dan deskripsi. Selain itu, pengguna juga bisa memilih kategori produk menggunakan chip yang bisa diklik — pilihan kategori yang tersedia adalah Makanan, Fashion, Jasa, Elektronik, dan Umum.
Form ini dilengkapi dengan validasi secara langsung (real-time validation). Misalnya, jika nama produk kurang dari 3 karakter, akan muncul pesan error di bawah field tersebut. Untuk field harga, keyboard yang muncul otomatis adalah keyboard angka sehingga pengguna tidak bisa memasukkan huruf. Harga yang diketik juga langsung ditampilkan dalam format Rupiah di bawah field sebagai pratinjau.
Tombol simpan hanya bisa ditekan jika semua field sudah terisi dengan benar. Saat tombol ditekan, akan muncul animasi loading selama sekitar 1 detik sebagai simulasi proses penyimpanan, sebelum pengguna diarahkan kembali ke halaman beranda.
| Screenshot | Keterangan |
|---|---|
| Halaman form tambah produk dengan field nama, harga, kategori, dan deskripsi. | |
| Contoh tampilan validasi real-time saat input tidak memenuhi syarat. | |
| Animasi loading pada tombol simpan saat produk sedang diproses. |
4. Bottom Navigation — Navigasi Antar Halaman
Di bagian bawah layar terdapat navigation bar yang berisi dua menu utama: Beranda dan Profil. Ikon yang ditampilkan akan berubah antara versi solid (terpilih) dan versi outline (tidak terpilih) untuk memberikan tanda visual yang jelas kepada pengguna tentang halaman mana yang sedang aktif. Navigation bar ini selalu terlihat di semua halaman kecuali halaman tambah produk.
5. Floating Action Button (FAB) — Jual Produk
Di halaman beranda terdapat tombol besar berwarna biru di pojok kanan bawah bertuliskan "Jual Produk". Tombol ini adalah Extended FAB, artinya tombol ini menampilkan ikon sekaligus teks agar tujuannya lebih jelas. FAB hanya muncul di halaman beranda dan berfungsi untuk membawa pengguna ke halaman tambah produk.
6. Snackbar — Notifikasi Aksi
Setiap kali pengguna berhasil menambahkan atau menghapus produk, akan muncul snackbar di bagian bawah layar. Snackbar ini berwarna gelap dengan ikon centang hijau di sebelah kiri pesan. Tampilannya muncul sebentar lalu hilang sendiri, sehingga tidak mengganggu pengalaman pengguna.
7. Halaman Profil (Profile Screen)
Halaman profil menampilkan informasi pengguna berupa avatar inisial, nama, dan kelas. Di bawahnya terdapat dua kartu statistik yang menampilkan jumlah produk yang dimiliki dan jumlah produk terjual. Terdapat juga daftar menu pengaturan seperti Edit Profil, Notifikasi, Bantuan, dan Tentang Aplikasi — masing-masing dengan ikon dan panah ke kanan sebagai petunjuk bahwa menu tersebut bisa ditekan. Di paling bawah terdapat tombol Keluar berwarna merah.
Penerapan Materi Kuliah dalam Aplikasi
1. Material Design 3 (Material You)
Aplikasi ini menggunakan Material Theme 3 dari Jetpack Compose yang merupakan wujud nyata dari
paradigma Material You. Warna-warna yang digunakan bukan hanya dipilih secara sembarangan, melainkan diatur
dalam sebuah color scheme yang menyeluruh — mulai dari warna utama (biru Google), warna sekunder
(hijau Google), warna latar belakang, hingga warna untuk kondisi error (merah). Semua warna ini terdaftar
dalam function MarketSiswaTheme dan kemudian dipakai secara konsisten di seluruh aplikasi.
Hal ini sesuai dengan prinsip "Material is the metaphor" — tampilan card memiliki bentuk dan bayangan yang membuat elemen-elemen di layar terasa memiliki kedalaman seperti benda nyata.
2. Tiga Pilar Utama Material Design
| Pilar | Penerapan dalam Aplikasi |
|---|---|
| Material is the metaphor | Card produk menggunakan elevation dan sudut melengkung (RoundedCornerShape) yang memberikan kesan lapisan di atas latar belakang, seperti kertas di atas meja. |
| Bold, graphic, intentional | Hierarki visual diterapkan pada Product Card: nama produk dicetak tebal dan besar, deskripsi lebih kecil dan berwarna abu-abu, harga diberi warna biru terang untuk menarik perhatian. |
| Motion provides meaning | Perpindahan antar halaman menggunakan AnimatedContent dengan efek fade in/out selama 220ms. Tombol simpan juga menggunakan animasi AnimatedContent untuk transisi antara kondisi normal dan kondisi loading. |
3. Komponen UI Modern
| Komponen | Penerapan |
|---|---|
| Top App Bar / Toolbar | Setiap halaman memiliki TopAppBar masing-masing: HomeTopBar dengan logo dan nama aplikasi, AddProductTopBar dengan tombol back, dan ProfileTopBar dengan judul "Profil Saya". |
| Card | Digunakan di hampir setiap halaman — card produk, greeting card, empty state card, stat card di profil, dan info banner di form tambah produk. |
| FAB (Floating Action Button) | Tombol "Jual Produk" di halaman beranda menggunakan ExtendedFloatingActionButton yang merupakan varian FAB dengan teks dan ikon, untuk aksi utama yang paling penting. |
| Bottom Navigation | Komponen NavigationBar berisi dua item navigasi (Beranda dan Profil) dengan indikator visual berupa perubahan ikon solid/outline dan warna biru pada tab yang aktif. |
| Text Field | Form tambah produk menggunakan OutlinedTextField dengan label, placeholder, pesan error real-time, dan counter karakter yang ditampilkan di bawah setiap field. |
| Snackbar | Digunakan sebagai feedback setelah pengguna menambahkan atau menghapus produk. Snackbar tampil di bagian bawah layar dan hilang sendiri setelah beberapa detik. |
4. Responsivitas dan Pendekatan Modern dengan Jetpack Compose
Seluruh layout aplikasi dibangun dengan Jetpack Compose, yaitu pendekatan deklaratif yang disebutkan dalam materi kuliah. Dengan Compose, tampilan UI ditulis seperti mendeskripsikan "seperti apa tampilan yang diinginkan" alih-alih "langkah demi langkah cara membuatnya" seperti pada XML tradisional.
Untuk scrolling yang efisien, digunakan LazyColumn (scroll vertikal) dan LazyRow
(scroll horizontal untuk chip kategori). Komponen ini hanya me-render item yang terlihat di layar,
sehingga performa tetap ringan meski daftar produk panjang — prinsip yang sama dengan RecyclerView
pada pendekatan XML tradisional. Penggunaan unit dp dan sp juga memastikan
ukuran elemen menyesuaikan diri dengan berbagai ukuran layar perangkat.
5. Alur Pengembangan (5 Fase)
| Fase | Penerapan dalam Proyek |
|---|---|
| Analisis | Kebutuhan aplikasi dirumuskan: siswa membutuhkan platform untuk menampilkan, menambahkan, dan menghapus produk jualan mereka di lingkungan sekolah. |
| Wireframe | Struktur halaman dirancang dengan tiga screen utama: Home, Add Product, dan Profile, yang dihubungkan dengan navigasi bottom bar dan FAB. |
| Injeksi Material | Komponen Material Design 3 diterapkan ke dalam wireframe — card, FAB, text field, navigation bar, dan snackbar semuanya menggunakan komponen dari library Material3. |
| Implementasi | Kode ditulis di Android Studio menggunakan Jetpack Compose dengan bahasa pemrograman Kotlin. |
| Uji UX | Validasi form, dialog konfirmasi hapus, animasi loading, dan snackbar ditambahkan untuk memastikan pengalaman pengguna terasa lancar dan tidak membingungkan. |
Penjelasan Kode MainActivity.kt
Seluruh logika dan tampilan aplikasi Market Siswa ditulis dalam satu file bernama MainActivity.kt.
File ini dibagi menjadi 13 bagian utama. Berikut penjelasan masing-masing bagian beserta potongan kodenya.
Bagian 1 — Import Library
Di baris paling atas, terdapat daftar import yang berisi semua library yang dibutuhkan aplikasi. Library ini disediakan oleh Google dan Jetpack Compose, sehingga kita tidak perlu menulis komponen dari nol. Beberapa yang penting antara lain:
androidx.compose.material3.*— semua komponen Material Design 3 seperti Card, Button, TextField, dll.androidx.compose.animation.*— untuk efek animasi transisi antar halaman dan animasi tombol.androidx.compose.foundation.lazy.*— untuk membuat daftar yang bisa di-scroll (LazyColumndanLazyRow).kotlinx.coroutines.*— untuk menjalankan proses yang membutuhkan jeda waktu (seperti simulasi loading) tanpa membekukan tampilan.java.text.NumberFormat— untuk memformat angka harga menjadi format Rupiah yang benar.
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.*
import androidx.compose.animation.core.tween
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.material3.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.text.NumberFormat
import java.util.Locale
Bagian 2 — Data Model (data class Product)
Bagian ini mendefinisikan struktur data untuk sebuah produk. Dalam Kotlin, data class digunakan
untuk menyimpan kumpulan data yang berkaitan. Setiap produk memiliki lima properti: id unik yang dibuat
otomatis dari waktu saat ini, nama, harga, deskripsi, dan kategori (dengan nilai bawaan "Umum" jika tidak diisi).
data class Product(
val id: Long = System.currentTimeMillis(),
val name: String,
val price: String,
val description: String,
val category: String = "Umum"
)
Properti id menggunakan System.currentTimeMillis() yang akan menghasilkan angka
berbeda setiap kali produk dibuat, sehingga setiap produk dijamin memiliki id yang unik.
Bagian 3 — Main Activity
MainActivity adalah titik masuk (entry point) dari aplikasi Android. Di sinilah
aplikasi pertama kali dijalankan. Function onCreate dipanggil otomatis oleh sistem saat
aplikasi dibuka. Di dalamnya, fungsi setContent digunakan untuk menghubungkan Jetpack Compose
ke layar perangkat. Seluruh tampilan dibungkus dalam MarketSiswaTheme agar tema warna
diterapkan secara menyeluruh, lalu MainScreen dipanggil sebagai layar pertama yang ditampilkan.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MarketSiswaTheme {
MainScreen()
}
}
}
}
Bagian 4 — Theme (MarketSiswaTheme)
Bagian ini mendefinisikan tema warna seluruh aplikasi. Dengan mendaftarkan warna di sini satu kali, semua komponen Material Design yang digunakan di seluruh aplikasi akan otomatis mengikuti warna yang sama tanpa perlu diatur satu per satu. Warna yang dipilih mengacu pada palet warna Google (biru, hijau, kuning) untuk kesan yang bersih dan profesional.
@Composable
fun MarketSiswaTheme(content: @Composable () -> Unit) {
MaterialTheme(
colorScheme = lightColorScheme(
primary = Color(0xFF1A73E8), // Biru Google
secondary = Color(0xFF34A853), // Hijau Google
tertiary = Color(0xFFFBBC04), // Kuning Google
error = Color(0xFFD93025), // Merah untuk error
background = Color(0xFFF8F9FA),
// ... warna lainnya
),
content = content
)
}
Bagian 5 — Main Screen (Scaffold)
MainScreen adalah "kerangka" utama aplikasi. Di sinilah semua bagian besar disatukan.
Bagian ini menggunakan komponen Scaffold dari Material Design yang secara otomatis mengatur
posisi Top App Bar, Bottom Navigation, FAB, dan Snackbar di tempat yang benar tanpa kita perlu mengatur
posisinya secara manual.
Di bagian atas function ini, beberapa variabel state penting didefinisikan:
currentScreen— menyimpan nama halaman yang sedang aktif ("home", "add", atau "profile").productList— daftar produk yang bisa berubah-ubah (mutableStateListOf), sehingga UI otomatis diperbarui setiap ada produk yang ditambah atau dihapus.snackbarHost— pengontrol untuk menampilkan snackbar.scope— digunakan untuk menjalankan proses asinkron seperti menampilkan snackbar atau simulasi loading.
@Composable
fun MainScreen() {
var currentScreen by remember { mutableStateOf("home") }
val productList = remember { mutableStateListOf<Product>() }
val snackbarHost = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
// Data awal saat pertama kali aplikasi dibuka
LaunchedEffect(Unit) {
if (productList.isEmpty()) {
productList.add(Product(name = "Brownies Lumer", price = "15000", ...))
productList.add(Product(name = "Kaos Custom", price = "85000", ...))
productList.add(Product(name = "Jasa Desain Logo", price = "50000", ...))
}
}
Scaffold(
snackbarHost = { ... },
topBar = { ... },
bottomBar = { ... },
floatingActionButton = { ... }
) { innerPadding ->
// Konten utama di sini
}
}
LaunchedEffect(Unit) digunakan untuk menjalankan kode satu kali saat screen pertama kali
ditampilkan — dalam hal ini untuk mengisi data produk awal agar aplikasi tidak terlihat kosong saat
pertama dibuka.
Bagian 6 — Animated Screen Transition
Perpindahan antar halaman (beranda, tambah produk, profil) tidak langsung berganti secara tiba-tiba,
melainkan menggunakan animasi fade in dan fade out selama 220 milidetik. Ini dilakukan
dengan komponen AnimatedContent. Ketika variabel currentScreen berubah,
Compose secara otomatis menjalankan animasi transisi dan menampilkan halaman yang sesuai.
AnimatedContent(
targetState = currentScreen,
transitionSpec = {
fadeIn(tween(220)) togetherWith fadeOut(tween(220))
},
label = "screen_transition"
) { screen ->
when (screen) {
"home" -> HomeScreen(...)
"add" -> AddProductScreen(...)
"profile" -> ProfileScreen(...)
}
}
Bagian 7 — Top App Bars
Terdapat tiga versi Top App Bar, masing-masing untuk satu halaman. Pemisahan ini dilakukan agar setiap halaman bisa menampilkan judul dan tombol navigasi yang sesuai konteksnya.
- HomeTopBar — menampilkan logo berbentuk kotak biru dengan huruf "M" dan teks "MarketSiswa".
- AddProductTopBar — menampilkan judul "Tambah Produk" dan tombol panah kembali di sisi kiri.
- ProfileTopBar — menampilkan judul "Profil Saya".
@Composable
fun AddProductTopBar(onBack: () -> Unit) {
TopAppBar(
title = { Text("Tambah Produk", fontWeight = FontWeight.SemiBold) },
navigationIcon = {
IconButton(onClick = onBack) {
Icon(Icons.Default.ArrowBack, contentDescription = "Kembali")
}
},
colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.White)
)
}
Bagian 8 — Home Screen
Halaman beranda menggunakan LazyColumn untuk menampilkan daftar produk yang bisa di-scroll.
Item pertama yang ditampilkan adalah GreetingCard, kemudian header "Semua Produk" beserta
jumlah item, lalu daftar produk di bawahnya. Jika tidak ada produk, EmptyStateCard
ditampilkan sebagai gantinya.
Penggunaan items(products, key = { it.id }) sangat penting — properti key
membantu Compose mengetahui produk mana yang berubah sehingga animasi penghapusan berjalan dengan benar
dan performa tetap optimal.
@Composable
fun HomeScreen(products: List<Product>, onDeleteProduct: (Product) -> Unit) {
LazyColumn(modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(...)) {
item { GreetingCard(productCount = products.size) }
if (products.isEmpty()) {
item { EmptyStateCard() }
} else {
items(products, key = { it.id }) { product ->
ProductCard(product = product, onDelete = { onDeleteProduct(product) })
}
}
}
}
Bagian 9 — Greeting Card
GreetingCard adalah card besar berwarna biru di bagian atas halaman beranda. Card ini
menampilkan sapaan kepada pengguna dan pesan yang berubah secara dinamis: jika belum ada produk,
pesannya mengajak untuk mulai berjualan; jika sudah ada produk, pesannya menampilkan jumlah produk aktif.
Di sisi kanan card terdapat ikon toko yang dibungkus dalam lingkaran transparan sebagai elemen dekoratif.
@Composable
fun GreetingCard(productCount: Int) {
Card(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(20.dp),
colors = CardDefaults.cardColors(containerColor = Color(0xFF1A73E8))
) {
Row(modifier = Modifier.padding(20.dp)) {
Column {
Text("Halo, Siswa! 👋", color = Color.White, fontWeight = FontWeight.ExtraBold)
Text(
if (productCount == 0) "Belum ada produk. Yuk mulai jual!"
else "Kamu punya $productCount produk aktif",
color = Color(0xFFBDD7F8)
)
}
// Ikon toko di sisi kanan
Icon(Icons.Default.Storefront, tint = Color.White)
}
}
}
Bagian 10 — Product Card
ProductCard adalah tampilan satu produk dalam bentuk card. Di dalamnya terdapat logika
untuk menampilkan dialog konfirmasi sebelum produk dihapus. Variabel showDeleteDialog
bertipe boolean yang mengontrol apakah dialog tersebut ditampilkan atau tidak.
Hierarki visual diterapkan di sini sesuai prinsip Material Design — nama produk ditampilkan paling besar
dan tebal, deskripsi lebih kecil dan berwarna abu-abu, sementara harga diberi warna biru terang agar
langsung menarik perhatian. Teks yang terlalu panjang dipotong secara otomatis dengan TextOverflow.Ellipsis.
@Composable
fun ProductCard(product: Product, onDelete: () -> Unit) {
var showDeleteDialog by remember { mutableStateOf(false) }
// Dialog konfirmasi sebelum hapus
if (showDeleteDialog) {
AlertDialog(
title = { Text("Hapus Produk?") },
text = { Text("\"${product.name}\" akan dihapus.") },
confirmButton = {
TextButton(onClick = { showDeleteDialog = false; onDelete() }) {
Text("Hapus", color = Color(0xFFD93025))
}
},
dismissButton = {
TextButton(onClick = { showDeleteDialog = false }) { Text("Batal") }
},
onDismissRequest = { showDeleteDialog = false }
)
}
Card(modifier = Modifier.fillMaxWidth()) {
Row(modifier = Modifier.padding(16.dp)) {
// Inisial produk sebagai avatar
Box(...) {
Text(product.name.first().uppercaseChar().toString())
}
Column(modifier = Modifier.weight(1f)) {
Text(product.name, fontWeight = FontWeight.SemiBold) // Level 1
Text(product.description, color = Color(0xFF5F6368)) // Level 2
Text(formatPrice(product.price), color = Color(0xFF1A73E8)) // Harga
}
IconButton(onClick = { showDeleteDialog = true }) {
Icon(Icons.Default.MoreVert, contentDescription = "Opsi")
}
}
}
}
Bagian 11 — Add Product Screen
Ini adalah halaman form untuk menambahkan produk baru. Terdapat beberapa state yang dikelola secara lokal di dalam function ini: nilai dari setiap field (nama, harga, deskripsi, kategori), status loading, dan status validasi.
Validasi dilakukan secara langsung saat pengguna mengetik, bukan hanya saat tombol simpan ditekan.
Ini disebut real-time validation. Contohnya, variabel nameError akan bernilai
true jika nama sudah diketik tapi panjangnya masih kurang dari 3 karakter.
Variabel isFormValid akan bernilai true hanya jika semua syarat terpenuhi,
dan tombol simpan hanya aktif (enabled) ketika isFormValid bernilai true.
Untuk pemilihan kategori, digunakan FilterChip yang ditampilkan dalam LazyRow
(baris yang bisa di-scroll ke samping). Ini lebih cepat digunakan dibandingkan dropdown menu karena
semua pilihan langsung terlihat.
@Composable
fun AddProductScreen(onProductAdded: (Product) -> Unit) {
var name by remember { mutableStateOf("") }
var price by remember { mutableStateOf("") }
var desc by remember { mutableStateOf("") }
var selectedCategory by remember { mutableStateOf("Umum") }
var isLoading by remember { mutableStateOf(false) }
// Validasi real-time
val nameError = name.isNotBlank() && name.length < 3
val priceError = price.isNotBlank() && price.toLongOrNull() == null
val isFormValid = name.isNotBlank() && price.isNotBlank() && desc.isNotBlank()
&& !nameError && !priceError
// Field Harga — hanya menerima angka, keyboard otomatis tipe angka
OutlinedTextField(
value = price,
onValueChange = { price = it.filter { c -> c.isDigit() } },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
isError = priceError,
supportingText = {
when {
priceError -> Text("Masukkan angka yang valid", color = Color(0xFFD93025))
price.isNotBlank() -> Text(formatPrice(price), color = Color(0xFF1A73E8))
}
}
)
// Pemilihan kategori dengan chip
LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
items(categories) { category ->
FilterChip(
selected = selectedCategory == category,
onClick = { selectedCategory = category },
label = { Text(category) }
)
}
}
// Tombol simpan dengan animasi loading
Button(
onClick = {
isLoading = true
scope.launch {
delay(1000) // Simulasi proses simpan
onProductAdded(Product(name = name.trim(), price = price, ...))
isLoading = false
}
},
enabled = isFormValid && !isLoading
) {
AnimatedContent(targetState = isLoading) { loading ->
if (loading) {
CircularProgressIndicator(modifier = Modifier.size(20.dp))
Text("Menyimpan...")
} else {
Icon(Icons.Default.CloudUpload, contentDescription = null)
Text("Simpan Produk")
}
}
}
}
Bagian 12 — Profile Screen
Halaman profil menampilkan informasi pengguna dalam beberapa bagian yang tersusun dari atas ke bawah
menggunakan LazyColumn.
- Card avatar — menampilkan inisial "JS", nama "John Siswa", dan label kelas "XII RPL 1" di tengah.
- Stat cards — dua card kecil berjajar secara horizontal untuk menampilkan jumlah produk dan produk terjual.
- Menu card — daftar menu pengaturan (Edit Profil, Notifikasi, Bantuan, Tentang Aplikasi) yang masing-masing memiliki ikon, judul, subjudul, dan panah ke kanan.
- Tombol Keluar — tombol berwarna merah dengan border untuk aksi logout.
@Composable
fun ProfileScreen(productCount: Int) {
LazyColumn(modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(16.dp)) {
// Avatar dan info pengguna
item {
Card {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Box(modifier = Modifier.size(88.dp).clip(CircleShape).background(Color(0xFFD2E3FC))) {
Text("JS", fontSize = 32.sp, fontWeight = FontWeight.ExtraBold)
}
Text("John Siswa", fontSize = 22.sp, fontWeight = FontWeight.Bold)
Text("XII RPL 1")
}
}
}
// Statistik
item {
Row {
StatCard(label = "Produk", value = "$productCount", icon = Icons.Default.Inventory2)
StatCard(label = "Terjual", value = "0", icon = Icons.Default.ShoppingCart)
}
}
// Menu pengaturan
item {
Card {
ProfileMenuItem(Icons.Default.Edit, "Edit Profil", "Ubah nama dan foto profil")
ProfileMenuItem(Icons.Default.Notifications, "Notifikasi", "Atur preferensi notifikasi")
ProfileMenuItem(Icons.Default.Help, "Bantuan", "Pusat bantuan & FAQ")
ProfileMenuItem(Icons.Default.Info, "Tentang Aplikasi", "MarketSiswa v1.0.0")
}
}
// Tombol keluar
item {
OutlinedButton(onClick = { /* TODO */ }) {
Icon(Icons.Default.Logout, contentDescription = null)
Text("Keluar")
}
}
}
}
Bagian 13 — Helper Composables
Agar kode tidak terlalu panjang dan mudah dibaca ulang, beberapa komponen kecil yang dipakai berulang dipisahkan menjadi function sendiri. Berikut daftar helper composables yang ada:
| Function | Fungsi |
|---|---|
FieldLabel() |
Menampilkan label di atas field form. Jika field wajib diisi, tanda bintang merah (*) otomatis ditambahkan. |
StatCard() |
Card kecil untuk menampilkan satu data statistik (ikon, angka, label) di halaman profil. |
ProfileMenuItem() |
Satu baris menu di halaman profil dengan ikon, judul, subjudul, dan panah ke kanan. |
MenuDivider() |
Garis pemisah tipis antar item menu di halaman profil. |
navBarItemColors() |
Mendefinisikan warna ikon dan teks navigation bar untuk kondisi dipilih dan tidak dipilih, agar tidak perlu ditulis ulang dua kali. |
fieldColors() |
Mendefinisikan warna border, label, dan cursor untuk semua OutlinedTextField di form tambah produk secara konsisten. |
@Composable
fun FieldLabel(text: String, required: Boolean) {
Row {
Text(text, fontWeight = FontWeight.SemiBold, fontSize = 14.sp)
if (required) Text(" *", color = Color(0xFFD93025), fontWeight = FontWeight.Bold)
}
}
@Composable
fun navBarItemColors() = NavigationBarItemDefaults.colors(
selectedIconColor = Color(0xFF1A73E8),
selectedTextColor = Color(0xFF1A73E8),
indicatorColor = Color(0xFFD2E3FC),
unselectedIconColor = Color(0xFF5F6368),
unselectedTextColor = Color(0xFF5F6368)
)
Bagian 14 — Utility (formatPrice)
Function ini bertugas mengubah angka harga yang disimpan sebagai teks (misalnya "15000") menjadi
format mata uang Rupiah yang benar (misalnya "Rp 15.000"). Pemformatan menggunakan NumberFormat
dengan locale Indonesia agar pemisah ribuan menggunakan titik sesuai kebiasaan penulisan di Indonesia.
Jika teks yang dimasukkan bukan angka yang valid, function ini akan mengembalikan teks apa adanya
dengan awalan "Rp" sebagai fallback.
fun formatPrice(price: String): String {
val number = price.toLongOrNull() ?: return "Rp $price"
val format = NumberFormat.getNumberInstance(Locale("id", "ID"))
return "Rp ${format.format(number)}"
}
Function ini dipanggil di dua tempat: di dalam ProductCard untuk menampilkan harga yang
sudah diformat, dan di dalam form tambah produk sebagai pratinjau harga secara real-time saat pengguna
mengetik angka harga.
Penutup
Market Siswa adalah implementasi dari seluruh materi yang dipelajari dalam mata kuliah Mobile Programming di pertemuan 11. Mulai dari penerapan Material Design 3, penggunaan komponen UI modern, animasi yang bermakna, hingga validasi form yang memperhatikan pengalaman pengguna — semua diterapkan dalam satu aplikasi yang utuh dan fungsional. Aplikasi ini dibangun sepenuhnya menggunakan Jetpack Compose dan Kotlin di Android Studio, tanpa satu baris pun kode XML untuk tampilan.











Komentar
Posting Komentar