import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../controllers/detail_controller.dart'; import '../models/filament_model.dart'; class DetailPage extends GetView { static const String namedRoute = '/detail-page'; const DetailPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( extendBodyBehindAppBar: true, backgroundColor: const Color(0xFF0D0F14), body: PopScope( // Beim System-Zurück das aktuelle Filament zurückgeben canPop: false, onPopInvokedWithResult: (_, _) => Get.back(result: controller.filament.value), child: Obx(() { if (controller.isLoading.value) { return const Center(child: CircularProgressIndicator()); } final f = controller.filament.value; return Stack( children: [ // ── Hintergrund-Gradient ────────────────────────────── Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color(0xFF0D0F14), Color(0xFF141824), Color(0xFF1A2035), Color(0xFF0F1520), ], stops: [0.0, 0.35, 0.65, 1.0], ), ), ), // ── Farbiger Glow oben ──────────────────────────────── Positioned( top: -80, left: -60, child: Container( width: 280, height: 280, decoration: BoxDecoration( shape: BoxShape.circle, color: _parseColor(f.color).withValues(alpha: 0.12), ), ), ), // ── Content ────────────────────────────────────────── SafeArea( child: CustomScrollView( slivers: [ // ── App Bar ─────────────────────────────────── SliverAppBar( backgroundColor: Colors.transparent, surfaceTintColor: Colors.transparent, elevation: 0, pinned: true, leading: IconButton( icon: const Icon( Icons.arrow_back_ios_new_rounded, color: Colors.white, ), // Aktuelles Filament beim Zurücknavigieren übergeben onPressed: () => Get.back(result: controller.filament.value), ), title: Obx( () => Text( controller.filament.value.name, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ), ), ), actions: [ IconButton( icon: const Icon( Icons.edit_outlined, color: Colors.white70, ), onPressed: controller.openEditDialog, tooltip: 'Bearbeiten', ), IconButton( icon: const Icon( Icons.delete_outline_rounded, color: Color(0xFFFF6B6B), ), onPressed: controller.deleteFilament, tooltip: 'Löschen', ), const SizedBox(width: 8), ], ), SliverPadding( padding: const EdgeInsets.fromLTRB(16, 8, 16, 32), sliver: SliverList( delegate: SliverChildListDelegate([ // ── Hero-Header ────────────────────────── _HeroHeader(filament: f), const SizedBox(height: 20), // ── Gewicht-Karte ──────────────────────── _WeightCard(controller: controller), const SizedBox(height: 16), // ── Druck-Parameter ────────────────────── _PrintParamsCard(filament: f), const SizedBox(height: 16), // ── Kauf-Info ──────────────────────────── _PurchaseCard(filament: f), const SizedBox(height: 16), // ── Notizen ────────────────────────────── if (f.notes != null && f.notes!.isNotEmpty) _NotesCard(notes: f.notes!), ]), ), ), ], ), ), ], ); }), ), // PopScope ); } Color _parseColor(String colorStr) { final known = { 'rot': Colors.red, 'blau': Colors.blue, 'grün': Colors.green, 'grau': Colors.grey, 'schwarz': Colors.black, 'weiß': Colors.white, 'gelb': Colors.yellow, 'orange': Colors.orange, 'lila': Colors.purple, 'pink': Colors.pink, 'braun': Colors.brown, }; final lower = colorStr.toLowerCase(); if (known.containsKey(lower)) return known[lower]!; final hex = colorStr.replaceAll('#', ''); if (hex.length == 6) { final v = int.tryParse('FF$hex', radix: 16); if (v != null) return Color(v); } return const Color(0xFF7B9FFF); } } // ───────────────────────────────────────────────────────────────────────────── // Hero Header // ───────────────────────────────────────────────────────────────────────────── class _HeroHeader extends StatelessWidget { final FilamentModel filament; const _HeroHeader({required this.filament}); Color get _color { final known = { 'rot': Colors.red, 'blau': Colors.blue, 'grün': Colors.green, 'grau': Colors.grey, 'schwarz': Colors.black, 'weiß': Colors.white, 'gelb': Colors.yellow, 'orange': Colors.orange, 'lila': Colors.purple, 'pink': Colors.pink, 'braun': Colors.brown, }; final lower = filament.color.toLowerCase(); if (known.containsKey(lower)) return known[lower]!; final hex = filament.color.replaceAll('#', ''); if (hex.length == 6) { final v = int.tryParse('FF$hex', radix: 16); if (v != null) return Color(v); } return const Color(0xFF7B9FFF); } @override Widget build(BuildContext context) { return ClipRRect( borderRadius: BorderRadius.circular(20), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 16, sigmaY: 16), child: Container( decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.07), borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.white.withValues(alpha: 0.15)), ), padding: const EdgeInsets.all(20), child: Row( children: [ // Großer Farb-Kreis Container( width: 64, height: 64, decoration: BoxDecoration( shape: BoxShape.circle, color: _color, boxShadow: [ BoxShadow( color: _color.withValues(alpha: 0.45), blurRadius: 20, spreadRadius: 2, ), ], border: Border.all( color: Colors.white.withValues(alpha: 0.3), width: 2, ), ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( filament.name, style: const TextStyle( color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Row( children: [ _Badge( label: filament.type, color: const Color(0xFF7B9FFF), ), const SizedBox(width: 8), _Badge(label: filament.color, color: _color), ], ), const SizedBox(height: 4), Text( filament.manufacturer, style: TextStyle( color: Colors.white.withValues(alpha: 0.5), fontSize: 13, ), ), ], ), ), ], ), ), ), ); } } // ───────────────────────────────────────────────────────────────────────────── // Gewicht-Karte mit interaktivem Slider // ───────────────────────────────────────────────────────────────────────────── class _WeightCard extends StatefulWidget { final DetailController controller; const _WeightCard({required this.controller}); @override State<_WeightCard> createState() => _WeightCardState(); } class _WeightCardState extends State<_WeightCard> { late double _sliderValue; @override void initState() { super.initState(); _sliderValue = widget.controller.filament.value.weightUsed; } double get _remaining => (widget.controller.filament.value.weight - _sliderValue).clamp( 0, widget.controller.filament.value.weight, ); Color get _progressColor { final p = widget.controller.filament.value.weight > 0 ? _remaining / widget.controller.filament.value.weight : 0; if (p > 0.5) return const Color(0xFF4FFFB0); if (p > 0.2) return const Color(0xFFFFD166); return const Color(0xFFFF6B6B); } @override Widget build(BuildContext context) { final f = widget.controller.filament.value; final maxWeight = f.weight; return _GlassCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const _CardTitle(icon: Icons.scale_outlined, label: 'Füllstand'), const SizedBox(height: 16), // Kreisanzeige + Zahlen Row( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: 120, height: 120, child: Stack( alignment: Alignment.center, children: [ SizedBox.expand( child: CircularProgressIndicator( value: maxWeight > 0 ? _remaining / maxWeight : 0, strokeWidth: 10, backgroundColor: Colors.white.withValues(alpha: 0.1), valueColor: AlwaysStoppedAnimation(_progressColor), strokeCap: StrokeCap.round, ), ), Column( mainAxisSize: MainAxisSize.min, children: [ Text( _remaining.toStringAsFixed(0), style: TextStyle( color: _progressColor, fontSize: 26, fontWeight: FontWeight.bold, ), ), Text( 'von ${maxWeight.toStringAsFixed(0)} g', style: TextStyle( color: Colors.white.withValues(alpha: 0.45), fontSize: 11, ), ), ], ), ], ), ), ], ), const SizedBox(height: 20), // Slider für verbrauchtes Gewicht Text( 'Verbraucht: ${_sliderValue.toStringAsFixed(0)} g', style: TextStyle( color: Colors.white.withValues(alpha: 0.7), fontSize: 13, ), ), const SizedBox(height: 6), SliderTheme( data: SliderTheme.of(context).copyWith( activeTrackColor: _progressColor, inactiveTrackColor: Colors.white.withValues(alpha: 0.12), thumbColor: _progressColor, overlayColor: _progressColor.withValues(alpha: 0.2), trackHeight: 6, thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 10), ), child: Slider( value: _sliderValue.clamp(0, maxWeight), min: 0, max: maxWeight > 0 ? maxWeight : 1, divisions: maxWeight > 0 ? maxWeight.toInt() : 1, onChanged: (v) => setState(() => _sliderValue = v), onChangeEnd: (v) => widget.controller.updateWeightUsed(v), ), ), ], ), ); } } // ───────────────────────────────────────────────────────────────────────────── // Druck-Parameter // ───────────────────────────────────────────────────────────────────────────── class _PrintParamsCard extends StatelessWidget { final FilamentModel filament; const _PrintParamsCard({required this.filament}); @override Widget build(BuildContext context) { return _GlassCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const _CardTitle( icon: Icons.settings_outlined, label: 'Druckparameter', ), const SizedBox(height: 16), Row( children: [ Expanded( child: _ParamTile( icon: Icons.thermostat_outlined, label: 'Düsentemperatur', value: '${filament.printingTemp} °C', color: const Color(0xFFFF9F43), ), ), const SizedBox(width: 12), Expanded( child: _ParamTile( icon: Icons.bed_outlined, label: 'Betttemperatur', value: '${filament.bedTemp} °C', color: const Color(0xFF7B9FFF), ), ), ], ), ], ), ); } } // ───────────────────────────────────────────────────────────────────────────── // Kauf-Info // ───────────────────────────────────────────────────────────────────────────── class _PurchaseCard extends StatelessWidget { final FilamentModel filament; const _PurchaseCard({required this.filament}); @override Widget build(BuildContext context) { return _GlassCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const _CardTitle( icon: Icons.receipt_long_outlined, label: 'Kaufinfo', ), const SizedBox(height: 16), Row( children: [ Expanded( child: _ParamTile( icon: Icons.euro_outlined, label: 'Preis', value: '${filament.price.toStringAsFixed(2)} €', color: const Color(0xFF4FFFB0), ), ), const SizedBox(width: 12), Expanded( child: _ParamTile( icon: Icons.calendar_today_outlined, label: 'Kaufdatum', value: filament.purchaseDate.isNotEmpty ? filament.purchaseDate : '–', color: const Color(0xFFFFD166), ), ), ], ), ], ), ); } } // ───────────────────────────────────────────────────────────────────────────── // Notizen // ───────────────────────────────────────────────────────────────────────────── class _NotesCard extends StatelessWidget { final String notes; const _NotesCard({required this.notes}); @override Widget build(BuildContext context) { return _GlassCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const _CardTitle(icon: Icons.notes_outlined, label: 'Notizen'), const SizedBox(height: 12), Text( notes, style: TextStyle( color: Colors.white.withValues(alpha: 0.75), fontSize: 14, height: 1.5, ), ), ], ), ); } } // ───────────────────────────────────────────────────────────────────────────── // Shared Helper Widgets // ───────────────────────────────────────────────────────────────────────────── class _GlassCard extends StatelessWidget { final Widget child; const _GlassCard({required this.child}); @override Widget build(BuildContext context) { return ClipRRect( borderRadius: BorderRadius.circular(20), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 16, sigmaY: 16), child: Container( width: double.infinity, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.07), borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.white.withValues(alpha: 0.12)), ), padding: const EdgeInsets.all(20), child: child, ), ), ); } } class _CardTitle extends StatelessWidget { final IconData icon; final String label; const _CardTitle({required this.icon, required this.label}); @override Widget build(BuildContext context) { return Row( children: [ Icon(icon, color: Colors.white54, size: 18), const SizedBox(width: 8), Text( label, style: TextStyle( color: Colors.white.withValues(alpha: 0.55), fontSize: 12, fontWeight: FontWeight.w600, letterSpacing: 1.1, ), ), ], ); } } class _ParamTile extends StatelessWidget { final IconData icon; final String label; final String value; final Color color; const _ParamTile({ required this.icon, required this.label, required this.value, required this.color, }); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: color.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(14), border: Border.all(color: color.withValues(alpha: 0.25)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, color: color, size: 20), const SizedBox(height: 8), Text( value, style: TextStyle( color: color, fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 2), Text( label, style: TextStyle( color: Colors.white.withValues(alpha: 0.45), fontSize: 11, ), ), ], ), ); } } class _Badge extends StatelessWidget { final String label; final Color color; const _Badge({required this.label, required this.color}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), decoration: BoxDecoration( color: color.withValues(alpha: 0.18), borderRadius: BorderRadius.circular(6), border: Border.all(color: color.withValues(alpha: 0.4)), ), child: Text( label, style: TextStyle( color: color, fontSize: 11, fontWeight: FontWeight.w600, ), ), ); } }