Project

General

Profile

Config pagedart » History » Version 1

cristobal hernandez, 12/18/2025 01:50 AM

1 1 cristobal hernandez
---
2 1 cristobal hernandez
3 1 cristobal hernandez
| [[Interfaz|⌂]] | [[home_page.dart]] | [[history_page.dart]] | [[feeding_page.dart]] | [[connection_page.dart]] | 
4 1 cristobal hernandez
5 1 cristobal hernandez
---
6 1 cristobal hernandez
7 1 cristobal hernandez
h1. config_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
// --- MODELO ---
15 1 cristobal hernandez
class FishParameters {
16 1 cristobal hernandez
  final double temperature_min;
17 1 cristobal hernandez
  final double temperature_max;
18 1 cristobal hernandez
  final double ph_min;
19 1 cristobal hernandez
  final double ph_max;
20 1 cristobal hernandez
  final double nivel_luz; 
21 1 cristobal hernandez
  final double nivel_agua;
22 1 cristobal hernandez
23 1 cristobal hernandez
  FishParameters(this.temperature_min, this.temperature_max, this.ph_min, this.ph_max, this.nivel_luz, this.nivel_agua);
24 1 cristobal hernandez
}
25 1 cristobal hernandez
26 1 cristobal hernandez
// --- DATOS FIJOS ---
27 1 cristobal hernandez
Map<String, FishParameters> fishParameters = {
28 1 cristobal hernandez
  'Pez Platy (Xiphophorus maculatus)': FishParameters(20.0, 26.0, 7.0, 8.2, 2.0, 45.0), 
29 1 cristobal hernandez
  'Guppy Cobra': FishParameters(22.0, 28.0, 7.0, 8.5, 2.0, 40.0), 
30 1 cristobal hernandez
  'Tetra Neón Verde (Simulans)': FishParameters(23.0, 29.0, 4.0, 6.5, 1.0, 40.0), 
31 1 cristobal hernandez
  'Tetra Luminoso (Hemigrammus erythrozonus)': FishParameters(24.0, 28.0, 6.0, 7.5, 1.0, 45.0),
32 1 cristobal hernandez
  'Pez Lápiz (Nannostomus beckfordi)': FishParameters(24.0, 28.0, 5.5, 7.5, 1.0, 45.0), 
33 1 cristobal hernandez
  'Tetra Limón': FishParameters(23.0, 28.0, 6.0, 7.5, 1.0, 50.0),
34 1 cristobal hernandez
  'Gourami Pigmeo (Trichopsis pumila)': FishParameters(25.0, 28.0, 6.0, 7.0, 1.0, 30.0), 
35 1 cristobal hernandez
  'Betta Imbellis (Betta Pacífico)': FishParameters(24.0, 28.0, 6.0, 7.5, 1.5, 30.0), 
36 1 cristobal hernandez
  'Pseudomugil Furcatus (Ojos Azules)': FishParameters(24.0, 28.0, 7.0, 8.0, 1.0, 45.0),
37 1 cristobal hernandez
  'Gobio Pavo Real (Tateurndina ocellicauda)': FishParameters(22.0, 26.0, 6.5, 7.5, 1.5, 40.0),
38 1 cristobal hernandez
  'Killi Cabo Lopez (Aphyosemion australe)': FishParameters(21.0, 24.0, 6.0, 7.0, 1.5, 40.0),
39 1 cristobal hernandez
  'Nube Blanca (Tanichthys albonubes)': FishParameters(15.0, 24.0, 6.0, 8.0, 1.0, 40.0),
40 1 cristobal hernandez
  'Medaka (Oryzias latipes)': FishParameters(16.0, 28.0, 7.0, 8.0, 1.0, 30.0),
41 1 cristobal hernandez
  'Multifasciatus (Neolamprologus multifasciatus)': FishParameters(24.0, 27.0, 7.5, 9.0, 2.5, 45.0),
42 1 cristobal hernandez
  'Rasbora Esmeralda (Danio erythromicron)': FishParameters(21.0, 25.0, 7.0, 8.0, 1.5, 30.0),
43 1 cristobal hernandez
  'Tetra Cobre (Hasemania nana)': FishParameters(22.0, 28.0, 6.0, 7.5, 1.0, 45.0),
44 1 cristobal hernandez
  'Pez Hacha Mármol (Carnegiella strigata)': FishParameters(24.0, 28.0, 5.5, 7.0, 2.0, 45.0),
45 1 cristobal hernandez
  'Corydora Habrosus': FishParameters(22.0, 26.0, 6.0, 7.5, 1.0, 35.0),
46 1 cristobal hernandez
  'Gamba Amano (Caridina multidentata)': FishParameters(18.0, 28.0, 6.5, 8.0, 1.5, 20.0),
47 1 cristobal hernandez
  'Gamba Crystal Red (Caridina cantonensis)': FishParameters(20.0, 24.0, 5.5, 6.8, 3.0, 20.0),
48 1 cristobal hernandez
  'Crayfish Enano (Cambarellus patzcuarensis)': FishParameters(15.0, 25.0, 6.5, 8.0, 2.0, 40.0),
49 1 cristobal hernandez
50 1 cristobal hernandez
  // Default
51 1 cristobal hernandez
  'Personalizado': FishParameters(24.0, 26.0, 6.5, 7.5, 2.0, 50.0),
52 1 cristobal hernandez
};
53 1 cristobal hernandez
54 1 cristobal hernandez
class ConfigPage extends StatefulWidget {
55 1 cristobal hernandez
  final String aquariumId;
56 1 cristobal hernandez
  final Function(String, FishParameters) onFishSelected;
57 1 cristobal hernandez
  final String currentFish;
58 1 cristobal hernandez
59 1 cristobal hernandez
  const ConfigPage({super.key, required this.onFishSelected, required this.currentFish, required this.aquariumId});
60 1 cristobal hernandez
61 1 cristobal hernandez
  @override
62 1 cristobal hernandez
  State<ConfigPage> createState() => _ConfigPageState();
63 1 cristobal hernandez
}
64 1 cristobal hernandez
65 1 cristobal hernandez
class _ConfigPageState extends State<ConfigPage> {
66 1 cristobal hernandez
  String _selectedFish = 'Personalizado';
67 1 cristobal hernandez
  
68 1 cristobal hernandez
  late TextEditingController _minTempCtrl;
69 1 cristobal hernandez
  late TextEditingController _maxTempCtrl;
70 1 cristobal hernandez
  late TextEditingController _minPhCtrl;
71 1 cristobal hernandez
  late TextEditingController _maxPhCtrl;
72 1 cristobal hernandez
  late TextEditingController _aguaCtrl;
73 1 cristobal hernandez
  double _nivelLuz = 2.0;
74 1 cristobal hernandez
75 1 cristobal hernandez
  late DocumentReference _configRef; // Referencia dinámica
76 1 cristobal hernandez
77 1 cristobal hernandez
  @override
78 1 cristobal hernandez
  void initState() {
79 1 cristobal hernandez
    super.initState();
80 1 cristobal hernandez
    _configRef = FirebaseFirestore.instance
81 1 cristobal hernandez
        .collection('acuarios')
82 1 cristobal hernandez
        .doc(widget.aquariumId)
83 1 cristobal hernandez
        .collection('data')
84 1 cristobal hernandez
        .doc('config');
85 1 cristobal hernandez
86 1 cristobal hernandez
    FishParameters params = fishParameters['Personalizado']!;
87 1 cristobal hernandez
    _setupControllers(params);
88 1 cristobal hernandez
    _cargarDatosDeNube();
89 1 cristobal hernandez
  }
90 1 cristobal hernandez
91 1 cristobal hernandez
  void _setupControllers(FishParameters params) {
92 1 cristobal hernandez
    _minTempCtrl = TextEditingController(text: params.temperature_min.toString());
93 1 cristobal hernandez
    _maxTempCtrl = TextEditingController(text: params.temperature_max.toString());
94 1 cristobal hernandez
    _minPhCtrl = TextEditingController(text: params.ph_min.toString());
95 1 cristobal hernandez
    _maxPhCtrl = TextEditingController(text: params.ph_max.toString());
96 1 cristobal hernandez
    _aguaCtrl = TextEditingController(text: params.nivel_agua.toStringAsFixed(0));
97 1 cristobal hernandez
    _nivelLuz = params.nivel_luz;
98 1 cristobal hernandez
  }
99 1 cristobal hernandez
100 1 cristobal hernandez
  void _cargarDatosDeNube() async {
101 1 cristobal hernandez
    try {
102 1 cristobal hernandez
      DocumentSnapshot doc = await _configRef.get();
103 1 cristobal hernandez
      if(doc.exists) {
104 1 cristobal hernandez
        Map<String, dynamic> data = doc.data() as Map<String, dynamic>;
105 1 cristobal hernandez
        setState(() {
106 1 cristobal hernandez
          _selectedFish = data['nombre_pez'] ?? 'Personalizado';
107 1 cristobal hernandez
          _minTempCtrl.text = (data['temp_min'] ?? 24.0).toString();
108 1 cristobal hernandez
          _maxTempCtrl.text = (data['temp_max'] ?? 28.0).toString();
109 1 cristobal hernandez
          _minPhCtrl.text = (data['ph_min'] ?? 6.5).toString();
110 1 cristobal hernandez
          _maxPhCtrl.text = (data['ph_max'] ?? 7.5).toString();
111 1 cristobal hernandez
          _aguaCtrl.text = (data['nivel_agua'] ?? 80).toString();
112 1 cristobal hernandez
          
113 1 cristobal hernandez
          int luzPorcentaje = data['nivel_luz'] ?? 50;
114 1 cristobal hernandez
          if (luzPorcentaje <= 40) _nivelLuz = 1.0;
115 1 cristobal hernandez
          else if (luzPorcentaje <= 70) _nivelLuz = 2.0;
116 1 cristobal hernandez
          else _nivelLuz = 3.0;
117 1 cristobal hernandez
        });
118 1 cristobal hernandez
      }
119 1 cristobal hernandez
    } catch (e) { print(e); }
120 1 cristobal hernandez
  }
121 1 cristobal hernandez
122 1 cristobal hernandez
  void _loadFishData(String fishName) {
123 1 cristobal hernandez
    if (fishParameters.containsKey(fishName)) {
124 1 cristobal hernandez
      FishParameters params = fishParameters[fishName]!;
125 1 cristobal hernandez
      setState(() {
126 1 cristobal hernandez
        _selectedFish = fishName;
127 1 cristobal hernandez
        _minTempCtrl.text = params.temperature_min.toString();
128 1 cristobal hernandez
        _maxTempCtrl.text = params.temperature_max.toString();
129 1 cristobal hernandez
        _minPhCtrl.text = params.ph_min.toString();
130 1 cristobal hernandez
        _maxPhCtrl.text = params.ph_max.toString();
131 1 cristobal hernandez
        _aguaCtrl.text = params.nivel_agua.toStringAsFixed(0);
132 1 cristobal hernandez
        _nivelLuz = params.nivel_luz;
133 1 cristobal hernandez
      });
134 1 cristobal hernandez
    }
135 1 cristobal hernandez
  }
136 1 cristobal hernandez
137 1 cristobal hernandez
  void _saveAndExit() async {
138 1 cristobal hernandez
    double minT = double.tryParse(_minTempCtrl.text) ?? 24.0;
139 1 cristobal hernandez
    double maxT = double.tryParse(_maxTempCtrl.text) ?? 28.0;
140 1 cristobal hernandez
    double minPh = double.tryParse(_minPhCtrl.text) ?? 6.5;
141 1 cristobal hernandez
    double maxPh = double.tryParse(_maxPhCtrl.text) ?? 7.5;
142 1 cristobal hernandez
    double agua = double.tryParse(_aguaCtrl.text) ?? 80.0;
143 1 cristobal hernandez
    
144 1 cristobal hernandez
    int luzPorcentaje = 50;
145 1 cristobal hernandez
    if (_nivelLuz == 1.0) luzPorcentaje = 30;
146 1 cristobal hernandez
    if (_nivelLuz == 2.0) luzPorcentaje = 60;
147 1 cristobal hernandez
    if (_nivelLuz == 3.0) luzPorcentaje = 100;
148 1 cristobal hernandez
149 1 cristobal hernandez
    await _configRef.update({
150 1 cristobal hernandez
      'nombre_pez': _selectedFish,
151 1 cristobal hernandez
      'temp_min': minT,
152 1 cristobal hernandez
      'temp_max': maxT,
153 1 cristobal hernandez
      'ph_min': minPh,
154 1 cristobal hernandez
      'ph_max': maxPh,
155 1 cristobal hernandez
      'nivel_agua': agua,
156 1 cristobal hernandez
      'nivel_luz': luzPorcentaje,
157 1 cristobal hernandez
      'ultima_modificacion': DateTime.now().toString()
158 1 cristobal hernandez
    });
159 1 cristobal hernandez
160 1 cristobal hernandez
    ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Configuración enviada')));
161 1 cristobal hernandez
    Navigator.pop(context);
162 1 cristobal hernandez
  }
163 1 cristobal hernandez
164 1 cristobal hernandez
  @override
165 1 cristobal hernandez
  Widget build(BuildContext context) {
166 1 cristobal hernandez
    List<String> dropdownItems = fishParameters.keys.toList();
167 1 cristobal hernandez
    bool isEditable = _selectedFish == 'Personalizado';
168 1 cristobal hernandez
169 1 cristobal hernandez
    return Scaffold(
170 1 cristobal hernandez
      appBar: AppBar(
171 1 cristobal hernandez
        title: const Text('Configuración', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
172 1 cristobal hernandez
        backgroundColor: Colors.transparent,
173 1 cristobal hernandez
        elevation: 0,
174 1 cristobal hernandez
        iconTheme: const IconThemeData(color: Colors.white),
175 1 cristobal hernandez
      ),
176 1 cristobal hernandez
      floatingActionButton: FloatingActionButton.extended(
177 1 cristobal hernandez
        onPressed: _saveAndExit,
178 1 cristobal hernandez
        backgroundColor: const Color(0xFFFF5400),
179 1 cristobal hernandez
        icon: const Icon(Icons.cloud_upload, color: Colors.white),
180 1 cristobal hernandez
        label: const Text("ENVIAR A PECERA", style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white)),
181 1 cristobal hernandez
      ),
182 1 cristobal hernandez
      body: SingleChildScrollView(
183 1 cristobal hernandez
        padding: const EdgeInsets.fromLTRB(20, 10, 20, 80),
184 1 cristobal hernandez
        child: Column(
185 1 cristobal hernandez
          crossAxisAlignment: CrossAxisAlignment.start,
186 1 cristobal hernandez
          children: [
187 1 cristobal hernandez
            const Text('Perfil del Acuario:', style: TextStyle(fontSize: 16, color: Colors.white70)),
188 1 cristobal hernandez
            const SizedBox(height: 10),
189 1 cristobal hernandez
            Container(
190 1 cristobal hernandez
              padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
191 1 cristobal hernandez
              decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(30), border: Border.all(color: const Color(0xFFFF5400), width: 3)),
192 1 cristobal hernandez
              child: DropdownButtonHideUnderline(
193 1 cristobal hernandez
                child: DropdownButton<String>(
194 1 cristobal hernandez
                  dropdownColor: Colors.white, 
195 1 cristobal hernandez
                  value: dropdownItems.contains(_selectedFish) ? _selectedFish : 'Personalizado',
196 1 cristobal hernandez
                  isExpanded: true,
197 1 cristobal hernandez
                  icon: const Icon(Icons.arrow_drop_down_circle, color: Color(0xFFFF5400)),
198 1 cristobal hernandez
                  style: const TextStyle(fontSize: 18, color: Colors.black87, fontWeight: FontWeight.bold),
199 1 cristobal hernandez
                  items: dropdownItems.map((String item) {
200 1 cristobal hernandez
                    return DropdownMenuItem<String>(value: item, child: Text(item));
201 1 cristobal hernandez
                  }).toList(),
202 1 cristobal hernandez
                  onChanged: (val) => _loadFishData(val!),
203 1 cristobal hernandez
                ),
204 1 cristobal hernandez
              ),
205 1 cristobal hernandez
            ),
206 1 cristobal hernandez
            const SizedBox(height: 20),
207 1 cristobal hernandez
            Container(
208 1 cristobal hernandez
              padding: const EdgeInsets.all(20),
209 1 cristobal hernandez
              decoration: BoxDecoration(color: Colors.black.withOpacity(0.3), borderRadius: BorderRadius.circular(20), border: Border.all(color: isEditable ? const Color(0xFFFF5400) : Colors.white12)),
210 1 cristobal hernandez
              child: IgnorePointer(
211 1 cristobal hernandez
                ignoring: !isEditable,
212 1 cristobal hernandez
                child: Opacity(
213 1 cristobal hernandez
                  opacity: isEditable ? 1.0 : 0.6,
214 1 cristobal hernandez
                  child: Column(
215 1 cristobal hernandez
                    children: [
216 1 cristobal hernandez
                        _buildRangeInput(icon: Icons.thermostat, label: 'Temperatura (°C)', minCtrl: _minTempCtrl, maxCtrl: _maxTempCtrl, enabled: isEditable),
217 1 cristobal hernandez
                        _buildRangeInput(icon: Icons.science, label: 'Rango de pH', minCtrl: _minPhCtrl, maxCtrl: _maxPhCtrl, enabled: isEditable),
218 1 cristobal hernandez
                        _buildSingleInput(icon: Icons.waves, label: 'Nivel Agua %', ctrl: _aguaCtrl, enabled: isEditable),
219 1 cristobal hernandez
                        const SizedBox(height: 15),
220 1 cristobal hernandez
                        const Text('Nivel de Luz', style: TextStyle(color: Colors.white70)),
221 1 cristobal hernandez
                        Slider(
222 1 cristobal hernandez
                          value: _nivelLuz,
223 1 cristobal hernandez
                          min: 1.0, max: 3.0, divisions: 2,
224 1 cristobal hernandez
                          activeColor: const Color(0xFFFF5400),
225 1 cristobal hernandez
                          inactiveColor: Colors.grey,
226 1 cristobal hernandez
                          onChanged: isEditable ? (val) => setState(() => _nivelLuz = val) : null, 
227 1 cristobal hernandez
                        ),
228 1 cristobal hernandez
                    ],
229 1 cristobal hernandez
                  ),
230 1 cristobal hernandez
                ),
231 1 cristobal hernandez
              ),
232 1 cristobal hernandez
            ),
233 1 cristobal hernandez
          ],
234 1 cristobal hernandez
        ),
235 1 cristobal hernandez
      ),
236 1 cristobal hernandez
    );
237 1 cristobal hernandez
  }
