import 'dart:ui';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Color seedColor = Colors.blueAccent;
void updateColor(Color color) {
setState(() => seedColor = color);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(seedColor: seedColor),
),
home: HomePage(onColorChange: updateColor, themeColor: seedColor),
);
}
}
class HomePage extends StatefulWidget {
final Function(Color) onColorChange;
final Color themeColor;
const HomePage({super.key, required this.onColorChange, required this.themeColor});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
final ageController = TextEditingController();
final heightController = TextEditingController();
final weightController = TextEditingController();
String gender = "Pria";
double? bmi;
double animatedBMI = 0;
String message = "";
Color resultColor = Colors.transparent;
List<String> history = [];
late AnimationController controller;
late Animation<double> animation;
@override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 800),
);
}
void calculateBMI() {
final h = double.tryParse(heightController.text);
final w = double.tryParse(weightController.text);
final age = int.tryParse(ageController.text);
if (h == null || w == null || age == null || h <= 0 || w <= 0 || age <= 0) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Isi usia, tinggi & berat yang valid ya 👀")),
);
return;
}
final result = w / ((h / 100) * (h / 100));
animation = Tween<double>(begin: 0, end: result).animate(
CurvedAnimation(parent: controller, curve: Curves.easeOut),
)
..addListener(() {
setState(() {
animatedBMI = animation.value;
});
});
controller.forward(from: 0);
setState(() {
bmi = result;
resultColor = getColor(result);
message = getMessage(result, age);
history.insert(0, "BMI ${result.toStringAsFixed(2)} • $gender • $age th");
});
}
String getMessage(double bmi, int age) {
String ageNote;
if (age < 18) {
ageNote = "Nutrisi penting di usia pertumbuhan 🍎";
} else if (age < 40) {
ageNote = "Jaga gaya hidup aktif ⚡";
} else {
ageNote = "Fokus kesehatan & metabolisme ❤️";
}
if (bmi < 18.5) return "Kurus 🌬️ $ageNote";
if (bmi < 24.9) return "Ideal 🎯 $ageNote";
if (bmi < 29.9) return "Berlebih 💪 $ageNote";
return "Obesitas 🔥 $ageNote";
}
Color getColor(double bmi) {
if (bmi < 18.5) return Colors.cyan;
if (bmi < 24.9) return Colors.green;
if (bmi < 29.9) return Colors.orange;
return Colors.red;
}
Widget glassCard({required Widget child}) {
return ClipRRect(
borderRadius: BorderRadius.circular(25),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 12, sigmaY: 12),
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(25),
border: Border.all(color: Colors.white.withOpacity(0.25)),
),
child: child,
),
),
);
}
Widget genderButton(String label, IconData icon, Color glow) {
bool selected = gender == label;
return Expanded(
child: GestureDetector(
onTap: () => setState(() => gender = label),
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
margin: const EdgeInsets.symmetric(horizontal: 8),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: selected ? glow.withOpacity(0.25) : Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: selected ? glow : Colors.white24),
),
child: Column(
children: [
Icon(icon, size: 38, color: selected ? glow : Colors.white),
const SizedBox(height: 6),
Text(label, style: TextStyle(color: selected ? glow : Colors.white)),
],
),
),
),
);
}
InputDecoration inputStyle(String label, IconData icon) {
return InputDecoration(
labelText: label,
labelStyle: const TextStyle(color: Colors.white),
prefixIcon: Icon(icon, color: Colors.white),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: const BorderSide(color: Colors.white54),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: BorderSide(color: widget.themeColor),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: widget.themeColor.withOpacity(0.9),
appBar: AppBar(
backgroundColor: widget.themeColor,
elevation: 0,
centerTitle: true,
title: const Text(
"Be Healthy With BMI",
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
),
iconTheme: const IconThemeData(color: Colors.white),
actions: [
PopupMenuButton<Color>(
icon: const Icon(Icons.palette_outlined, color: Colors.white),
onSelected: widget.onColorChange,
itemBuilder: (context) => const [
PopupMenuItem(value: Colors.blueAccent, child: Text("Blue")),
PopupMenuItem(value: Colors.pinkAccent, child: Text("Pink")),
PopupMenuItem(value: Colors.greenAccent, child: Text("Green")),
PopupMenuItem(value: Colors.deepPurpleAccent, child: Text("Purple")),
],
)
],
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20),
anak : Kolom (
anak-anak : [
Kartu kaca (
anak : Kolom (
anak-anak : [
const Text("Hitung BMI Kamu",
gaya : TextStyle ( ukuran font : 22 , warna : Colors . putih )),
const SizeBox ( tinggi : 20 ),
Baris (
anak-anak : [
genderButton ( "Pria" , Icons . male , Colors . white ),
genderButton ( "Wanita" , Icons . female , Colors . pink ),
],
),
const SizeBox ( tinggi : 20 ),
Kolom Teks (
pengontrol : pengontrol usia ,
keyboardType : TextInputType . number ,
gaya : const TextStyle ( warna : Colors . putih ),
decoration: inputStyle("Usia (tahun)", Icons.cake),
),
const SizeBox ( tinggi : 15 ),
Kolom Teks (
pengontrol : pengontrol ketinggian ,
keyboardType : TextInputType . number ,
gaya : const TextStyle ( warna : Colors . putih ),
dekorasi : inputStyle ( "Tinggi (cm)" , Icons . height ),
),
const SizeBox ( tinggi : 15 ),
Kolom Teks (
pengontrol : pengontrol berat ,
keyboardType : TextInputType . number ,
gaya : const TextStyle ( warna : Colors . putih ),
dekorasi : inputStyle ( "Berat (kg)" , Icons . monitor_weight ),
),
const SizeBox ( tinggi : 20 ),
Tombol Elevated (
onPressed : calculateBMI ,
gaya : Tombol yang Ditinggikan . gayaDari (
backgroundColor : Colors . white ,
foregroundColor : widget . themeColor ,
padding : const EdgeInsets . symmetric ( horizontal : 40 , vertical : 15 ),
bentuk : RoundedRectangleBorder ( borderRadius : BorderRadius . circular ( 20 )),
),
child: const Text("HITUNG BMI"),
),
jika ( bmi != null ) ...[
const SizeBox ( tinggi : 25 ),
Teks ( animatedBMI.toStringAsFixed ( 2 ) ,
gaya : Gaya Teks (
Ukuran Huruf : 42
warna : resultColor ,
FontWeight : FontWeight . bold )),
const SizeBox ( tinggi : 10 ),
Indikator Kemajuan Linier (
nilai : ( bmi ! / 40 ). clamp ( 0 , 1 ),
minTinggi : 10
backgroundColor : Colors . white24 ,
warna : resultColor ,
),
const SizeBox ( tinggi : 10 ),
Teks ( pesan ,
Perataan teks : Perataan Teks . tengah ,
gaya : const TextStyle ( warna : Colors . putih )),
]
],
),
),
jika ( history.isNotEmpty ) ... [
const SizeBox ( tinggi : 25 ),
Kartu kaca (
anak : Kolom (
crossAxisAlignment : CrossAxisAlignment . start ,
anak-anak : [
const Text("Riwayat BMI",
gaya : Gaya Teks (
warna : Warna . putih ,
Ukuran Huruf : 18
FontWeight : FontWeight . bold )),
const SizeBox ( tinggi : 10 ),
... sejarah . ambil ( 5 ). peta (( e ) => Teks (
Dan ,
gaya : const TextStyle ( warna : Colors . putih70 ),
))
],
),
)
]
],
),
),
);
}
}
Komentar
Posting Komentar