This commit is contained in:
nuryuda 2025-11-07 17:02:27 +07:00
parent 099c35f19a
commit b88d767571
2 changed files with 698 additions and 145 deletions

View File

@ -8,3 +8,56 @@ https://docs.google.com/document/d/1iGiC0Bg3Bdcd2Maq45TYkCDUkZ5Ql51E/edit?rtpof=
Starter dimodifikasi dan terinspirasi dari: Starter dimodifikasi dan terinspirasi dari:
https://developer.android.com/codelabs/basic-android-compose-calculate-tip#0 https://developer.android.com/codelabs/basic-android-compose-calculate-tip#0
# 🧮 BMI Calculator (Metrik & Imperial)
Sebuah aplikasi modern dan interaktif untuk menghitung **Body Mass Index (BMI)** menggunakan dua sistem satuan:
🌍 **Metrik (kg, m)** dan 🇺🇸 **Imperial (lbs, inch)**.
Aplikasi ini membantu kamu mengetahui apakah berat badanmu tergolong **kurus**, **ideal**, **berlebih**, atau **obesitas** berdasarkan standar kesehatan internasional (WHO).
---
## ✨ Fitur Utama
✅ Hitung BMI dengan **dua pilihan sistem satuan**:
- **Metrik** → kilogram (kg) & meter (m)
- **Imperial** → pound (lbs) & inch (in)
✅ Menampilkan hasil **BMI secara otomatis**
✅ Menentukan **kategori berat badan** sesuai standar WHO
✅ Antarmuka **sederhana, responsif, dan mudah digunakan**
✅ Dapat dijalankan di **desktop maupun mobile**
✅ Dikembangkan dengan bantuan **AI cerdas (ChatGPT & Claude)**
---
## 🧠 Kategori BMI
| Kategori | Rentang Nilai BMI |
|------------------------------|------------------|
| Kurang / Underweight | < 18.5 |
| Normal / Ideal | 18.5 24.9 |
| Kelebihan berat badan / Overweight | 25.0 29.9 |
| Obesitas / Obese | ≥ 30.0 |
---
## 🚀 Cara Menggunakan
1. Pilih sistem satuan: **Metrik** atau **Imperial**
2. Masukkan berat badan dan tinggi badan sesuai sistem satuan
3. Klik tombol **Hitung / Calculate**
4. Lihat hasil **BMI** dan kategori kesehatannya
---
## 💻 Contoh Penggunaan
### 🌍 **Metrik**
Aplikasi Ini Di Bantu Oleh
-ChatGpt
-Claude Ai
Satrio Putra Wardani 202310715307

View File

