Tugas 10 PPB G - Membuat Aplikasi Dessert Clicker Menggunakan Jetpack Compose

 Tugas Pertemuan 10 PPB G 2025

oleh : Arfi Raushani Fikra (5025211084)

Pada pertemuan ke-10 ini, kami diminta membuat aplikasi android sederhana untuk mempelajari activity lifecycle. Aplikasi ini bernama Dessert Clicker.Di Dessert Clicker, setiap kali pengguna melakukan klik dessert di layar, aplikasi akan "membeli" dessert tersebut untuk pengguna. Aplikasi memperbarui nilai di tampilan untuk jumlah dessert yang "dibeli" dan total pendapatan untuk dessert yang "dibeli".

Source code dapat dilihat disini

Berikut penjelasan kodenya

1. Model – Dessert.kt
package com.example.dessertclicker.model

data class Dessert(val imageId: Int, val price: Int, val startProductionAmount: Int)
  • imageId: ID gambar untuk dessert.
  • price: Harga dessert saat dijual.
  • startProductionAmount: Minimum jumlah penjualan sebelum dessert ini mulai muncul.

2. Data Source – Datasource.kt

Berisi daftar Dessert yang disediakan secara statis dalam objek singleton Datasource.

object Datasource {
val dessertList = listOf(
Dessert(R.drawable.cupcake, 5, 0),
Dessert(R.drawable.donut, 10, 5),
Dessert(R.drawable.eclair, 15, 20),
Dessert(R.drawable.froyo, 30, 50),
Dessert(R.drawable.gingerbread, 50, 100),
Dessert(R.drawable.honeycomb, 100, 200),
Dessert(R.drawable.icecreamsandwich, 500, 500),
Dessert(R.drawable.jellybean, 1000, 1000),
Dessert(R.drawable.kitkat, 2000, 2000),
Dessert(R.drawable.lollipop, 3000, 4000),
Dessert(R.drawable.marshmallow, 4000, 8000),
Dessert(R.drawable.nougat, 5000, 16000),
Dessert(R.drawable.oreo, 6000, 20000)
)
}


3. MainActivity – MainActivity.kt

Lifecycle Logging

Mencatat aktivitas lifecycle (onCreate, onStart, dst.) menggunakan Log.d.

a. Fungsi determineDessertToShow

Menentukan dessert mana yang akan ditampilkan berdasarkan total dessert yang sudah terjual

fun determineDessertToShow(
desserts: List<Dessert>,
dessertsSold: Int
): Dessert {
var dessertToShow = desserts.first()
for (dessert in desserts) {
if (dessertsSold >= dessert.startProductionAmount) {
dessertToShow = dessert
} else {
// The list of desserts is sorted by startProductionAmount. As you sell more desserts,
// you'll start producing more expensive desserts as determined by startProductionAmount
// We know to break as soon as we see a dessert who's "startProductionAmount" is greater
// than the amount sold.
break
}
}

return dessertToShow
}
b. Fungsi shareSoldDessertsInformation

Membuat intent ACTION_SEND untuk membagikan info total dessert yang terjual dan total pendapatan

private fun shareSoldDessertsInformation(intentContext: Context, dessertsSold: Int, revenue: Int) {
val sendIntent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(
Intent.EXTRA_TEXT,
intentContext.getString(R.string.share_text, dessertsSold, revenue)
)
type = "text/plain"
}

val shareIntent = Intent.createChooser(sendIntent, null)

try {
ContextCompat.startActivity(intentContext, shareIntent, null)
} catch (e: ActivityNotFoundException) {
Toast.makeText(
intentContext,
intentContext.getString(R.string.sharing_not_available),
Toast.LENGTH_LONG
).show()
}
}


c. Komponen Composable UI

# DessertClickerApp()

UI utama yang mengatur state aplikasi:

  • revenue: total pendapatan.

  • dessertsSold: total dessert terjual.

  • currentDessertImageId, currentDessertPrice: data dessert saat ini.

Saat gambar diklik:

  • Pendapatan dan jumlah terjual bertambah.

  • Dessert diganti sesuai hasil fungsi determineDessertToShow().

