Tugas 3 PPB - Membuat Aplikasi "Birthday Greetings"

Tugas 3 - Aplikasi Birthday Greetings Menggunakan Jetpack Compose

Pada tugas ini saya membuat aplikasi Android sederhana bertema birthday greetings menggunakan Jetpack Compose. Aplikasi ini dirancang seperti kartu ucapan ulang tahun digital dengan dua layar utama, yaitu layar pembuka dan layar ucapan. Fokus utama dari project ini bukan hanya pada tampilan, tetapi juga pada bagaimana setiap blok kode bekerja untuk mengatur state, transisi antarlayar, popup, dan komponen visual dekoratif.

1. Informasi Pribadi

Nama Dapunta Adyapaksi Ratyanasja
NRP 5025231187
Kelas Pemrograman Perangkat Bergerak (C)
Judul Tugas Membuat Aplikasi Birthday Greetings Menggunakan Jetpack Compose
Bahasa Kotlin
Toolkit UI Jetpack Compose

2. Gambaran Umum Aplikasi

Aplikasi ini dibuat dengan alur interaksi sederhana. Saat aplikasi pertama kali dibuka, pengguna akan melihat pesan pembuka yang ditujukan kepada penerima ucapan, dalam hal ini Sam. Setelah tombol Open the Gift ditekan, aplikasi menampilkan popup singkat sebagai efek pembukaan hadiah. Setelah itu, tampilan berpindah ke layar ucapan ulang tahun yang berisi pesan utama dan tombol untuk mengulang kembali.

Nama pengirim dan penerima pada aplikasi ini dibuat statis dan hardcode, yaitu from = "Dapunta" dan to = "Sam"

3. Fitur dan Fungsionalitas Aplikasi

Layar pembuka Menampilkan pesan awal yang mengajak penerima untuk membuka hadiah.
Tombol interaktif Tombol Open the Gift memicu popup dan transisi ke layar berikutnya.
Popup hadiah Muncul sementara sebagai efek bahwa hadiah sedang dibuka.
Transisi layar Perpindahan dari layar pembuka ke layar ucapan dilakukan dengan animasi AnimatedContent.
Layar ucapan Menampilkan teks ucapan ulang tahun dengan nama penerima dan pengirim.
Tombol ulang Tombol See Again mengembalikan aplikasi ke layar awal.
Dekorasi visual Menggunakan gradient background, card, emoji, dan kombinasi warna cerah agar tampilan lebih menarik.

4. Kode Program Utama

File utama MainActivity.kt yang berisi seluruh logika aplikasi. Pada project ini, seluruh UI ditulis langsung menggunakan Compose tanpa file XML.

package com.example.birthday

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MaterialTheme {
                BirthdayApp()
            }
        }
    }
}

@Composable
fun BirthdayApp() {
    val from = "Dapunta"
    val to = "Sam"

    var showGreetingScreen by remember { mutableStateOf(false) }
    var showPopup by remember { mutableStateOf(false) }

    if (showPopup) {
        AlertDialog(
            onDismissRequest = { },
            confirmButton = {},
            title = {
                Text(
                    text = "🎁 Opening your gift...",
                    fontWeight = FontWeight.Bold
                )
            },
            text = {
                Text("A special surprise is on the way for $to ✨")
            },
            containerColor = Color(0xFFFFF4FB),
            shape = RoundedCornerShape(20.dp)
        )

        LaunchedEffect(Unit) {
            delay(1200)
            showPopup = false
            showGreetingScreen = true
        }
    }

    AnimatedContent(
        targetState = showGreetingScreen,
        transitionSpec = { fadeIn() togetherWith fadeOut() },
        label = "birthday_screen_transition"
    ) { isGreetingScreen ->
        if (isGreetingScreen) {
            GreetingScreen(
                from = from,
                to = to,
                onReplay = { showGreetingScreen = false }
            )
        } else {
            IntroScreen(
                to = to,
                onOpenGift = { showPopup = true }
            )
        }
    }
}