@ -1,18 +1,3 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.tiptime package com.example.tiptime
import android.os.Bundle import android.os.Bundle
@ -20,42 +5,42 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.annotation.StringRes import androidx.compose.animation.*
import androidx.compose.foundation.layout.Arrangement import androidx.compose.animation.core.*
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.*
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.safeDrawingPadding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon import androidx.compose.material.icons.Icons
import androidx.compose.material3.MaterialTheme import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Surface import androidx.compose.material3.*
import androidx.compose.material3.Switch import androidx.compose.runtime.*
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
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.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.tiptime.ui.theme.TipTimeTheme import com.example.tiptime.ui.theme.TipTimeTheme
import java.text.NumberFormat import kotlin.math.pow
import java.text.DecimalFormat
// Warna tema klasik
private val ClassicGold = Color(0xFFD4AF37)
private val ClassicCream = Color(0xFFFFF8DC)
private val ClassicBrown = Color(0xFF8B4513)
private val ClassicDarkBrown = Color(0xFF654321)
private val ClassicBeige = Color(0xFFF5F5DC)
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -63,10 +48,14 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
TipTimeTheme { TipTimeTheme {
Surface( Surface(modifier = Modifier.fillMaxSize()) {
modifier = Modifier.fillMaxSize(), var showWelcome by remember { mutableStateOf(true) }
) {
TipTimeLayout() if (showWelcome) {
WelcomeScreen(onContinue = { showWelcome = false })
} else {
BmiCalculatorLayout(onBackPressed = { showWelcome = true })
}
} }
} }
} }
@ -74,141 +63,652 @@ class MainActivity : ComponentActivity() {
} }
@Composable @Composable
fun TipTimeLayout() { fun WelcomeScreen(onContinue: () -> Unit) {
var amountInput by remember { mutableStateOf("") } var visible by remember { mutableStateOf(false) }
var tipInput by remember { mutableStateOf("") }
var roundUp by remember { mutableStateOf(false) }
val BmiHeight = amountInput.toDoubleOrNull() ?: 0.0 LaunchedEffect(Unit) {
val BmiWeight = tipInput.toDoubleOrNull() ?: 0.0 visible = true
val bmi = calculateBMI(BmiHeight, BmiWeight, roundUp) }
val category = calculateBMICategory(BmiHeight, BmiWeight, roundUp)
Box(
modifier = Modifier
.fillMaxSize()
.background(
Brush.verticalGradient(
colors = listOf(
ClassicDarkBrown,
ClassicBrown,
ClassicBeige
)
)
)
) {
Column(
modifier = Modifier
.fillMaxSize()
.statusBarsPadding()
.padding(32.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween
) {
// Spacer untuk centering
Spacer(modifier = Modifier.height(60.dp))
// Content utama
AnimatedVisibility(
visible = visible,
enter = fadeIn(animationSpec = tween(1000)) +
scaleIn(initialScale = 0.8f, animationSpec = tween(1000))
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
// Icon/Logo
Card(
modifier = Modifier
.size(140.dp)
.shadow(16.dp, RoundedCornerShape(70.dp)),
shape = RoundedCornerShape(70.dp),
colors = CardDefaults.cardColors(
containerColor = ClassicGold
),
border = BorderStroke(4.dp, ClassicCream)
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxSize()
) {
Text(
text = "💪",
fontSize = 72.sp
)
}
}
Spacer(modifier = Modifier.height(40.dp))
// Ornamen atas
Text(
text = "✦ ═══════════════ ✦",
color = ClassicGold,
fontSize = 18.sp,
modifier = Modifier.padding(bottom = 20.dp)
)
// Judul utama
Text(
text = "KALKULATOR",
fontSize = 32.sp,
fontWeight = FontWeight.Bold,
color = ClassicGold,
letterSpacing = 5.sp
)
Text(
text = "BMI Satrio Putra Wardani",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = ClassicCream,
letterSpacing = 3.sp,
modifier = Modifier.padding(vertical = 12.dp)
)
// Ornamen bawah
Text(
text = "✦ ═══════════════ ✦",
color = ClassicGold,
fontSize = 18.sp,
modifier = Modifier.padding(bottom = 32.dp)
)
// Deskripsi
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 8.dp),
shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors(
containerColor = ClassicCream.copy(alpha = 0.15f)
),
border = BorderStroke(1.5.dp, ClassicGold.copy(alpha = 0.4f))
) {
Column(
modifier = Modifier.padding(28.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Hitung Indeks Massa Tubuh Anda",
fontSize = 19.sp,
fontWeight = FontWeight.SemiBold,
color = ClassicCream,
textAlign = TextAlign.Center,
modifier = Modifier.padding(bottom = 16.dp)
)
Text(
text = "Ketahui status kesehatan Anda dengan perhitungan BMI yang akurat dan mudah",
fontSize = 15.sp,
color = ClassicCream.copy(alpha = 0.85f),
textAlign = TextAlign.Center,
lineHeight = 22.sp
)
}
}
}
}
// Tombol dan footer
AnimatedVisibility(
visible = visible,
enter = fadeIn(animationSpec = tween(1000, delayMillis = 500)) +
slideInVertically(
initialOffsetY = { 50 },
animationSpec = tween(1000, delayMillis = 500)
)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(bottom = 8.dp)
) {
Button(
onClick = onContinue,
modifier = Modifier
.fillMaxWidth()
.height(60.dp)
.shadow(12.dp, RoundedCornerShape(30.dp)),
shape = RoundedCornerShape(30.dp),
colors = ButtonDefaults.buttonColors(
containerColor = ClassicGold,
contentColor = ClassicDarkBrown
),
border = BorderStroke(2.5.dp, ClassicCream)
) {
Text(
text = "MULAI PERHITUNGAN",
fontSize = 19.sp,
fontWeight = FontWeight.Bold,
letterSpacing = 1.5.sp
)
}
Spacer(modifier = Modifier.height(28.dp))
// Divider dekoratif
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(horizontal = 32.dp)
) {
Box(
modifier = Modifier
.weight(1f)
.height(1.dp)
.background(ClassicCream.copy(alpha = 0.3f))
)
Text(
text = "",
color = ClassicGold.copy(alpha = 0.6f),
fontSize = 16.sp
)
Box(
modifier = Modifier
.weight(1f)
.height(1.dp)
.background(ClassicCream.copy(alpha = 0.3f))
)
}
Spacer(modifier = Modifier.height(20.dp))
Text(
text = "Universitas Bhayangkara Jakarta Raya",
fontSize = 13.sp,
color = ClassicDarkBrown.copy(alpha = 0.5f),
letterSpacing = 1.sp
)
Text(
text = "SATRIO PUTRA WARDANI",
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
color = ClassicGold.copy(alpha = 0.8f),
letterSpacing = 2.sp,
modifier = Modifier.padding(top = 4.dp)
)
}
}
}
}
}
@Composable
fun BmiCalculatorLayout(onBackPressed: () -> Unit) {
var heightInput by remember { mutableStateOf("") }
var weightInput by remember { mutableStateOf("") }
var useImperial by remember { mutableStateOf(false) }
val height = heightInput.toDoubleOrNull() ?: 0.0
val weight = weightInput.toDoubleOrNull() ?: 0.0
val bmi = calculateBMI(height, weight, useImperial)
val category = getBMICategory(bmi)
Box(
modifier = Modifier
.fillMaxSize()
.background(
Brush.verticalGradient(
colors = listOf(
ClassicCream,
ClassicBeige
)
)
)
) {
Column( Column(
modifier = Modifier modifier = Modifier
.statusBarsPadding() .statusBarsPadding()
.padding(horizontal = 40.dp) .padding(20.dp)
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState()),
.safeDrawingPadding(), horizontalAlignment = Alignment.CenterHorizontally
) {
// Tombol Back
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
IconButton(
onClick = onBackPressed,
modifier = Modifier
.shadow(4.dp, RoundedCornerShape(12.dp))
.background(ClassicGold, RoundedCornerShape(12.dp))
.size(48.dp)
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Kembali",
tint = ClassicDarkBrown
)
}
Spacer(modifier = Modifier.width(12.dp))
Text(
text = "Kembali ke Beranda",
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
color = ClassicDarkBrown
)
}
// Header dengan ornamen klasik
Card(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp)
.shadow(8.dp, RoundedCornerShape(16.dp)),
shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors(
containerColor = ClassicDarkBrown
),
border = BorderStroke(2.dp, ClassicGold)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center modifier = Modifier.padding(24.dp)
) { ) {
Text( Text(
text = stringResource(R.string.calculate_tip), text = "═══════════════",
modifier = Modifier color = ClassicGold,
.padding(bottom = 16.dp, top = 40.dp) fontSize = 20.sp
.align(alignment = Alignment.Start)
) )
EditNumberField( Text(
label = R.string.height, text = "KALKULATOR BMI",
fontSize = 32.sp,
fontWeight = FontWeight.Bold,
color = ClassicGold,
letterSpacing = 2.sp,
modifier = Modifier.padding(vertical = 8.dp)
)
Text(
text = "═══════════════",
color = ClassicGold,
fontSize = 20.sp
)
Text(
text = "Body Mass Index Calculator",
fontSize = 14.sp,
color = ClassicCream,
fontWeight = FontWeight.Light,
modifier = Modifier.padding(top = 8.dp)
)
}
}
// Form Input Card
Card(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
.shadow(6.dp, RoundedCornerShape(12.dp)),
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(
containerColor = Color.White
),
border = BorderStroke(1.dp, ClassicGold)
) {
Column(
modifier = Modifier.padding(20.dp)
) {
Text(
text = "Data Pengukuran",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = ClassicDarkBrown,
modifier = Modifier.padding(bottom = 16.dp)
)
ClassicTextField(
label = if (useImperial) "Tinggi (inch)" else "Tinggi (cm)",
leadingIcon = R.drawable.number, leadingIcon = R.drawable.number,
keyboardOptions = KeyboardOptions.Default.copy( keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number, keyboardType = KeyboardType.Number,
imeAction = ImeAction.Next imeAction = ImeAction.Next
), ),
value = amountInput, value = heightInput,
onValueChanged = { amountInput = it }, onValueChanged = { heightInput = it },
modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth(), modifier = Modifier
.padding(bottom = 16.dp)
.fillMaxWidth()
) )
EditNumberField(
label = R.string.weight, ClassicTextField(
label = if (useImperial) "Berat (lbs)" else "Berat (kg)",
leadingIcon = R.drawable.number, leadingIcon = R.drawable.number,
keyboardOptions = KeyboardOptions.Default.copy( keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number, keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done imeAction = ImeAction.Done
), ),
value = tipInput, value = weightInput,
onValueChanged = { tipInput = it }, onValueChanged = { weightInput = it },
modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth(), modifier = Modifier
) .padding(bottom = 16.dp)
RoundTheTipRow( .fillMaxWidth()
roundUp = roundUp,
onRoundUpChanged = { roundUp = it },
modifier = Modifier.padding(bottom = 32.dp)
)
Text(
text = stringResource(R.string.bmi_calculation, bmi),
style = MaterialTheme.typography.displaySmall
)
Text(
text = stringResource(R.string.bmi_category, category),
style = MaterialTheme.typography.displaySmall
) )
Spacer(modifier = Modifier.height(150.dp)) HorizontalDivider(
color = ClassicGold,
thickness = 1.dp,
modifier = Modifier.padding(vertical = 8.dp)
)
ClassicUnitSwitch(
useImperial = useImperial,
onUnitChange = { useImperial = it }
)
}
}
// Result Card
if (height > 0 && weight > 0) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
.shadow(8.dp, RoundedCornerShape(12.dp)),
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(
containerColor = ClassicDarkBrown
),
border = BorderStroke(2.dp, ClassicGold)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxWidth()
.padding(28.dp)
) {
Text(
text = "— Hasil Perhitungan —",
fontSize = 16.sp,
color = ClassicGold,
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(bottom = 16.dp)
)
Text(
text = "INDEKS MASSA TUBUH",
fontSize = 14.sp,
color = ClassicCream,
letterSpacing = 1.sp,
modifier = Modifier.padding(bottom = 8.dp)
)
Text(
text = DecimalFormat("#.##").format(bmi),
fontSize = 56.sp,
fontWeight = FontWeight.Bold,
color = ClassicGold,
modifier = Modifier.padding(vertical = 8.dp)
)
Box(
modifier = Modifier
.background(
color = getCategoryColor(category),
shape = RoundedCornerShape(24.dp)
)
.padding(horizontal = 24.dp, vertical = 12.dp)
) {
Text(
text = category.uppercase(),
fontSize = 20.sp,
color = Color.White,
fontWeight = FontWeight.Bold,
letterSpacing = 1.sp
)
}
Text(
text = getCategoryDescription(category),
fontSize = 13.sp,
color = ClassicCream,
textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 16.dp)
)
}
}
// Info Card
BMICategoryInfo()
}
Spacer(modifier = Modifier.height(40.dp))
}
} }
} }
@Composable @Composable
fun EditNumberField( fun ClassicTextField(
@StringRes label: Int, label: String,
@DrawableRes leadingIcon: Int, @DrawableRes leadingIcon: Int,
keyboardOptions: KeyboardOptions, keyboardOptions: KeyboardOptions,
value: String, value: String,
onValueChanged: (String) -> Unit, onValueChanged: (String) -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
TextField( OutlinedTextField(
value = value, value = value,
singleLine = true,
leadingIcon = { Icon(painter = painterResource(id = leadingIcon), null) },
modifier = modifier,
onValueChange = onValueChanged, onValueChange = onValueChanged,
label = { Text(stringResource(label)) }, label = {
keyboardOptions = keyboardOptions Text(
label,
color = ClassicBrown,
fontWeight = FontWeight.Medium
)
},
singleLine = true,
leadingIcon = {
Icon(
painter = painterResource(id = leadingIcon),
contentDescription = null,
tint = ClassicGold
)
},
keyboardOptions = keyboardOptions,
modifier = modifier,
colors = OutlinedTextFieldDefaults.colors(
focusedBorderColor = ClassicGold,
unfocusedBorderColor = ClassicBrown.copy(alpha = 0.5f),
focusedTextColor = ClassicDarkBrown,
unfocusedTextColor = ClassicDarkBrown
),
shape = RoundedCornerShape(8.dp)
) )
} }
@Composable @Composable
fun RoundTheTipRow( fun ClassicUnitSwitch(
roundUp: Boolean, useImperial: Boolean,
onRoundUpChanged: (Boolean) -> Unit, onUnitChange: (Boolean) -> Unit
modifier: Modifier = Modifier
) { ) {
Row( Row(
modifier = modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) { ) {
Text(text = stringResource(R.string.use_usc)) Text(
"Satuan Imperial (US)",
color = ClassicDarkBrown,
fontWeight = FontWeight.Medium
)
Switch( Switch(
modifier = Modifier checked = useImperial,
.fillMaxWidth() onCheckedChange = onUnitChange,
.wrapContentWidth(Alignment.End), colors = SwitchDefaults.colors(
checked = roundUp, checkedThumbColor = ClassicGold,
onCheckedChange = onRoundUpChanged checkedTrackColor = ClassicGold.copy(alpha = 0.5f),
uncheckedThumbColor = ClassicBrown,
uncheckedTrackColor = ClassicBrown.copy(alpha = 0.3f)
)
) )
} }
} }
/** @Composable
* Calculates the BMI fun BMICategoryInfo() {
* Card(
* Catatan: tambahkan unit test untuk kalkulasi BMI ini modifier = Modifier
*/ .fillMaxWidth()
private fun calculateBMI(BmiHeight: Double, BmiWeight: Double = 15.0, roundUp: Boolean): String { .padding(vertical = 8.dp)
var bmi = BmiWeight / 100 * BmiHeight .shadow(4.dp, RoundedCornerShape(12.dp)),
if (roundUp) { shape = RoundedCornerShape(12.dp),
bmi = kotlin.math.ceil(bmi) colors = CardDefaults.cardColors(
} containerColor = Color.White
return NumberFormat.getNumberInstance().format(bmi) ),
} border = BorderStroke(1.dp, ClassicGold.copy(alpha = 0.3f))
/** ) {
* Calculates the BMI Category Column(
* modifier = Modifier.padding(20.dp)
* Catatan: tambahkan unit test untuk kalkulasi BMI ini ) {
*/ Text(
text = "Kategori BMI",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = ClassicDarkBrown,
modifier = Modifier.padding(bottom = 12.dp)
)
private fun calculateBMICategory(BmiHeight: Double, BmiWeight: Double = 15.0, roundUp: Boolean): String { CategoryRow("Kurus", "< 18.5", Color(0xFF4A90E2))
var bmi = BmiWeight / 100 * BmiHeight CategoryRow("Normal", "18.5 - 24.9", Color(0xFF50C878))
if (roundUp) { CategoryRow("Gemuk", "25.0 - 29.9", Color(0xFFFF9500))
bmi = kotlin.math.ceil(bmi) CategoryRow("Obesitas", "≥ 30.0", Color(0xFFE74C3C))
}
} }
return NumberFormat.getNumberInstance().format(bmi)
} }
@Composable
fun CategoryRow(category: String, range: String, color: Color) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier
.size(12.dp)
.background(color, RoundedCornerShape(2.dp))
)
Spacer(modifier = Modifier.width(12.dp))
Text(
text = category,
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
color = ClassicDarkBrown,
modifier = Modifier.weight(1f)
)
Text(
text = range,
fontSize = 14.sp,
color = ClassicBrown,
fontWeight = FontWeight.Normal
)
}
}
fun getCategoryColor(category: String): Color {
return when (category) {
"Kurus" -> Color(0xFF4A90E2)
"Normal" -> Color(0xFF50C878)
"Gemuk" -> Color(0xFFFF9500)
"Obesitas" -> Color(0xFFE74C3C)
else -> ClassicBrown
}
}
fun getCategoryDescription(category: String): String {
return when (category) {
"Kurus" -> "Berat badan Anda kurang dari ideal"
"Normal" -> "Berat badan Anda ideal dan sehat"
"Gemuk" -> "Berat badan Anda melebihi ideal"
"Obesitas" -> "Konsultasikan dengan dokter Anda"
else -> ""
}
}
fun calculateBMI(height: Double, weight: Double, useImperial: Boolean): Double {
if (height <= 0 || weight <= 0) return 0.0
return if (useImperial) {
703 * weight / height.pow(2)
} else {
weight / (height / 100).pow(2)
}
}
fun getBMICategory(bmi: Double): String {
return when {
bmi == 0.0 -> "-"
bmi < 18.5 -> "Kurus"
bmi < 25 -> "Normal"
bmi < 30 -> "Gemuk"
else -> "Obesitas"
}
}
@Preview(showBackground = true) @Preview(showBackground = true)
@Composable @Composable
fun TipTimeLayoutPreview() { fun WelcomeScreenPreview() {
TipTimeTheme { TipTimeTheme {
TipTimeLayout() WelcomeScreen(onContinue = {})
}
}
@Preview(showBackground = true)
@Composable
fun BmiCalculatorPreview() {
TipTimeTheme {
BmiCalculatorLayout(onBackPressed = {})
} }
} }