import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import '../models/home_model.dart'; class AddWeightDialog extends StatefulWidget { final String personName; /// Letztes bekanntes Gewicht dieser Person – wird für weightChange benötigt. final double lastWeight; /// Wenn gesetzt → Edit-Modus: Felder werden vorausgefüllt. final WeightModel? existingEntry; const AddWeightDialog({ super.key, required this.personName, required this.lastWeight, this.existingEntry, }); /// Öffnet den Dialog zum Hinzufügen eines neuen Eintrags. static Future show({ required String personName, required double lastWeight, }) { return Get.dialog( AddWeightDialog(personName: personName, lastWeight: lastWeight), barrierColor: Colors.black.withValues(alpha: 0.55), ); } /// Öffnet den Dialog zum Bearbeiten eines vorhandenen Eintrags. /// [previousWeight] = Gewicht des Eintrags VOR dem zu bearbeitenden. static Future showEdit({ required WeightModel entry, required double previousWeight, }) { return Get.dialog( AddWeightDialog( personName: entry.name, lastWeight: previousWeight, existingEntry: entry, ), barrierColor: Colors.black.withValues(alpha: 0.55), ); } @override State createState() => _AddWeightDialogState(); } class _AddWeightDialogState extends State with SingleTickerProviderStateMixin { final _formKey = GlobalKey(); final _weightController = TextEditingController(); late final TextEditingController _nameController; DateTime _selectedDate = DateTime.now(); double? _parsedWeight; late AnimationController _anim; late Animation _fadeScale; @override void initState() { super.initState(); _nameController = TextEditingController(text: widget.personName); // Edit-Modus: Felder vorausfüllen if (widget.existingEntry != null) { final e = widget.existingEntry!; _weightController.text = e.weight.toString().replaceAll('.', ','); _parsedWeight = e.weight; _selectedDate = e.date; } _anim = AnimationController( vsync: this, duration: const Duration(milliseconds: 220), ); _fadeScale = CurvedAnimation(parent: _anim, curve: Curves.easeOutBack); _anim.forward(); } @override void dispose() { _weightController.dispose(); _nameController.dispose(); _anim.dispose(); super.dispose(); } double get _weightChange { if (_parsedWeight == null || widget.lastWeight == 0) return 0; return double.parse( (_parsedWeight! - widget.lastWeight).toStringAsFixed(2), ); } Color get _changeColor { if (_weightChange < 0) return const Color(0xFF4FFFB0); if (_weightChange > 0) return const Color(0xFFFF6B6B); return Colors.white54; } String get _changeLabel { if (_parsedWeight == null) return ''; if (widget.lastWeight == 0) return 'Erster Eintrag'; final sign = _weightChange > 0 ? '+' : ''; return '$sign${_weightChange.toStringAsFixed(1)} kg'; } Future _pickDate() async { final picked = await showDatePicker( context: context, initialDate: _selectedDate, firstDate: DateTime(2000), lastDate: DateTime(2100), builder: (context, child) => Theme( data: Theme.of(context).copyWith( colorScheme: const ColorScheme.dark( primary: Color(0xFF7B9FFF), onSurface: Colors.white, surface: Color(0xFF1A2035), ), ), child: child!, ), ); if (picked != null) setState(() => _selectedDate = picked); } void _submit() { if (!_formKey.currentState!.validate()) return; final name = _nameController.text.trim(); final entry = WeightModel( // Im Edit-Modus dieselbe ID behalten id: widget.existingEntry?.id ?? DateTime.now().millisecondsSinceEpoch.toString(), name: name, weight: _parsedWeight!, date: _selectedDate, weightChange: widget.lastWeight == 0 ? 0 : _weightChange, ); Navigator.of(context).pop(entry); } @override Widget build(BuildContext context) { return ScaleTransition( scale: _fadeScale, child: FadeTransition( opacity: _anim, child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 460), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 40), child: ClipRRect( borderRadius: BorderRadius.circular(24), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 24, sigmaY: 24), child: Material( type: MaterialType.transparency, child: Container( decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(24), border: Border.all( color: Colors.white.withValues(alpha: 0.2), width: 1, ), ), child: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // ── Titelzeile ────────────────────────── _DialogHeader( personName: widget.personName, isEdit: widget.existingEntry != null, ), Padding( padding: const EdgeInsets.fromLTRB(20, 20, 20, 0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Name _GlassField( label: 'Name', icon: Icons.person_outline_rounded, child: TextFormField( controller: _nameController, style: const TextStyle( color: Colors.white, fontSize: 16, ), decoration: _inputDecoration('z. B. Joe'), validator: (v) => (v == null || v.trim().isEmpty) ? 'Name eingeben' : null, ), ), const SizedBox(height: 14), // Gewicht _GlassField( label: 'Gewicht', icon: Icons.monitor_weight_outlined, child: TextFormField( controller: _weightController, keyboardType: const TextInputType.numberWithOptions( decimal: true, ), inputFormatters: [ FilteringTextInputFormatter.allow( RegExp(r'^\d*[.,]?\d*'), ), ], style: const TextStyle( color: Colors.white, fontSize: 16, ), decoration: _inputDecoration( 'z. B. 72,5', ), onChanged: (v) { final parsed = double.tryParse( v.replaceAll(',', '.'), ); setState(() => _parsedWeight = parsed); }, validator: (v) { if (v == null || v.isEmpty) { return 'Gewicht eingeben'; } if (double.tryParse( v.replaceAll(',', '.'), ) == null) { return 'Ungültige Zahl'; } return null; }, ), ), const SizedBox(height: 14), // Datum _GlassField( label: 'Datum', icon: Icons.calendar_today_outlined, child: InkWell( onTap: _pickDate, borderRadius: BorderRadius.circular(10), child: InputDecorator( decoration: _inputDecoration(''), child: Text( DateFormat( 'dd.MM.yyyy', ).format(_selectedDate), style: const TextStyle( color: Colors.white, fontSize: 16, ), ), ), ), ), const SizedBox(height: 20), // Vorschau weightChange if (_parsedWeight != null) _ChangePreview( label: _changeLabel, color: _changeColor, lastWeight: widget.lastWeight, ), if (_parsedWeight != null) const SizedBox(height: 20), // Buttons Row( children: [ Expanded( child: _GlassButton( label: 'Abbrechen', onPressed: () => Navigator.of(context).pop(), primary: false, ), ), const SizedBox(width: 12), Expanded( child: _GlassButton( label: widget.existingEntry != null ? 'Speichern' : 'Hinzufügen', onPressed: _submit, primary: true, ), ), ], ), const SizedBox(height: 20), ], ), ), ], ), ), // Form ), // Container ), // Material ), // BackdropFilter ), // ClipRRect ), // Padding ), // ConstrainedBox ), // Center ), // FadeTransition ); // ScaleTransition } InputDecoration _inputDecoration(String hint) => InputDecoration( hintText: hint, hintStyle: TextStyle( color: Colors.white.withValues(alpha: 0.35), fontSize: 14, ), filled: true, fillColor: Colors.white.withValues(alpha: 0.07), contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Colors.white.withValues(alpha: 0.2)), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Colors.white.withValues(alpha: 0.2)), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide(color: Color(0xFF7B9FFF), width: 1.5), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide(color: Color(0xFFFF6B6B), width: 1.2), ), focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide(color: Color(0xFFFF6B6B), width: 1.5), ), errorStyle: const TextStyle(color: Color(0xFFFF6B6B), fontSize: 11), ); } // ───────────────────────────────────────────────────────────────────────────── // Hilfs-Widgets // ───────────────────────────────────────────────────────────────────────────── class _DialogHeader extends StatelessWidget { final String personName; final bool isEdit; const _DialogHeader({required this.personName, this.isEdit = false}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.fromLTRB(20, 18, 12, 14), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.06), border: Border( bottom: BorderSide(color: Colors.white.withValues(alpha: 0.12)), ), ), child: Row( children: [ Icon( isEdit ? Icons.edit_outlined : Icons.add_circle_outline_rounded, color: const Color(0xFF7B9FFF), size: 22, ), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( isEdit ? 'Eintrag bearbeiten' : 'Gewicht hinzufügen', style: const TextStyle( color: Colors.white, fontSize: 17, fontWeight: FontWeight.w700, letterSpacing: 0.3, ), ), Text( personName, style: TextStyle( color: Colors.white.withValues(alpha: 0.55), fontSize: 13, ), ), ], ), ), IconButton( onPressed: () => Navigator.of(context).pop(), icon: Icon( Icons.close_rounded, color: Colors.white.withValues(alpha: 0.5), size: 20, ), splashRadius: 18, ), ], ), ); } } class _GlassField extends StatelessWidget { final String label; final IconData icon; final Widget child; const _GlassField({ required this.label, required this.icon, required this.child, }); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(icon, color: Colors.white.withValues(alpha: 0.5), size: 14), const SizedBox(width: 6), Text( label, style: TextStyle( color: Colors.white.withValues(alpha: 0.6), fontSize: 12, fontWeight: FontWeight.w600, letterSpacing: 0.8, ), ), ], ), const SizedBox(height: 6), child, ], ); } } class _ChangePreview extends StatelessWidget { final String label; final Color color; final double lastWeight; const _ChangePreview({ required this.label, required this.color, required this.lastWeight, }); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), decoration: BoxDecoration( color: color.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(10), border: Border.all(color: color.withValues(alpha: 0.35)), ), child: Row( children: [ Icon(Icons.swap_vert_rounded, color: color, size: 18), const SizedBox(width: 8), Text( lastWeight == 0 ? label : 'Änderung zum letzten Eintrag: ', style: TextStyle( color: Colors.white.withValues(alpha: 0.7), fontSize: 13, ), ), if (lastWeight != 0) Text( label, style: TextStyle( color: color, fontSize: 13, fontWeight: FontWeight.w700, ), ), ], ), ); } } class _GlassButton extends StatelessWidget { final String label; final VoidCallback onPressed; final bool primary; const _GlassButton({ required this.label, required this.onPressed, required this.primary, }); @override Widget build(BuildContext context) { return Material( color: Colors.transparent, child: InkWell( onTap: onPressed, borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.symmetric(vertical: 13), decoration: BoxDecoration( color: primary ? const Color(0xFF7B9FFF).withValues(alpha: 0.25) : Colors.white.withValues(alpha: 0.07), borderRadius: BorderRadius.circular(12), border: Border.all( color: primary ? const Color(0xFF7B9FFF).withValues(alpha: 0.6) : Colors.white.withValues(alpha: 0.15), width: 1, ), ), alignment: Alignment.center, child: Text( label, style: TextStyle( color: primary ? const Color(0xFF7B9FFF) : Colors.white60, fontWeight: FontWeight.w700, fontSize: 14, letterSpacing: 0.3, ), ), ), ), ); } }