@Composable
fun IntroScreen(
    to: String,
    onOpenGift: () -> Unit
) {
    val bgBrush = Brush.verticalGradient(
        colors = listOf(
            Color(0xFFFFD6E8),
            Color(0xFFFFF1C9),
            Color(0xFFD6F4FF)
        )
    )

    Surface(
        modifier = Modifier.fillMaxSize(),
        color = Color.Transparent
    ) {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .background(bgBrush)
                .statusBarsPadding()
                .padding(20.dp)
        ) {
            DecorativeEmojisTop()

            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(top = 40.dp, bottom = 24.dp),
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(
                    text = "🎉 Birthday Surprise 🎉",
                    fontSize = 28.sp,
                    fontWeight = FontWeight.ExtraBold,
                    color = Color(0xFF7A2E5D),
                    textAlign = TextAlign.Center
                )

                Spacer(modifier = Modifier.height(24.dp))

                Card(
                    modifier = Modifier.fillMaxWidth(),
                    shape = RoundedCornerShape(28.dp),
                    colors = CardDefaults.cardColors(
                        containerColor = Color.White.copy(alpha = 0.92f)
                    ),
                    elevation = CardDefaults.cardElevation(defaultElevation = 8.dp)
                ) {
                    Column(
                        modifier = Modifier.padding(24.dp),
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        Text(
                            text = "🎁",
                            fontSize = 72.sp
                        )

                        Spacer(modifier = Modifier.height(12.dp))

                        Text(
                            text = "Hi $to, I have something special for you!\nPlease open the box!",
                            fontSize = 22.sp,
                            lineHeight = 30.sp,
                            fontWeight = FontWeight.SemiBold,
                            color = Color(0xFF4E3A59),
                            textAlign = TextAlign.Center
                        )

                        Spacer(modifier = Modifier.height(24.dp))

                        Button(
                            onClick = onOpenGift,
                            shape = RoundedCornerShape(18.dp),
                            colors = ButtonDefaults.buttonColors(
                                containerColor = Color(0xFFFF5E9C),
                                contentColor = Color.White
                            ),
                            modifier = Modifier
                                .fillMaxWidth()
                                .height(56.dp)
                        ) {
                            Text(
                                text = "Open the Gift",
                                fontSize = 18.sp,
                                fontWeight = FontWeight.Bold
                            )
                        }
                    }
                }
            }

            DecorativeEmojisBottom(
                modifier = Modifier.align(Alignment.BottomCenter)
            )
        }
    }
}

@Composable
fun GreetingScreen(
    from: String,
    to: String,
    onReplay: () -> Unit
) {
    val bgBrush = Brush.verticalGradient(
        colors = listOf(
            Color(0xFF8EC5FC),
            Color(0xFFE0C3FC),
            Color(0xFFFFDEE9)
        )
    )

    Surface(
        modifier = Modifier.fillMaxSize(),
        color = Color.Transparent
    ) {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .background(bgBrush)
                .statusBarsPadding()
                .padding(20.dp)
        ) {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(vertical = 24.dp),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center
            ) {
                Text(
                    text = "🎂✨🎈",
                    fontSize = 48.sp
                )

                Spacer(modifier = Modifier.height(16.dp))

                Card(
                    modifier = Modifier.fillMaxWidth(),
                    shape = RoundedCornerShape(30.dp),
                    colors = CardDefaults.cardColors(
                        containerColor = Color.White.copy(alpha = 0.93f)
                    ),
                    elevation = CardDefaults.cardElevation(defaultElevation = 10.dp)
                ) {
                    Column(
                        modifier = Modifier.padding(28.dp),
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        Text(
                            text = "Happy Birthday, $to!",
                            fontSize = 30.sp,
                            fontWeight = FontWeight.ExtraBold,
                            color = Color(0xFF9C2F6B),
                            textAlign = TextAlign.Center
                        )

                        Spacer(modifier = Modifier.height(18.dp))

                        Text(
                            text = "Wish you all the best. I hope your dreams come true and your days are always filled with happiness, laughter, and love.\n\nFrom the deepest heart,\n$from",
                            fontSize = 20.sp,
                            lineHeight = 30.sp,
                            color = Color(0xFF4C4060),
                            textAlign = TextAlign.Center
                        )

                        Spacer(modifier = Modifier.height(24.dp))

                        Button(
                            onClick = onReplay,
                            shape = RoundedCornerShape(18.dp),
                            colors = ButtonDefaults.buttonColors(
                                containerColor = Color(0xFF7B61FF),
                                contentColor = Color.White
                            ),
                            modifier = Modifier
                                .fillMaxWidth()
                                .height(54.dp)
                        ) {
                            Text(
                                text = "See Again",
                                fontSize = 18.sp,
                                fontWeight = FontWeight.Bold
                            )
                        }
                    }
                }

                Spacer(modifier = Modifier.height(20.dp))

                Text(
                    text = "🎉 🎊 💖 🌟 🎁",
                    fontSize = 30.sp
                )
            }
        }
    }
}

