Memperbarui Tampilan UI Dan UX Agar Lebih Menarik Dan Lebih Mudah Digunakan Seperti:

### **Versi 2.0 - Perubahan Unit Metric**
#### Perubahan Major:
-  **Mengubah unit tinggi dari meter ke centimeter (cm)**
    - Alasan: Lebih mudah dipahami pengguna Indonesia
    - Format baru: tinggi dalam **cm** (contoh: 170 cm)
-  Update formula kalkulasi:
    - Konversi cm ke meter: `heightInMeters = height / 100.0`
    - BMI = weight / (heightInMeters²)
-  Update validasi input:
    - Minimum tinggi: 50 cm
    - Maximum tinggi: 300 cm

---

### **Versi 3.0 - UI/UX Enhancement**
#### Perubahan UI/UX:
-  **Modern Card Design** dengan shadow effects
-  **Gradient Background** (Sky Blue ke White)
-  **Custom Input Fields** dengan Card wrapper
-  **Unit System Card** dengan design yang lebih menarik
-  **Color-coded Results** berdasarkan kategori BMI:
    - 🔵 Underweight - Light Blue
    - 🔵 Normal - Sky Blue
    - 🔵 Overweight - Medium Blue
    - 🔵 Obese - Dark Blue
-  **BMI Result Card** dengan:
    - Display BMI value besar dan bold
    - Badge kategori dengan emoji
    - Deskripsi lengkap untuk setiap kategori
    - Referensi range BMI WHO
-  **Responsive Layout** dengan vertical scroll
-  **Better Typography** dengan font weights dan sizing

---