@Composable
private fun DessertClickerApp(
desserts: List<Dessert>
) {

var revenue by rememberSaveable { mutableStateOf(0) }
var dessertsSold by rememberSaveable { mutableStateOf(0) }

val currentDessertIndex by rememberSaveable { mutableStateOf(0) }

var currentDessertPrice by rememberSaveable {
mutableStateOf(desserts[currentDessertIndex].price)
}
var currentDessertImageId by rememberSaveable {
mutableStateOf(desserts[currentDessertIndex].imageId)
}

Scaffold(
topBar = {
val intentContext = LocalContext.current
val layoutDirection = LocalLayoutDirection.current
DessertClickerAppBar(
onShareButtonClicked = {
shareSoldDessertsInformation(
intentContext = intentContext,
dessertsSold = dessertsSold,
revenue = revenue
)
},
modifier = Modifier
.fillMaxWidth()
.padding(
start = WindowInsets.safeDrawing.asPaddingValues()
.calculateStartPadding(layoutDirection),
end = WindowInsets.safeDrawing.asPaddingValues()
.calculateEndPadding(layoutDirection),
)
.background(MaterialTheme.colorScheme.primary)
)
}
) { contentPadding ->
DessertClickerScreen(
revenue = revenue,
dessertsSold = dessertsSold,
dessertImageId = currentDessertImageId,
onDessertClicked = {

// Update the revenue
revenue += currentDessertPrice
dessertsSold++

// Show the next dessert
val dessertToShow = determineDessertToShow(desserts, dessertsSold)
currentDessertImageId = dessertToShow.imageId
currentDessertPrice = dessertToShow.price
},
modifier = Modifier.padding(contentPadding)
)
}
}


# DessertClickerAppBar()

Top bar aplikasi berisi judul dan tombol share.

@Composable
private fun DessertClickerAppBar(
onShareButtonClicked: () -> Unit,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = stringResource(R.string.app_name),
modifier = Modifier.padding(start = dimensionResource(R.dimen.padding_medium)),
color = MaterialTheme.colorScheme.onPrimary,
style = MaterialTheme.typography.titleLarge,
)
IconButton(
onClick = onShareButtonClicked,
modifier = Modifier.padding(end = dimensionResource(R.dimen.padding_medium)),
) {
Icon(
imageVector = Icons.Filled.Share,
contentDescription = stringResource(R.string.share),
tint = MaterialTheme.colorScheme.onPrimary
)
}
}
}


# DessertClickerScreen()

Menampilkan latar belakang, gambar dessert, dan info transaksi:

@Composable
fun DessertClickerScreen(
revenue: Int,
dessertsSold: Int,
@DrawableRes dessertImageId: Int,
onDessertClicked: () -> Unit,
modifier: Modifier = Modifier
) {
Box(modifier = modifier) {
Image(
painter = painterResource(R.drawable.bakery_back),
contentDescription = null,
contentScale = ContentScale.Crop
)
Column {
Box(
modifier = Modifier
.weight(1f)
.fillMaxWidth(),
) {
Image(
painter = painterResource(dessertImageId),
contentDescription = null,
modifier = Modifier
.width(dimensionResource(R.dimen.image_size))
.height(dimensionResource(R.dimen.image_size))
.align(Alignment.Center)
.clickable { onDessertClicked() },
contentScale = ContentScale.Crop,
)
}
TransactionInfo(
revenue = revenue,
dessertsSold = dessertsSold,
modifier = Modifier.background(MaterialTheme.colorScheme.secondaryContainer)
)
}
}
}


# TransactionInfo()

Menampilkan informasi:

  • Total pendapatan (RevenueInfo)

  • Total penjualan dessert (DessertsSoldInfo)

@Composable
private fun TransactionInfo(
revenue: Int,
dessertsSold: Int,
modifier: Modifier = Modifier
) {
Column(modifier = modifier) {
DessertsSoldInfo(
dessertsSold = dessertsSold,
modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_medium))
)
RevenueInfo(
revenue = revenue,
modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_medium))
)
}
}

# RevenueInfo()

Menampilkan informasi total pendapatan (revenue) dalam bentuk baris (Row), dengan label "Total Revenue" di kiri dan jumlah pendapatan dalam format dolar di kanan.

@Composable
private fun RevenueInfo(revenue: Int, modifier: Modifier = Modifier) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = stringResource(R.string.total_revenue),
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onSecondaryContainer
)
Text(
text = "$${revenue}",
textAlign = TextAlign.Right,
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onSecondaryContainer
)
}
}


# DessertsSoldInfo()

Menampilkan jumlah makanan penutup (dessert) yang terjual. Sama seperti RevenueInfo, tapi dengan label dan gaya teks berbeda.

@Composable
private fun DessertsSoldInfo(dessertsSold: Int, modifier: Modifier = Modifier) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = stringResource(R.string.dessert_sold),
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onSecondaryContainer
)
Text(
text = dessertsSold.toString(),
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onSecondaryContainer
)
}
}

# @Preview

Preview sederhana untuk UI menggunakan DessertClickerTheme.

@Preview
@Composable
fun MyDessertClickerAppPreview() {
DessertClickerTheme {
DessertClickerApp(listOf(Dessert(R.drawable.cupcake, 5, 0)))
}
}

Berikut hasil dari aplikasinya



                                                                        ~ Terima Kasih ~

Comments

Popular posts from this blog

Tugas 1 Presentasi Evolusi Teknologi Bergerak PPB G

Tugas 5 PPB G - Membuat Aplikasi Kalkulator Sederhana Menggunakan Jetpack Compose

ETS PPB G - Membuat Aplikasi Centry: Money Tracker App Menggunakan Jetpack Compose