@Composable
fun DecorativeEmojisTop() {
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        Text(text = "🎈", fontSize = 32.sp)
        Text(text = "✨", fontSize = 28.sp)
        Text(text = "🎉", fontSize = 30.sp)
    }
}

@Composable
fun DecorativeEmojisBottom(modifier: Modifier = Modifier) {
    Row(
        modifier = modifier
            .fillMaxWidth()
            .clip(RoundedCornerShape(20.dp))
            .background(Color.White.copy(alpha = 0.35f))
            .padding(vertical = 10.dp, horizontal = 14.dp),
        horizontalArrangement = Arrangement.SpaceEvenly,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Text(text = "🎂", fontSize = 28.sp)
        Text(text = "💖", fontSize = 28.sp)
        Text(text = "🎁", fontSize = 28.sp)
        Text(text = "🎊", fontSize = 28.sp)
        Text(text = "🌟", fontSize = 28.sp)
    }
}

5. Penjelasan Kode dan Fungsinya

a. Baris package com.example.birthday

Baris package digunakan untuk menandai lokasi file Kotlin di dalam struktur project. Package membantu pengelompokan file program agar rapi dan memudahkan Android Studio mengenali struktur project.

b. Blok import ...

Bagian import dipakai untuk mengambil class dan fungsi yang dibutuhkan. Pada kode ini, import digunakan untuk memanggil komponen Compose seperti Text, Button, Card, AlertDialog, serta fitur animasi seperti AnimatedContent. Selain itu, ada juga import untuk remember dan mutableStateOf yang berperan penting dalam pengelolaan state.

c. Blok class MainActivity : ComponentActivity()

Ini adalah activity utama yang pertama kali berjalan ketika aplikasi dibuka. Karena aplikasi menggunakan Compose, activity mewarisi ComponentActivity agar bisa menampilkan antarmuka melalui setContent.

d. Fungsi onCreate()

Fungsi onCreate() adalah titik awal activity. Pada bagian ini, UI utama langsung dimasukkan ke layar melalui setContent. Di dalamnya juga digunakan MaterialTheme agar tampilan mengikuti gaya Material Design.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MaterialTheme {
                BirthdayApp()
            }
        }
    }
}

e. Fungsi BirthdayApp() sebagai pusat alur aplikasi

Fungsi BirthdayApp() adalah composable utama yang menjadi pusat kontrol aplikasi. Pada blok ini terdapat data statis pengirim dan penerima, state untuk menentukan apakah layar ucapan sudah ditampilkan, serta state untuk mengatur popup pembukaan hadiah.

@Composable
fun BirthdayApp() {
    val from = "Dapunta"
    val to = "Sam"

    var showGreetingScreen by remember { mutableStateOf(false) }
    var showPopup by remember { mutableStateOf(false) }
}

Dua variabel state di atas sangat penting. Nilai showGreetingScreen menentukan apakah yang ditampilkan adalah layar awal atau layar ucapan. Nilai showPopup menentukan apakah dialog pembukaan hadiah sedang aktif. Karena keduanya disimpan sebagai state Compose, setiap perubahan nilainya akan langsung memperbarui tampilan secara otomatis.

f. Data pengirim dan penerima

Nama pengirim dan penerima dibuat statis.

val from = "Dapunta"
val to = "Sam"

Keuntungan pendekatan ini adalah logika aplikasi menjadi lebih sederhana. Nama tidak perlu diambil dari input pengguna, sehingga fokus pengerjaan ada pada flow UI dan transisinya.

g. Blok if (showPopup) untuk menampilkan dialog