### **Versi 4.0 - Blue Color Scheme Implementation**
#### Perubahan Tema:
-  **Complete Blue Color Theme**
    - Primary Color: Light Blue (#03A9F4)
    - Secondary Color: Medium Blue (#0288D1)
    - Background: Very Light Blue White (#F5FBFF)
    - Accent: Sky Blue dan Navy Blue
-  **Light Theme Colors**:
    - Primary: Light Blue - warna utama aplikasi
    - Container: Very Light Blue - untuk card backgrounds
    - Surface: White - permukaan bersih
-  **Dark Theme Colors**:
    - Primary: Sky Blue - untuk dark mode
    - Background: Very Dark Blue
    - Surface: Deep Blue
-  **Component Colors**:
    - Button: Light Blue dengan shadow
    - Input border: Light Blue saat focus
    - Switch: Sky Blue theme
    - Cards: Gradasi warna biru

---
This commit is contained in:
dend 2025-11-06 22:01:43 +07:00
parent 53ba1398ff
commit e1fa621f14
3 changed files with 627 additions and 182 deletions

205
README.md
View File

@ -1,10 +1,205 @@
Kalkulator BMI
===============
# Kalkulator BMI
Silahkan kembangkan aplikasi ini untuk melakukan perhitungan BMI
## 📱 Deskripsi Aplikasi
Aplikasi **BMI Calculator** (Body Mass Index Calculator) adalah aplikasi Android yang dibangun menggunakan Jetpack Compose untuk menghitung Indeks Massa Tubuh pengguna berdasarkan tinggi dan berat badan. Aplikasi ini mendukung dua sistem unit pengukuran: **Metric (kg/cm)** dan **Imperial (lbs/inches)**.
Petunjuk lebih detil dapat dibaca di
---
## 👨‍💻 Informasi Pengembang
- **NPM**: 202310715051
- **Nama**: Dendi Yogia Pratama
---
## 📚 Referensi
Petunjuk lebih detail dapat dibaca di:
https://docs.google.com/document/d/1iGiC0Bg3Bdcd2Maq45TYkCDUkZ5Ql51E/edit?rtpof=true
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
---
## 🔄 Changelog - Perubahan dari Awal sampai Akhir
### **Versi 0.5 - Setup Code**
- Memperbaiki Rumus Yang Digunakan Untuk Menghitung BMI Agar Lebih Akurat Di Kedua Sistem Unit
- Mengatasi Tulisan NAN Yang Muncul Ditampilan Sebelum Kalkulasi
### **Versi 1.0 - Initial Setup (Base Code)**
#### Fitur Awal:
- Setup project dengan Jetpack Compose
- Layout dasar dengan TextField untuk input
- Fungsi kalkulasi BMI sederhana
- Menggunakan sistem Metric (meter dan kg)
---
### **Versi 1.5 - Penambahan Unit System**
#### Perubahan:
- ✅ Menambahkan **Switch Toggle** untuk memilih sistem unit (USC/SI)
- ✅ Implementasi dua formula BMI:
- **SI Formula**: BMI = weight (kg) / height² (meters)
- **USC Formula**: BMI = 703 × weight (lbs) / height² (inches)
- ✅ Validasi input untuk kedua sistem unit
- ✅ Dynamic label pada input field sesuai sistem unit yang dipilih
---
### **Versi 2.0 - Perubahan Unit Metric**
#### Perubahan Major:
- ✅ **Mengubah unit tinggi dari meter ke centimeter (cm)**
- Alasan: Lebih mudah dipahami pengguna Indonesia
- Format baru: tinggi dalam **cm** (contoh: 170 cm)
- ✅ Update formula kalkulasi:
- Konversi cm ke meter: `heightInMeters = height / 100.0`
- BMI = weight / (heightInMeters²)
- ✅ Update validasi input:
- Minimum tinggi: 50 cm
- Maximum tinggi: 300 cm
---
### **Versi 3.0 - UI/UX Enhancement**
#### Perubahan UI/UX:
- ✅ **Modern Card Design** dengan shadow effects
- ✅ **Gradient Background** (Sky Blue ke White)
- ✅ **Custom Input Fields** dengan Card wrapper
- ✅ **Unit System Card** dengan design yang lebih menarik
- ✅ **Color-coded Results** berdasarkan kategori BMI:
- 🔵 Underweight - Light Blue
- 🔵 Normal - Sky Blue
- 🔵 Overweight - Medium Blue
- 🔵 Obese - Dark Blue
- ✅ **BMI Result Card** dengan:
- Display BMI value besar dan bold
- Badge kategori dengan emoji
- Deskripsi lengkap untuk setiap kategori
- Referensi range BMI WHO
- ✅ **Responsive Layout** dengan vertical scroll
- ✅ **Better Typography** dengan font weights dan sizing
---
### **Versi 4.0 - Blue Color Scheme Implementation**
#### Perubahan Tema:
- ✅ **Complete Blue Color Theme**
- Primary Color: Light Blue (#03A9F4)
- Secondary Color: Medium Blue (#0288D1)
- Background: Very Light Blue White (#F5FBFF)
- Accent: Sky Blue dan Navy Blue
- ✅ **Light Theme Colors**:
- Primary: Light Blue - warna utama aplikasi
- Container: Very Light Blue - untuk card backgrounds
- Surface: White - permukaan bersih
- ✅ **Dark Theme Colors**:
- Primary: Sky Blue - untuk dark mode
- Background: Very Dark Blue
- Surface: Deep Blue
- ✅ **Component Colors**:
- Button: Light Blue dengan shadow
- Input border: Light Blue saat focus
- Switch: Sky Blue theme
- Cards: Gradasi warna biru
---
## ✨ Fitur Aplikasi
### **1. Dual Unit System**
- 🌍 **Metric System**: Kilogram (kg) dan Centimeter (cm)
- 🇺🇸 **Imperial System**: Pounds (lbs) dan Inches (in)
- 🔄 Toggle switch untuk berpindah sistem
- 🔄 Auto-reset input saat ganti sistem
### **2. Input Validation**
#### Metric System:
- Tinggi: 50 - 300 cm
- Berat: 20 - 500 kg
#### Imperial System:
- Tinggi: 20 - 120 inches
- Berat: 44 - 1100 lbs
### **3. BMI Categories (WHO Standard)**
- **Underweight**: BMI < 18.5
- **Normal weight**: BMI 18.5 - 24.9
- **Overweight**: BMI 25.0 - 29.9
- **Obese**: BMI ≥ 30.0
### **4. Visual Features**
- 🎨 Gradient background dengan warna biru
- 💳 Card-based design untuk semua komponen
- 🌈 Color-coded results berdasarkan kategori
- 📊 BMI range reference dengan color indicators
- ✨ Shadow effects untuk depth visual
- 🔤 Modern typography dengan font weights
- 📱 Responsive dan scrollable layout
---
## 🛠️ Teknologi yang Digunakan
- **Kotlin** - Programming Language
- **Jetpack Compose** - Modern UI Toolkit
- **Material Design 3** - Design System
- **Android Studio** - IDE
---
## 📋 Struktur Kode
### **Data Class**
```kotlin
data class BMIResult(
val bmiValue: Double,
val category: String,
val isValid: Boolean = true,
val errorMessage: String = ""
)
```
### **Main Components**
1. **BMICalculatorLayout** - Main screen layout
2. **UnitSystemCard** - Toggle untuk sistem unit
3. **BMIInputField** - Custom input field dengan card design
4. **BMIResultCard** - Menampilkan hasil dengan visual menarik
5. **BMIRangeReference** - Referensi range BMI WHO
### **Key Functions**
- `calculateBMI()` - Menghitung BMI dengan validasi
- `validateBMIInput()` - Validasi input berdasarkan sistem unit
- `getBMICategory()` - Menentukan kategori BMI
- `getBMICategoryInfo()` - Memberikan deskripsi kategori
- `formatBMIResult()` - Format display angka BMI
---
## 🎨 Design Principles
1. **User-Friendly**: Interface yang mudah dipahami
2. **Visual Feedback**: Color-coding untuk hasil yang jelas
3. **Informative**: Deskripsi lengkap untuk setiap kategori
4. **Modern**: Design mengikuti Material Design 3
5. **Accessible**: Contrast yang baik dan text yang readable
6. **Responsive**: Layout adaptif untuk berbagai ukuran layar
---
## 🚀 Cara Menggunakan
1. Pilih sistem unit (Metric/Imperial) dengan toggle switch
2. Masukkan tinggi badan (cm atau inches)
3. Masukkan berat badan (kg atau lbs)
4. Tekan tombol "Hitung BMI"
5. Lihat hasil BMI dengan kategori dan deskripsi lengkap
---
## 📈 Future Improvements
- [ ] Menyimpan riwayat perhitungan BMI
- [ ] Grafik trend BMI dari waktu ke waktu
- [ ] Rekomendasi diet dan olahraga
- [ ] Export hasil ke PDF
- [ ] Multi-language support
- [ ] Dark mode toggle
- [ ] BMI ideal calculator berdasarkan usia dan gender
---

View File

@ -4,15 +4,25 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
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.text.font.FontWeight
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.unit.dp
import androidx.compose.ui.unit.sp
import com.example.tiptime.ui.theme.TipTimeTheme
import java.text.NumberFormat
@ -54,23 +64,93 @@ data class BMIResult(
* Composable untuk switch pemilihan unit sistem (USC/SI)
*/
@Composable
fun UnitSystemSwitchRow(
fun UnitSystemCard(
useUSC: Boolean,
onUseUSCChanged: (Boolean) -> Unit,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
Card(
modifier = modifier
.fillMaxWidth()
.shadow(4.dp, RoundedCornerShape(16.dp)),
shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.primaryContainer
)
) {
Text(text = "Use USC Units (lbs/inches)")
Switch(
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.End),
checked = useUSC,
onCheckedChange = onUseUSCChanged
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column {
Text(
text = "Sistem Unit",
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold
)
Text(
text = if (useUSC) "Imperial (lbs/inches)" else "Metric (kg/cm)",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.7f)
)
}
Switch(
checked = useUSC,
onCheckedChange = onUseUSCChanged,
colors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colorScheme.primary,
checkedTrackColor = MaterialTheme.colorScheme.primaryContainer
)
)
}
}
}
/**
* Custom Input Field dengan design yang lebih menarik
*/
@Composable
fun BMIInputField(
value: String,
onValueChange: (String) -> Unit,
label: String,
unit: String,
modifier: Modifier = Modifier
) {
Card(
modifier = modifier
.fillMaxWidth()
.shadow(2.dp, RoundedCornerShape(12.dp)),
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
)
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = label,
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary,
fontWeight = FontWeight.SemiBold
)
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField(
value = value,
onValueChange = onValueChange,
suffix = { Text(unit, fontWeight = FontWeight.Bold) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
modifier = Modifier.fillMaxWidth(),
singleLine = true,
shape = RoundedCornerShape(8.dp),
colors = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.primary,
unfocusedBorderColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f)
)
)
}
}
}
@ -84,130 +164,296 @@ fun BMICalculatorLayout() {
var useUSC by remember { mutableStateOf(false) }
var bmiResult by remember { mutableStateOf<BMIResult?>(null) }
Column(
Box(
modifier = Modifier
.padding(40.dp)
.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = "BMI Calculator",
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.padding(bottom = 16.dp)
)
// Unit System Switch
UnitSystemSwitchRow(
useUSC = useUSC,
onUseUSCChanged = { useUSC = it },
modifier = Modifier.padding(vertical = 8.dp)
)
// Height Input
OutlinedTextField(
value = heightInput,
onValueChange = { heightInput = it },
label = {
Text(if (useUSC) "Height (inches)" else "Height (meters)")
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
modifier = Modifier.fillMaxWidth(),
singleLine = true
)
// Weight Input
OutlinedTextField(
value = weightInput,
onValueChange = { weightInput = it },
label = {
Text(if (useUSC) "Weight (lbs)" else "Weight (kg)")
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
modifier = Modifier.fillMaxWidth(),
singleLine = true
)
// Calculate Button
Button(
onClick = {
val height = heightInput.toDoubleOrNull()
val weight = weightInput.toDoubleOrNull()
if (height != null && weight != null) {
bmiResult = calculateBMI(height, weight, useUSC)
} else {
bmiResult = BMIResult(
bmiValue = 0.0,
category = "",
isValid = false,
errorMessage = "Please enter valid numbers"
.fillMaxSize()
.background(
Brush.verticalGradient(
colors = listOf(
MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.3f),
MaterialTheme.colorScheme.background
)
}
},
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp)
) {
Text("Calculate BMI")
}
// Display Result
bmiResult?.let { result ->
Card(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
colors = CardDefaults.cardColors(
containerColor = if (result.isValid)
MaterialTheme.colorScheme.primaryContainer
else
MaterialTheme.colorScheme.errorContainer
)
)
) {
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(24.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// Header
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Column(
modifier = Modifier.padding(16.dp)
) {
if (result.isValid) {
Text(
text = "Your BMI: ${formatBMIResult(result)}",
style = MaterialTheme.typography.titleLarge
)
Text(
text = "Category: ${result.category}",
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.padding(top = 8.dp)
)
Text(
text = "💪 BMI Calculator",
style = MaterialTheme.typography.headlineLarge,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary
)
Text(
text = "Hitung Indeks Massa Tubuh Anda",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.6f)
)
}
// BMI Info
Spacer(modifier = Modifier.height(8.dp))
Text(
text = getBMICategoryInfo(result.category),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onPrimaryContainer
)
Spacer(modifier = Modifier.height(8.dp))
// Unit System Switch
UnitSystemCard(
useUSC = useUSC,
onUseUSCChanged = {
useUSC = it
// Reset input saat ganti sistem
heightInput = ""
weightInput = ""
bmiResult = null
}
)
// Height Input
BMIInputField(
value = heightInput,
onValueChange = { heightInput = it },
label = "Tinggi Badan",
unit = if (useUSC) "inches" else "cm"
)
// Weight Input
BMIInputField(
value = weightInput,
onValueChange = { weightInput = it },
label = "Berat Badan",
unit = if (useUSC) "lbs" else "kg"
)
// Calculate Button
Button(
onClick = {
val height = heightInput.toDoubleOrNull()
val weight = weightInput.toDoubleOrNull()
if (height != null && weight != null) {
bmiResult = calculateBMI(height, weight, useUSC)
} else {
Text(
text = "Error: ${result.errorMessage}",
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.error
bmiResult = BMIResult(
bmiValue = 0.0,
category = "",
isValid = false,
errorMessage = "Mohon masukkan angka yang valid"
)
}
},
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.shadow(8.dp, RoundedCornerShape(12.dp)),
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text(
"Hitung BMI",
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold
)
}
// Display Result
bmiResult?.let { result ->
BMIResultCard(result = result)
}
Spacer(modifier = Modifier.height(16.dp))
}
}
}
/**
* Card untuk menampilkan hasil BMI dengan design menarik
*/
@Composable
fun BMIResultCard(result: BMIResult) {
Card(
modifier = Modifier
.fillMaxWidth()
.shadow(8.dp, RoundedCornerShape(20.dp)),
shape = RoundedCornerShape(20.dp),
colors = CardDefaults.cardColors(
containerColor = if (result.isValid) {
when (result.category) {
"Underweight" -> Color(0xFFFFF3E0)
"Normal weight" -> Color(0xFFE8F5E9)
"Overweight" -> Color(0xFFFFF9C4)
"Obese" -> Color(0xFFFFEBEE)
else -> MaterialTheme.colorScheme.surface
}
} else {
MaterialTheme.colorScheme.errorContainer
}
)
) {
Column(
modifier = Modifier.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
if (result.isValid) {
// BMI Value
Text(
text = "BMI Anda",
style = MaterialTheme.typography.titleMedium,
color = Color.Gray
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = formatBMIResult(result),
style = MaterialTheme.typography.displayLarge,
fontWeight = FontWeight.Bold,
color = getBMICategoryColor(result.category)
)
Spacer(modifier = Modifier.height(16.dp))
// Category Badge
Surface(
shape = RoundedCornerShape(20.dp),
color = getBMICategoryColor(result.category).copy(alpha = 0.2f),
modifier = Modifier.padding(vertical = 8.dp)
) {
Text(
text = getCategoryEmoji(result.category) + " " + result.category,
modifier = Modifier.padding(horizontal = 20.dp, vertical = 10.dp),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
color = getBMICategoryColor(result.category)
)
}
Spacer(modifier = Modifier.height(12.dp))
Divider(color = Color.Gray.copy(alpha = 0.3f))
Spacer(modifier = Modifier.height(12.dp))
// BMI Info
Text(
text = getBMICategoryInfo(result.category),
style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center,
color = Color.DarkGray
)
// BMI Range Reference
Spacer(modifier = Modifier.height(16.dp))
BMIRangeReference()
} else {
Text(
text = "⚠️ Error",
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.error
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = result.errorMessage,
style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.error
)
}
}
}
}
/**
* Reference BMI Range
*/
@Composable
fun BMIRangeReference() {
Card(
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(
containerColor = Color.White.copy(alpha = 0.7f)
)
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "Referensi BMI (WHO)",
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(bottom = 8.dp)
)
BMIRangeItem("< 18.5", "Underweight", Color(0xFFFF9800))
BMIRangeItem("18.5 - 24.9", "Normal", Color(0xFF4CAF50))
BMIRangeItem("25.0 - 29.9", "Overweight", Color(0xFFFFEB3B))
BMIRangeItem("≥ 30.0", "Obese", Color(0xFFF44336))
}
}
}
@Composable
fun BMIRangeItem(range: String, category: String, color: Color) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier
.size(12.dp)
.background(color, RoundedCornerShape(6.dp))
)
Spacer(modifier = Modifier.width(12.dp))
Text(
text = range,
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Medium,
modifier = Modifier.weight(1f)
)
Text(
text = category,
style = MaterialTheme.typography.bodySmall,
color = Color.Gray
)
}
}
/**
* Helper functions
*/
fun getBMICategoryColor(category: String): Color {
return when (category) {
"Underweight" -> Color(0xFFFF9800)
"Normal weight" -> Color(0xFF4CAF50)
"Overweight" -> Color(0xFFFFEB3B)
"Obese" -> Color(0xFFF44336)
else -> Color.Gray
}
}
fun getCategoryEmoji(category: String): String {
return when (category) {
"Underweight" -> "⚠️"
"Normal weight" -> ""
"Overweight" -> ""
"Obese" -> "🔴"
else -> ""
}
}
/**
* Memvalidasi input tinggi dan berat badan
*/
fun validateBMIInput(height: Double, weight: Double, useUSC: Boolean): Pair<Boolean, String> {
if (!useUSC) {
// SI Units validation
// SI Units validation (cm dan kg)
when {
height <= 0 -> return Pair(false, "Tinggi harus lebih dari 0")
height < 0.5 -> return Pair(false, "Tinggi terlalu rendah (minimum 0.5m)")
height > 3.0 -> return Pair(false, "Tinggi tidak wajar (maksimum 3.0m)")
height < 50 -> return Pair(false, "Tinggi terlalu rendah (minimum 50cm)")
height > 300 -> return Pair(false, "Tinggi tidak wajar (maksimum 300cm)")
weight <= 0 -> return Pair(false, "Berat harus lebih dari 0")
weight < 20 -> return Pair(false, "Berat terlalu rendah (minimum 20kg)")
weight > 500 -> return Pair(false, "Berat tidak wajar (maksimum 500kg)")
@ -231,7 +477,7 @@ fun validateBMIInput(height: Double, weight: Double, useUSC: Boolean): Pair<Bool
* Menghitung BMI berdasarkan sistem unit yang dipilih
*
* Formula USC: BMI = 703 × (weight in lbs / height² in inches)
* Formula SI: BMI = weight in kg / height² in meters
* Formula SI: BMI = weight in kg / (height in cm / 100)²
*/
fun calculateBMI(height: Double, weight: Double, useUSC: Boolean): BMIResult {
// Validasi input
@ -251,8 +497,10 @@ fun calculateBMI(height: Double, weight: Double, useUSC: Boolean): BMIResult {
// USC: BMI = 703 × (weight in lbs / height² in inches)
703.0 * (weight / (height * height))
} else {
// SI: BMI = weight in kg / height² in meters
weight / (height * height)
// SI: BMI = weight in kg / (height in meters)²
// Konversi cm ke meter
val heightInMeters = height / 100.0
weight / (heightInMeters * heightInMeters)
}
// Tentukan kategori BMI
@ -283,10 +531,10 @@ fun getBMICategory(bmi: Double): String {
*/
fun getBMICategoryInfo(category: String): String {
return when (category) {
"Underweight" -> "BMI < 18.5: Berat badan kurang"
"Normal weight" -> "BMI 18.5-24.9: Berat badan ideal"
"Overweight" -> "BMI 25.0-29.9: Kelebihan berat badan"
"Obese" -> "BMI ≥ 30.0: Obesitas"
"Underweight" -> "Berat badan Anda kurang dari ideal. Pertimbangkan untuk meningkatkan asupan nutrisi."
"Normal weight" -> "Selamat! Berat badan Anda dalam rentang ideal dan sehat."
"Overweight" -> "Berat badan Anda sedikit berlebih. Pertimbangkan pola makan sehat dan olahraga teratur."
"Obese" -> "Berat badan Anda berlebih. Konsultasikan dengan dokter untuk program penurunan berat badan."
else -> ""
}
}

View File

@ -17,62 +17,64 @@ package com.example.tiptime.ui.theme
import androidx.compose.ui.graphics.Color
val md_theme_light_primary = Color(0xFF984061)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFFFD9E2)
val md_theme_light_onPrimaryContainer = Color(0xFF3E001D)
val md_theme_light_secondary = Color(0xFF754B9C)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFF1DBFF)
val md_theme_light_onSecondaryContainer = Color(0xFF2D0050)
val md_theme_light_tertiary = Color(0xFF984060)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFFFD9E2)
val md_theme_light_onTertiaryContainer = Color(0xFF3E001D)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFFAFCFF)
val md_theme_light_onBackground = Color(0xFF001F2A)
val md_theme_light_surface = Color(0xFFFAFCFF)
val md_theme_light_onSurface = Color(0xFF001F2A)
val md_theme_light_surfaceVariant = Color(0xFFF2DDE2)
val md_theme_light_onSurfaceVariant = Color(0xFF514347)
val md_theme_light_outline = Color(0xFF837377)
val md_theme_light_inverseOnSurface = Color(0xFFE1F4FF)
val md_theme_light_inverseSurface = Color(0xFF003547)
val md_theme_light_inversePrimary = Color(0xFFFFB0C8)
val md_theme_light_surfaceTint = Color(0xFF984061)
val md_theme_light_outlineVariant = Color(0xFFD5C2C6)
val md_theme_light_scrim = Color(0xFF000000)
// Light Theme - Light Blue Dominant
val md_theme_light_primary = Color(0xFF03A9F4) // Light Blue
val md_theme_light_onPrimary = Color(0xFFFFFFFF) // White
val md_theme_light_primaryContainer = Color(0xFFB3E5FC) // Very Light Blue
val md_theme_light_onPrimaryContainer = Color(0xFF01579B) // Navy Blue
val md_theme_light_secondary = Color(0xFF0288D1) // Medium Blue
val md_theme_light_onSecondary = Color(0xFFFFFFFF) // White
val md_theme_light_secondaryContainer = Color(0xFFE1F5FE) // Pale Blue
val md_theme_light_onSecondaryContainer = Color(0xFF014A7F) // Dark Blue
val md_theme_light_tertiary = Color(0xFF0277BD) // Blue
val md_theme_light_onTertiary = Color(0xFFFFFFFF) // White
val md_theme_light_tertiaryContainer = Color(0xFFB3E5FC) // Very Light Blue
val md_theme_light_onTertiaryContainer = Color(0xFF003D5B) // Deep Blue
val md_theme_light_error = Color(0xFFBA1A1A) // Red
val md_theme_light_errorContainer = Color(0xFFFFDAD6) // Light Red
val md_theme_light_onError = Color(0xFFFFFFFF) // White
val md_theme_light_onErrorContainer = Color(0xFF410002) // Dark Red
val md_theme_light_background = Color(0xFFF5FBFF) // Very Light Blue White
val md_theme_light_onBackground = Color(0xFF001F2A) // Dark Blue Gray
val md_theme_light_surface = Color(0xFFFFFFFF) // White
val md_theme_light_onSurface = Color(0xFF001F2A) // Dark Blue Gray
val md_theme_light_surfaceVariant = Color(0xFFE3F2FD) // Pale Blue
val md_theme_light_onSurfaceVariant = Color(0xFF455A64) // Blue Gray
val md_theme_light_outline = Color(0xFF78909C) // Medium Blue Gray
val md_theme_light_inverseOnSurface = Color(0xFFE1F4FF) // Very Light Blue
val md_theme_light_inverseSurface = Color(0xFF003547) // Deep Blue
val md_theme_light_inversePrimary = Color(0xFF81D4FA) // Sky Blue
val md_theme_light_surfaceTint = Color(0xFF03A9F4) // Light Blue
val md_theme_light_outlineVariant = Color(0xFFB0BEC5) // Light Blue Gray
val md_theme_light_scrim = Color(0xFF000000) // Black
val md_theme_dark_primary = Color(0xFFFFB0C8)
val md_theme_dark_onPrimary = Color(0xFF5E1133)
val md_theme_dark_primaryContainer = Color(0xFF7B2949)
val md_theme_dark_onPrimaryContainer = Color(0xFFFFD9E2)
val md_theme_dark_secondary = Color(0xFFDEB7FF)
val md_theme_dark_onSecondary = Color(0xFF44196A)
val md_theme_dark_secondaryContainer = Color(0xFF5C3382)
val md_theme_dark_onSecondaryContainer = Color(0xFFF1DBFF)
val md_theme_dark_tertiary = Color(0xFFFFB1C7)
val md_theme_dark_onTertiary = Color(0xFF5E1132)
val md_theme_dark_tertiaryContainer = Color(0xFF7B2948)
val md_theme_dark_onTertiaryContainer = Color(0xFFFFD9E2)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF001F2A)
val md_theme_dark_onBackground = Color(0xFFBFE9FF)
val md_theme_dark_surface = Color(0xFF001F2A)
val md_theme_dark_onSurface = Color(0xFFBFE9FF)
val md_theme_dark_surfaceVariant = Color(0xFF514347)
val md_theme_dark_onSurfaceVariant = Color(0xFFD5C2C6)
val md_theme_dark_outline = Color(0xFF9E8C90)
val md_theme_dark_inverseOnSurface = Color(0xFF001F2A)
val md_theme_dark_inverseSurface = Color(0xFFBFE9FF)
val md_theme_dark_inversePrimary = Color(0xFF984061)
val md_theme_dark_surfaceTint = Color(0xFFFFB0C8)
val md_theme_dark_outlineVariant = Color(0xFF514347)
val md_theme_dark_scrim = Color(0xFF000000)
// Dark Theme - Dark Blue Dominant
val md_theme_dark_primary = Color(0xFF81D4FA) // Sky Blue
val md_theme_dark_onPrimary = Color(0xFF003D5B) // Deep Blue
val md_theme_dark_primaryContainer = Color(0xFF01579B) // Navy Blue
val md_theme_dark_onPrimaryContainer = Color(0xFFB3E5FC) // Very Light Blue
val md_theme_dark_secondary = Color(0xFF64B5F6) // Light Blue
val md_theme_dark_onSecondary = Color(0xFF014A7F) // Dark Blue
val md_theme_dark_secondaryContainer = Color(0xFF0277BD) // Blue
val md_theme_dark_onSecondaryContainer = Color(0xFFE1F5FE) // Pale Blue
val md_theme_dark_tertiary = Color(0xFF90CAF9) // Soft Blue
val md_theme_dark_onTertiary = Color(0xFF003D5B) // Deep Blue
val md_theme_dark_tertiaryContainer = Color(0xFF01579B) // Navy Blue
val md_theme_dark_onTertiaryContainer = Color(0xFFB3E5FC) // Very Light Blue
val md_theme_dark_error = Color(0xFFFFB4AB) // Light Red
val md_theme_dark_errorContainer = Color(0xFF93000A) // Dark Red
val md_theme_dark_onError = Color(0xFF690005) // Deep Red
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6) // Light Red
val md_theme_dark_background = Color(0xFF001F2A) // Very Dark Blue
val md_theme_dark_onBackground = Color(0xFFB3E5FC) // Very Light Blue
val md_theme_dark_surface = Color(0xFF003547) // Deep Blue
val md_theme_dark_onSurface = Color(0xFFE1F5FE) // Pale Blue
val md_theme_dark_surfaceVariant = Color(0xFF01579B) // Navy Blue
val md_theme_dark_onSurfaceVariant = Color(0xFFB3E5FC) // Very Light Blue
val md_theme_dark_outline = Color(0xFF607D8B) // Blue Gray
val md_theme_dark_inverseOnSurface = Color(0xFF001F2A) // Very Dark Blue
val md_theme_dark_inverseSurface = Color(0xFFB3E5FC) // Very Light Blue
val md_theme_dark_inversePrimary = Color(0xFF0277BD) // Blue
val md_theme_dark_surfaceTint = Color(0xFF81D4FA) // Sky Blue
val md_theme_dark_outlineVariant = Color(0xFF455A64) // Blue Gray
val md_theme_dark_scrim = Color(0xFF000000) // Black