238 1 cristobal hernandez
239 1 cristobal hernandez
  Widget _buildRangeInput({required IconData icon, required String label, required TextEditingController minCtrl, required TextEditingController maxCtrl, required bool enabled}) {
240 1 cristobal hernandez
    return Padding(
241 1 cristobal hernandez
      padding: const EdgeInsets.symmetric(vertical: 10.0),
242 1 cristobal hernandez
      child: Row(
243 1 cristobal hernandez
        children: [
244 1 cristobal hernandez
          Icon(icon, color: Colors.white54),
245 1 cristobal hernandez
          const SizedBox(width: 10),
246 1 cristobal hernandez
          Expanded(child: Text(label, style: const TextStyle(color: Colors.white70))),
247 1 cristobal hernandez
          SizedBox(width: 60, child: _buildTextField(minCtrl, enabled)),
248 1 cristobal hernandez
          const Text(' - ', style: TextStyle(color: Colors.white)),
249 1 cristobal hernandez
          SizedBox(width: 60, child: _buildTextField(maxCtrl, enabled)),
250 1 cristobal hernandez
        ],
251 1 cristobal hernandez
      ),
252 1 cristobal hernandez
    );
253 1 cristobal hernandez
  }
254 1 cristobal hernandez
255 1 cristobal hernandez
  Widget _buildSingleInput({required IconData icon, required String label, required TextEditingController ctrl, required bool enabled}) {
256 1 cristobal hernandez
    return Padding(
257 1 cristobal hernandez
      padding: const EdgeInsets.symmetric(vertical: 10.0),
258 1 cristobal hernandez
      child: Row(
259 1 cristobal hernandez
        children: [
260 1 cristobal hernandez
          Icon(icon, color: Colors.white54),
261 1 cristobal hernandez
          const SizedBox(width: 10),
262 1 cristobal hernandez
          Expanded(child: Text(label, style: const TextStyle(color: Colors.white70))),
263 1 cristobal hernandez
          SizedBox(width: 80, child: _buildTextField(ctrl, enabled)),
264 1 cristobal hernandez
        ],
265 1 cristobal hernandez
      ),
266 1 cristobal hernandez
    );
267 1 cristobal hernandez
  }
268 1 cristobal hernandez
269 1 cristobal hernandez
  Widget _buildTextField(TextEditingController controller, bool enabled) {
270 1 cristobal hernandez
    return TextFormField(
271 1 cristobal hernandez
      controller: controller,
272 1 cristobal hernandez
      enabled: enabled,
273 1 cristobal hernandez
      keyboardType: const TextInputType.numberWithOptions(decimal: true),
274 1 cristobal hernandez
      style: const TextStyle(color: Colors.white),
275 1 cristobal hernandez
      textAlign: TextAlign.center,
276 1 cristobal hernandez
      decoration: InputDecoration(
277 1 cristobal hernandez
        filled: true,
278 1 cristobal hernandez
        fillColor: Colors.white10,
279 1 cristobal hernandez
        contentPadding: const EdgeInsets.all(5),
280 1 cristobal hernandez
        border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
281 1 cristobal hernandez
      ),
282 1 cristobal hernandez
    );
283 1 cristobal hernandez
  }
284 1 cristobal hernandez
}
285 1 cristobal hernandez
   
286 1 cristobal hernandez
</code></pre>