Ketika tombol Open the Gift ditekan, state showPopup berubah menjadi true. Akibatnya, blok if (showPopup) dijalankan dan dialog muncul di layar.

if (showPopup) {
    AlertDialog(
        onDismissRequest = { },
        confirmButton = {},
        title = {
            Text(
                text = "🎁 Opening your gift...",
                fontWeight = FontWeight.Bold
            )
        },
        text = {
            Text("A special surprise is on the way for $to ✨")
        }
    )
}

Dialog ini berfungsi sebagai efek transisi sementara, seolah-olah hadiah sedang dibuka sebelum pengguna masuk ke layar utama ucapan. Karena confirmButton = {}, dialog ini tidak menampilkan tombol aksi tambahan. Jadi dialog hanya berperan sebagai tampilan sementara, bukan dialog interaktif biasa.

h. Blok LaunchedEffect(Unit) dan delay(1200)

Setelah popup muncul, aplikasi tidak langsung berpindah layar. Di sini digunakan LaunchedEffect untuk menjalankan proses tertunda selama 1200 milidetik. Setelah jeda selesai, popup ditutup dan layar ucapan ditampilkan.

LaunchedEffect(Unit) {
    delay(1200)
    showPopup = false
    showGreetingScreen = true
}

Secara fungsional, blok ini adalah inti dari efek “membuka hadiah”. Tanpa blok ini, popup tidak akan punya durasi tampil, dan perpindahan layar akan terasa terlalu mendadak.

i. Blok AnimatedContent(...) untuk perpindahan layar

Perpindahan antara layar pembuka dan layar ucapan diatur dengan AnimatedContent. Komponen ini memantau nilai showGreetingScreen. Jika nilainya false, layar awal yang ditampilkan. Jika nilainya true, aplikasi menampilkan layar ucapan.

AnimatedContent(
    targetState = showGreetingScreen,
    transitionSpec = { fadeIn() togetherWith fadeOut() },
    label = "birthday_screen_transition"
) { isGreetingScreen ->
    if (isGreetingScreen) {
        GreetingScreen(
            from = from,
            to = to,
            onReplay = { showGreetingScreen = false }
        )
    } else {
        IntroScreen(
            to = to,
            onOpenGift = { showPopup = true }
        )
    }
}

Efek fadeIn() dan fadeOut() membuat perpindahan antarlayar terasa lebih halus. Secara visual, pengguna tidak merasa seperti masuk ke halaman yang benar-benar terpisah, melainkan seperti membuka lembar ucapan digital.

j. Fungsi IntroScreen()

Fungsi ini bertugas menampilkan layar pertama aplikasi. Di layar ini terdapat judul Birthday Surprise, pesan pembuka, gambar emoji kado, tombol Open the Gift, dan elemen dekoratif di bagian atas serta bawah.

@Composable
fun IntroScreen(
    to: String,
    onOpenGift: () -> Unit
)

Parameter to digunakan untuk menyisipkan nama penerima ke dalam teks, sedangkan onOpenGift adalah callback yang dijalankan saat tombol dibuka. Dengan pola seperti ini, UI dan logika bisa dipisahkan dengan rapi.

k. Gradient background pada layar awal

Warna latar layar awal dibuat menggunakan Brush.verticalGradient. Ini membuat tampilan lebih menarik dibanding satu warna polos biasa.

val bgBrush = Brush.verticalGradient(
    colors = listOf(
        Color(0xFFFFD6E8),
        Color(0xFFFFF1C9),
        Color(0xFFD6F4FF)
    )
)

Kombinasi warna pink, kuning lembut, dan biru muda dipilih agar memberi kesan ceria, hangat, dan cocok untuk tema ulang tahun.

l. Blok Surface, Box, dan Column

Tiga komponen ini dipakai sebagai kerangka layout utama.

  • Surface berfungsi sebagai wadah dasar layar.
  • Box dipakai untuk menumpuk elemen, misalnya dekorasi atas, isi utama, dan dekorasi bawah.
  • Column digunakan untuk menyusun isi secara vertikal dari atas ke bawah.

Dengan kombinasi ini, struktur layout menjadi sederhana namun tetap fleksibel.

m. Penggunaan Card sebagai area isi utama

