Feeding pagedart » History » Version 1
cristobal hernandez, 12/18/2025 01:45 AM
| 1 | 1 | cristobal hernandez | --- |
|---|---|---|---|
| 2 | 1 | cristobal hernandez | |
| 3 | 1 | cristobal hernandez | | [[Interfaz|⌂]] | [[home_page.dart]] | [[history_page.dart]] | [[connection_page.dart]] | [[config_page.dart]] | |
| 4 | 1 | cristobal hernandez | |
| 5 | 1 | cristobal hernandez | --- |
| 6 | 1 | cristobal hernandez | |
| 7 | 1 | cristobal hernandez | h1. feeding_page.dart |
| 8 | 1 | cristobal hernandez | |
| 9 | 1 | cristobal hernandez | <pre><code class="java"> |
| 10 | 1 | cristobal hernandez | |
| 11 | 1 | cristobal hernandez | import 'package:flutter/material.dart'; |
| 12 | 1 | cristobal hernandez | import 'package:cloud_firestore/cloud_firestore.dart'; |
| 13 | 1 | cristobal hernandez | |
| 14 | 1 | cristobal hernandez | class FeedingPage extends StatefulWidget { |
| 15 | 1 | cristobal hernandez | final String aquariumId; |
| 16 | 1 | cristobal hernandez | |
| 17 | 1 | cristobal hernandez | const FeedingPage({super.key, required this.aquariumId}); |
| 18 | 1 | cristobal hernandez | |
| 19 | 1 | cristobal hernandez | @override |
| 20 | 1 | cristobal hernandez | State<FeedingPage> createState() => _FeedingPageState(); |
| 21 | 1 | cristobal hernandez | } |
| 22 | 1 | cristobal hernandez | |
| 23 | 1 | cristobal hernandez | class _FeedingPageState extends State<FeedingPage> { |
| 24 | 1 | cristobal hernandez | bool _isAutoFeedOn = true; |
| 25 | 1 | cristobal hernandez | List<TimeOfDay> _feedTimes = []; |
| 26 | 1 | cristobal hernandez | bool _loading = true; |
| 27 | 1 | cristobal hernandez | |
| 28 | 1 | cristobal hernandez | late DocumentReference _configRef; |
| 29 | 1 | cristobal hernandez | |
| 30 | 1 | cristobal hernandez | @override |
| 31 | 1 | cristobal hernandez | void initState() { |
| 32 | 1 | cristobal hernandez | super.initState(); |
| 33 | 1 | cristobal hernandez | _configRef = FirebaseFirestore.instance |
| 34 | 1 | cristobal hernandez | .collection('acuarios') |
| 35 | 1 | cristobal hernandez | .doc(widget.aquariumId) |
| 36 | 1 | cristobal hernandez | .collection('data') |
| 37 | 1 | cristobal hernandez | .doc('config'); |
| 38 | 1 | cristobal hernandez | |
| 39 | 1 | cristobal hernandez | _cargarDatos(); |
| 40 | 1 | cristobal hernandez | } |
| 41 | 1 | cristobal hernandez | |
| 42 | 1 | cristobal hernandez | void _cargarDatos() async { |
| 43 | 1 | cristobal hernandez | try { |
| 44 | 1 | cristobal hernandez | DocumentSnapshot doc = await _configRef.get(); |
| 45 | 1 | cristobal hernandez | if (doc.exists) { |
| 46 | 1 | cristobal hernandez | Map<String, dynamic> data = doc.data() as Map<String, dynamic>; |
| 47 | 1 | cristobal hernandez | bool estadoSwitch = data['sistema_alimentacion_on'] ?? true; |
| 48 | 1 | cristobal hernandez | List<dynamic> horasString = data['horarios_comida'] ?? []; |
| 49 | 1 | cristobal hernandez | |
| 50 | 1 | cristobal hernandez | setState(() { |
| 51 | 1 | cristobal hernandez | _isAutoFeedOn = estadoSwitch; |
| 52 | 1 | cristobal hernandez | _feedTimes = horasString.map((s) { |
| 53 | 1 | cristobal hernandez | final parts = s.split(":"); |
| 54 | 1 | cristobal hernandez | return TimeOfDay(hour: int.parse(parts[0]), minute: int.parse(parts[1])); |
| 55 | 1 | cristobal hernandez | }).toList(); |
| 56 | 1 | cristobal hernandez | _loading = false; |
| 57 | 1 | cristobal hernandez | }); |
| 58 | 1 | cristobal hernandez | } else { |
| 59 | 1 | cristobal hernandez | setState(() => _loading = false); |
| 60 | 1 | cristobal hernandez | } |
| 61 | 1 | cristobal hernandez | } catch (e) { setState(() => _loading = false); } |
| 62 | 1 | cristobal hernandez | } |
| 63 | 1 | cristobal hernandez | |
| 64 | 1 | cristobal hernandez | void _guardarCambios() async { |
| 65 | 1 | cristobal hernandez | List<String> horasString = _feedTimes.map((t) { |
| 66 | 1 | cristobal hernandez | String h = t.hour.toString().padLeft(2, '0'); |
| 67 | 1 | cristobal hernandez | String m = t.minute.toString().padLeft(2, '0'); |
| 68 | 1 | cristobal hernandez | return "$h:$m"; |
| 69 | 1 | cristobal hernandez | }).toList(); |
| 70 | 1 | cristobal hernandez | |
| 71 | 1 | cristobal hernandez | await _configRef.update({ |
| 72 | 1 | cristobal hernandez | 'horarios_comida': horasString, |
| 73 | 1 | cristobal hernandez | 'sistema_alimentacion_on': _isAutoFeedOn |
| 74 | 1 | cristobal hernandez | }); |
| 75 | 1 | cristobal hernandez | } |
| 76 | 1 | cristobal hernandez | |
| 77 | 1 | cristobal hernandez | Future<void> _addTime() async { |
| 78 | 1 | cristobal hernandez | final TimeOfDay? newTime = await showTimePicker( |
| 79 | 1 | cristobal hernandez | context: context, |
| 80 | 1 | cristobal hernandez | initialTime: TimeOfDay.now(), |
| 81 | 1 | cristobal hernandez | builder: (context, child) { |
| 82 | 1 | cristobal hernandez | return Theme( |
| 83 | 1 | cristobal hernandez | data: ThemeData.dark().copyWith( |
| 84 | 1 | cristobal hernandez | colorScheme: const ColorScheme.dark( |
| 85 | 1 | cristobal hernandez | primary: Color(0xFFFF5400), |
| 86 | 1 | cristobal hernandez | onPrimary: Colors.white, |
| 87 | 1 | cristobal hernandez | surface: Color(0xFF001B2E), |
| 88 | 1 | cristobal hernandez | ), |
| 89 | 1 | cristobal hernandez | ), |
| 90 | 1 | cristobal hernandez | child: child!, |
| 91 | 1 | cristobal hernandez | ); |
| 92 | 1 | cristobal hernandez | }, |
| 93 | 1 | cristobal hernandez | ); |
| 94 | 1 | cristobal hernandez | |
| 95 | 1 | cristobal hernandez | if (newTime != null) { |
| 96 | 1 | cristobal hernandez | setState(() { |
| 97 | 1 | cristobal hernandez | _feedTimes.add(newTime); |
| 98 | 1 | cristobal hernandez | _feedTimes.sort((a, b) => (a.hour * 60 + a.minute).compareTo(b.hour * 60 + b.minute)); |
| 99 | 1 | cristobal hernandez | }); |
| 100 | 1 | cristobal hernandez | _guardarCambios(); |
| 101 | 1 | cristobal hernandez | } |
| 102 | 1 | cristobal hernandez | } |
| 103 | 1 | cristobal hernandez | |
| 104 | 1 | cristobal hernandez | void _removeTime(int index) { |
| 105 | 1 | cristobal hernandez | setState(() { |
| 106 | 1 | cristobal hernandez | _feedTimes.removeAt(index); |
| 107 | 1 | cristobal hernandez | }); |
| 108 | 1 | cristobal hernandez | _guardarCambios(); |
| 109 | 1 | cristobal hernandez | } |
| 110 | 1 | cristobal hernandez | |
| 111 | 1 | cristobal hernandez | @override |
| 112 | 1 | cristobal hernandez | Widget build(BuildContext context) { |
| 113 | 1 | cristobal hernandez | return Scaffold( |
| 114 | 1 | cristobal hernandez | appBar: AppBar( |
| 115 | 1 | cristobal hernandez | title: const Text('Alimentación', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white)), |
| 116 | 1 | cristobal hernandez | backgroundColor: Colors.transparent, |
| 117 | 1 | cristobal hernandez | iconTheme: const IconThemeData(color: Colors.white), |
| 118 | 1 | cristobal hernandez | elevation: 0, |
| 119 | 1 | cristobal hernandez | ), |
| 120 | 1 | cristobal hernandez | body: _loading |
| 121 | 1 | cristobal hernandez | ? const Center(child: CircularProgressIndicator(color: Color(0xFFFF5400))) |
| 122 | 1 | cristobal hernandez | : SingleChildScrollView( |
| 123 | 1 | cristobal hernandez | padding: const EdgeInsets.all(20), |
| 124 | 1 | cristobal hernandez | child: Column( |
| 125 | 1 | cristobal hernandez | crossAxisAlignment: CrossAxisAlignment.start, |
| 126 | 1 | cristobal hernandez | children: [ |
| 127 | 1 | cristobal hernandez | Container( |
| 128 | 1 | cristobal hernandez | decoration: BoxDecoration( |
| 129 | 1 | cristobal hernandez | color: _isAutoFeedOn ? const Color(0xFFFF5400) : Colors.grey.shade800, |
| 130 | 1 | cristobal hernandez | borderRadius: BorderRadius.circular(20), |
| 131 | 1 | cristobal hernandez | border: Border.all(color: Colors.black, width: 3), |
| 132 | 1 | cristobal hernandez | boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.5), blurRadius: 10, offset: const Offset(0, 5))], |
| 133 | 1 | cristobal hernandez | ), |
| 134 | 1 | cristobal hernandez | child: SwitchListTile( |
| 135 | 1 | cristobal hernandez | contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), |
| 136 | 1 | cristobal hernandez | title: Text(_isAutoFeedOn ? 'Dispensador ACTIVO' : 'Dispensador APAGADO', style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w900, fontSize: 20)), |
| 137 | 1 | cristobal hernandez | subtitle: Text(_isAutoFeedOn ? 'El sistema alimentará a las horas indicadas.' : 'El sistema NO alimentará.', style: TextStyle(color: Colors.white.withOpacity(0.8), fontSize: 12)), |
| 138 | 1 | cristobal hernandez | activeColor: Colors.white, |
| 139 | 1 | cristobal hernandez | activeTrackColor: Colors.black26, |
| 140 | 1 | cristobal hernandez | value: _isAutoFeedOn, |
| 141 | 1 | cristobal hernandez | onChanged: (bool value) { |
| 142 | 1 | cristobal hernandez | setState(() => _isAutoFeedOn = value); |
| 143 | 1 | cristobal hernandez | _guardarCambios(); |
| 144 | 1 | cristobal hernandez | }, |
| 145 | 1 | cristobal hernandez | secondary: const Icon(Icons.set_meal, color: Colors.white, size: 30), |
| 146 | 1 | cristobal hernandez | ), |
| 147 | 1 | cristobal hernandez | ), |
| 148 | 1 | cristobal hernandez | const SizedBox(height: 30), |
| 149 | 1 | cristobal hernandez | Opacity( |
| 150 | 1 | cristobal hernandez | opacity: _isAutoFeedOn ? 1.0 : 0.5, |
| 151 | 1 | cristobal hernandez | child: Column( |
| 152 | 1 | cristobal hernandez | crossAxisAlignment: CrossAxisAlignment.start, |
| 153 | 1 | cristobal hernandez | children: [ |
| 154 | 1 | cristobal hernandez | const Text('Horarios Programados', style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)), |
| 155 | 1 | cristobal hernandez | const SizedBox(height: 15), |
| 156 | 1 | cristobal hernandez | ListView.builder( |
| 157 | 1 | cristobal hernandez | shrinkWrap: true, |
| 158 | 1 | cristobal hernandez | physics: const NeverScrollableScrollPhysics(), |
| 159 | 1 | cristobal hernandez | itemCount: _feedTimes.length, |
| 160 | 1 | cristobal hernandez | itemBuilder: (context, index) { |
| 161 | 1 | cristobal hernandez | final time = _feedTimes[index]; |
| 162 | 1 | cristobal hernandez | return Container( |
| 163 | 1 | cristobal hernandez | margin: const EdgeInsets.only(bottom: 10), |
| 164 | 1 | cristobal hernandez | decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(15), border: Border.all(color: const Color(0xFFFF5400), width: 3)), |
| 165 | 1 | cristobal hernandez | child: ListTile( |
| 166 | 1 | cristobal hernandez | leading: const Icon(Icons.access_time_filled, color: Colors.black87), |
| 167 | 1 | cristobal hernandez | title: Text(time.format(context), style: const TextStyle(color: Colors.black87, fontSize: 20, fontWeight: FontWeight.bold)), |
| 168 | 1 | cristobal hernandez | trailing: IconButton( |
| 169 | 1 | cristobal hernandez | icon: const Icon(Icons.delete, color: Color(0xFFD32F2F)), |
| 170 | 1 | cristobal hernandez | onPressed: _isAutoFeedOn ? () => _removeTime(index) : null, |
| 171 | 1 | cristobal hernandez | ), |
| 172 | 1 | cristobal hernandez | ), |
| 173 | 1 | cristobal hernandez | ); |
| 174 | 1 | cristobal hernandez | }, |
| 175 | 1 | cristobal hernandez | ), |
| 176 | 1 | cristobal hernandez | const SizedBox(height: 20), |
| 177 | 1 | cristobal hernandez | Center( |
| 178 | 1 | cristobal hernandez | child: ElevatedButton.icon( |
| 179 | 1 | cristobal hernandez | onPressed: _isAutoFeedOn ? _addTime : null, |
| 180 | 1 | cristobal hernandez | style: ElevatedButton.styleFrom(backgroundColor: const Color(0xFFFF5400), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 15)), |
| 181 | 1 | cristobal hernandez | icon: const Icon(Icons.add_alarm), |
| 182 | 1 | cristobal hernandez | label: const Text('Agregar Horario', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), |
| 183 | 1 | cristobal hernandez | ), |
| 184 | 1 | cristobal hernandez | ), |
| 185 | 1 | cristobal hernandez | ], |
| 186 | 1 | cristobal hernandez | ), |
| 187 | 1 | cristobal hernandez | ) |
| 188 | 1 | cristobal hernandez | ], |
| 189 | 1 | cristobal hernandez | ), |
| 190 | 1 | cristobal hernandez | ), |
| 191 | 1 | cristobal hernandez | ); |
| 192 | 1 | cristobal hernandez | } |
| 193 | 1 | cristobal hernandez | } |
| 194 | 1 | cristobal hernandez | |
| 195 | 1 | cristobal hernandez | </code></pre> |