Isi pesan utama diletakkan dalam Card. Card memberi tampilan seperti kartu ucapan modern dengan sudut membulat, background putih transparan, dan bayangan halus.

Card(
    modifier = Modifier.fillMaxWidth(),
    shape = RoundedCornerShape(28.dp),
    colors = CardDefaults.cardColors(
        containerColor = Color.White.copy(alpha = 0.92f)
    ),
    elevation = CardDefaults.cardElevation(defaultElevation = 8.dp)
)

Secara fungsional, card membuat konten utama lebih fokus dan mudah dibaca karena terpisah jelas dari background dekoratif.

n. Tombol Open the Gift

Tombol ini adalah pemicu utama flow aplikasi. Saat ditekan, callback onOpenGift akan dijalankan, yang pada akhirnya mengubah state showPopup menjadi true.

Button(
    onClick = onOpenGift,
    shape = RoundedCornerShape(18.dp),
    colors = ButtonDefaults.buttonColors(
        containerColor = Color(0xFFFF5E9C),
        contentColor = Color.White
    )
) {
    Text(
        text = "Open the Gift",
        fontSize = 18.sp,
        fontWeight = FontWeight.Bold
    )
}

Jadi, satu klik pada tombol ini memulai tiga hal sekaligus: dialog muncul, waktu tunggu berjalan, lalu layar berubah ke ucapan ulang tahun.

o. Fungsi GreetingScreen()

Fungsi ini menampilkan layar kedua, yaitu layar utama ucapan ulang tahun. Di dalamnya ada judul ucapan, isi pesan, nama pengirim, tombol untuk mengulang, dan dekorasi emoji.

@Composable
fun GreetingScreen(
    from: String,
    to: String,
    onReplay: () -> Unit
)

Parameter from dan to digunakan untuk menampilkan pesan personal. Sedangkan onReplay digunakan untuk mengembalikan tampilan ke layar awal.

p. Gradient background pada layar ucapan

Layar ucapan juga memakai gradient, tetapi dengan kombinasi warna yang berbeda agar terasa sebagai momen spesial setelah hadiah dibuka.

val bgBrush = Brush.verticalGradient(
    colors = listOf(
        Color(0xFF8EC5FC),
        Color(0xFFE0C3FC),
        Color(0xFFFFDEE9)
    )
)

Warna biru, ungu muda, dan pink lembut memberi kesan manis dan elegan, sehingga ucapan terasa lebih hangat.

q. Teks ucapan utama

Judul ucapan dibuat dengan ukuran besar, tebal, dan warna yang kontras. Ini penting karena judul adalah pusat perhatian utama layar kedua.

Text(
    text = "Happy Birthday, $to!",
    fontSize = 30.sp,
    fontWeight = FontWeight.ExtraBold,
    color = Color(0xFF9C2F6B),
    textAlign = TextAlign.Center
)

Nama penerima dimasukkan langsung ke dalam string, sehingga tampilan menjadi lebih personal walaupun datanya tetap hardcode.

r. Isi pesan ucapan

Pesan ucapan ditulis dalam bahasa Inggris sesuai kebutuhan tugas. Pesan ini ditampilkan dengan Text biasa, tetapi diberi lineHeight agar lebih nyaman dibaca.

Text(
    text = "Wish you all the best. I hope your dreams come true and your days are always filled with happiness, laughter, and love.\n\nFrom the deepest heart,\n$from",
    fontSize = 20.sp,
    lineHeight = 30.sp,
    color = Color(0xFF4C4060),
    textAlign = TextAlign.Center
)

Bagian ini merupakan inti isi kartu ucapan. Nama pengirim kembali ditampilkan di akhir pesan untuk memberi kesan seperti surat pribadi.

s. Tombol See Again

Tombol ini dipakai untuk mengulang alur aplikasi dari awal. Saat ditekan, callback onReplay akan dijalankan dan nilai showGreetingScreen diubah kembali menjadi false.

Button(
    onClick = onReplay,
    shape = RoundedCornerShape(18.dp),
    colors = ButtonDefaults.buttonColors(
        containerColor = Color(0xFF7B61FF),
        contentColor = Color.White
    )
) {
    Text(
        text = "See Again",
        fontSize = 18.sp,
        fontWeight = FontWeight.Bold
    )
}

Secara fungsional, tombol ini membuat aplikasi bisa digunakan berulang kali tanpa perlu menutup dan membuka ulang aplikasi.

t. Fungsi DecorativeEmojisTop() dan DecorativeEmojisBottom()

Dua fungsi ini dibuat khusus untuk elemen dekoratif. Keduanya tidak mengandung logika interaksi, tetapi berperan penting dalam mempercantik UI.

@Composable
fun DecorativeEmojisTop() {
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        Text(text = "🎈", fontSize = 32.sp)
        Text(text = "✨", fontSize = 28.sp)
        Text(text = "🎉", fontSize = 30.sp)
    }
}
@Composable
fun DecorativeEmojisBottom(modifier: Modifier = Modifier) {
    Row(
        modifier = modifier
            .fillMaxWidth()
            .clip(RoundedCornerShape(20.dp))
            .background(Color.White.copy(alpha = 0.35f))
            .padding(vertical = 10.dp, horizontal = 14.dp),
        horizontalArrangement = Arrangement.SpaceEvenly,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Text(text = "🎂", fontSize = 28.sp)
        Text(text = "💖", fontSize = 28.sp)
        Text(text = "🎁", fontSize = 28.sp)
        Text(text = "🎊", fontSize = 28.sp)
        Text(text = "🌟", fontSize = 28.sp)
    }
}

Dengan membuat dekorasi sebagai composable terpisah, kode menjadi lebih modular, lebih mudah dibaca, dan lebih mudah dikembangkan lagi nanti.

6. Alur Kerja Aplikasi Secara Keseluruhan

Langkah 1 - Aplikasi dibuka

Android menjalankan MainActivity, lalu setContent memanggil BirthdayApp(). Pada kondisi awal, showGreetingScreen = false dan showPopup = false, sehingga layar yang ditampilkan adalah IntroScreen.

Langkah 2 - Pengguna menekan tombol Open the Gift

Tombol menjalankan callback onOpenGift yang mengubah showPopup menjadi true. Karena itu, dialog hadiah ditampilkan di layar.

Langkah 3 - Popup tampil sementara

Saat popup aktif, LaunchedEffect menunggu selama 1200 milidetik. Setelah itu, popup ditutup dan state showGreetingScreen diubah menjadi true.

Langkah 4 - Layar berpindah ke ucapan

Karena target state pada AnimatedContent berubah, Compose mengganti tampilan dari IntroScreen menjadi GreetingScreen dengan animasi fade.

Langkah 5 - Pengguna bisa mengulang lagi

Jika tombol See Again ditekan, state showGreetingScreen kembali menjadi false. Akibatnya, layar kembali ke tampilan awal.

7. Kelebihan Implementasi Ini

  • Struktur kode relatif ringkas karena menggunakan Jetpack Compose tanpa XML.
  • State dikelola dengan sederhana menggunakan remember dan mutableStateOf.
  • Alur aplikasi jelas karena hanya berpusat pada dua state utama.
  • Tampilan lebih menarik berkat gradient, card, dan emoji dekoratif.
  • Kode modular karena layar dan dekorasi dipisah menjadi beberapa composable.
  • Transisi antar tampilan terasa halus dengan AnimatedContent.

8. Screenshot Aplikasi

Screenshot tampilan awal aplikasi birthday greetings
Gambar 1. Tampilan awal aplikasi saat pengguna diminta membuka hadiah.
Screenshot tampilan ucapan birthday greetings
Gambar 2. Tampilan layar ucapan ulang tahun setelah hadiah dibuka.

9. Penutup

Dari tugas ini saya belajar bahwa Jetpack Compose sangat memudahkan pembuatan aplikasi Android sederhana dengan tampilan yang menarik. Meskipun project ini tidak terlalu kompleks, di dalamnya sudah terdapat konsep penting seperti composable function, state management, callback interaksi, popup, animasi transisi, layout, dan styling. Dengan latihan seperti ini, saya jadi lebih memahami bagaimana menyusun aplikasi Android modern secara deklaratif menggunakan Kotlin dan Jetpack Compose.

Komentar

Postingan Populer