Project

General

Profile

Home pagedart » History » Version 6

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

1 1 cristobal hernandez
---
2 1 cristobal hernandez
3 4 cristobal hernandez
| [[Interfaz|⌂]] | [[history_page.dart]] | [[feeding_page.dart]] | [[connection_page.dart]] | [[config_page.dart]] | 
4 1 cristobal hernandez
5 1 cristobal hernandez
---
6 5 cristobal hernandez
7 5 cristobal hernandez
h1. home_page.dart
8 5 cristobal hernandez
9 5 cristobal hernandez
<pre><code class="dart">
10 5 cristobal hernandez
11 6 cristobal hernandez
import 'package:flutter/material.dart';
12 6 cristobal hernandez
import 'package:cloud_firestore/cloud_firestore.dart';
13 6 cristobal hernandez
import 'config_page.dart';
14 6 cristobal hernandez
import 'feeding_page.dart';
15 6 cristobal hernandez
import 'history_page.dart'; 
16 5 cristobal hernandez
17 6 cristobal hernandez
class SaveNemoHomePage extends StatefulWidget {
18 6 cristobal hernandez
  final String aquariumId; 
19 6 cristobal hernandez
20 6 cristobal hernandez
  const SaveNemoHomePage({super.key, required this.aquariumId});
21 6 cristobal hernandez
22 6 cristobal hernandez
  @override
23 6 cristobal hernandez
  State<SaveNemoHomePage> createState() => _SaveNemoHomePageState();
24 6 cristobal hernandez
}
25 6 cristobal hernandez
26 6 cristobal hernandez
class _SaveNemoHomePageState extends State<SaveNemoHomePage> {
27 6 cristobal hernandez
  late Stream<DocumentSnapshot> _sensorStream;
28 6 cristobal hernandez
  String _selectedFish = 'Cargando...';
29 6 cristobal hernandez
30 6 cristobal hernandez
  @override
31 6 cristobal hernandez
  void initState() {
32 6 cristobal hernandez
    super.initState();
33 6 cristobal hernandez
    _sensorStream = FirebaseFirestore.instance
34 6 cristobal hernandez
        .collection('acuarios')
35 6 cristobal hernandez
        .doc(widget.aquariumId)
36 6 cristobal hernandez
        .collection('data')
37 6 cristobal hernandez
        .doc('estado')
38 6 cristobal hernandez
        .snapshots();
39 6 cristobal hernandez
  }
40 6 cristobal hernandez
41 6 cristobal hernandez
  void _onTapNavigation(int index) {
42 6 cristobal hernandez
    if (index == 0) {
43 6 cristobal hernandez
      // Alimentación
44 6 cristobal hernandez
      Navigator.push(
45 6 cristobal hernandez
        context, 
46 6 cristobal hernandez
        MaterialPageRoute(builder: (context) => FeedingPage(aquariumId: widget.aquariumId))
47 6 cristobal hernandez
      );
48 6 cristobal hernandez
    } else if (index == 1) {
49 6 cristobal hernandez
      // Alertas (Historial)
50 6 cristobal hernandez
      Navigator.push(
51 6 cristobal hernandez
        context,
52 6 cristobal hernandez
        MaterialPageRoute(
53 6 cristobal hernandez
          builder: (context) => HistoryPage(aquariumId: widget.aquariumId),
54 6 cristobal hernandez
        ),
55 6 cristobal hernandez
      );
56 6 cristobal hernandez
    } else if (index == 2) {
57 6 cristobal hernandez
      // Configuración
58 6 cristobal hernandez
      Navigator.push(
59 6 cristobal hernandez
        context,
60 6 cristobal hernandez
        MaterialPageRoute(
61 6 cristobal hernandez
          builder: (context) => ConfigPage(
62 6 cristobal hernandez
            aquariumId: widget.aquariumId, 
63 6 cristobal hernandez
            onFishSelected: (name, params) {}, 
64 6 cristobal hernandez
            currentFish: _selectedFish,
65 6 cristobal hernandez
          ),
66 6 cristobal hernandez
        ),
67 6 cristobal hernandez
      );
68 6 cristobal hernandez
    }
69 6 cristobal hernandez
  }
70 6 cristobal hernandez
71 6 cristobal hernandez
  @override
72 6 cristobal hernandez
  Widget build(BuildContext context) {
73 6 cristobal hernandez
    return Scaffold(
74 6 cristobal hernandez
      appBar: AppBar(
75 6 cristobal hernandez
        title: Column(
76 6 cristobal hernandez
          children: [
77 6 cristobal hernandez
            const Text('SaveNemo', 
78 6 cristobal hernandez
              style: TextStyle(fontWeight: FontWeight.w900, letterSpacing: 1.2, color: Colors.white)),
79 6 cristobal hernandez
            Text("ID: ${widget.aquariumId}", 
80 6 cristobal hernandez
              style: const TextStyle(fontSize: 12, color: Colors.white54, letterSpacing: 1.0)), 
81 6 cristobal hernandez
          ],
82 6 cristobal hernandez
        ),
83 6 cristobal hernandez
        centerTitle: true,
84 6 cristobal hernandez
        backgroundColor: Colors.transparent,
85 6 cristobal hernandez
        elevation: 0,
86 6 cristobal hernandez
        iconTheme: const IconThemeData(color: Colors.white),
87 6 cristobal hernandez
      ),
88 6 cristobal hernandez
      
89 6 cristobal hernandez
      body: StreamBuilder<DocumentSnapshot>(
90 6 cristobal hernandez
        stream: _sensorStream,
91 6 cristobal hernandez
        builder: (context, snapshot) {
92 6 cristobal hernandez
          if (snapshot.hasError) return const Center(child: Text("Error de conexión", style: TextStyle(color: Colors.white)));
93 6 cristobal hernandez
          if (snapshot.connectionState == ConnectionState.waiting) return const Center(child: CircularProgressIndicator(color: Color(0xFFFF5400)));
94 6 cristobal hernandez
95 6 cristobal hernandez
          Map<String, dynamic> data = snapshot.data!.data() as Map<String, dynamic>? ?? {};
96 6 cristobal hernandez
          
97 6 cristobal hernandez
          double currentTemp = (data['temp_actual'] ?? 0.0).toDouble();
98 6 cristobal hernandez
          double currentPh = (data['ph_actual'] ?? 7.0).toDouble();
99 6 cristobal hernandez
          int currentLight = (data['luz_actual'] ?? 0).toInt();
100 6 cristobal hernandez
          int currentWater = (data['agua_nivel'] ?? 0).toInt();
101 6 cristobal hernandez
          
102 6 cristobal hernandez
          List<dynamic> alertas = data['alertas'] ?? []; 
103 6 cristobal hernandez
          String alertasTexto = alertas.toString(); 
104 6 cristobal hernandez
          bool hayProblemas = alertas.isNotEmpty;
105 6 cristobal hernandez
106 6 cristobal hernandez
          return Column(
107 6 cristobal hernandez
            children: [
108 6 cristobal hernandez
              Expanded(
109 6 cristobal hernandez
                child: SingleChildScrollView(
110 6 cristobal hernandez
                  padding: const EdgeInsets.all(16.0),
111 6 cristobal hernandez
                  child: Column(
112 6 cristobal hernandez
                    children: <Widget>[
113 6 cristobal hernandez
                      // TARJETA TEMPERATURA
114 6 cristobal hernandez
                      _buildNemoCard(
115 6 cristobal hernandez
                        context,
116 6 cristobal hernandez
                        icon: Icons.thermostat_rounded,
117 6 cristobal hernandez
                        value: '${currentTemp.toStringAsFixed(1)}°C',
118 6 cristobal hernandez
                        label: 'Temperatura',
119 6 cristobal hernandez
                        alert: alertasTexto.contains("Temp") || alertasTexto.contains("T."), 
120 6 cristobal hernandez
                        alertText: "Fuera de Rango",
121 6 cristobal hernandez
                      ),
122 6 cristobal hernandez
                      const SizedBox(height: 20),
123 6 cristobal hernandez
124 6 cristobal hernandez
                      // TARJETA PH
125 6 cristobal hernandez
                      _buildNemoCard(
126 6 cristobal hernandez
                        context,
127 6 cristobal hernandez
                        customIcon: Container(
128 6 cristobal hernandez
                          alignment: Alignment.center,
129 6 cristobal hernandez
                          decoration: BoxDecoration(shape: BoxShape.circle, border: Border.all(color: Colors.black87, width: 2.5)),
130 6 cristobal hernandez
                          width: 50, height: 50,
131 6 cristobal hernandez
                          child: const Text('pH', style: TextStyle(fontSize: 20, fontWeight: FontWeight.w900, color: Colors.black87)),
132 6 cristobal hernandez
                        ),
133 6 cristobal hernandez
                        value: currentPh.toStringAsFixed(1),
134 6 cristobal hernandez
                        label: 'pH Actual',
135 6 cristobal hernandez
                        alert: alertasTexto.contains("pH"),
136 6 cristobal hernandez
                        alertText: "Peligro Químico",
137 6 cristobal hernandez
                      ),
138 6 cristobal hernandez
                      const SizedBox(height: 20),
139 6 cristobal hernandez
                      
140 6 cristobal hernandez
                      // TARJETA LUZ
141 6 cristobal hernandez
                      _buildNemoCard(
142 6 cristobal hernandez
                        context,
143 6 cristobal hernandez
                        icon: Icons.lightbulb_circle,
144 6 cristobal hernandez
                        value: '$currentLight%',
145 6 cristobal hernandez
                        label: 'Iluminación',
146 6 cristobal hernandez
                        alert: alertasTexto.contains("Luz") || alertasTexto.contains("Iluminación"),
147 6 cristobal hernandez
                        alertText: "Problema Luz",
148 6 cristobal hernandez
                      ),
149 6 cristobal hernandez
                      const SizedBox(height: 20),
150 6 cristobal hernandez
151 6 cristobal hernandez
                      // TARJETA AGUA
152 6 cristobal hernandez
                      _buildNemoCard(
153 6 cristobal hernandez
                        context,
154 6 cristobal hernandez
                        icon: Icons.waves, 
155 6 cristobal hernandez
                        value: '$currentWater%',
156 6 cristobal hernandez
                        label: 'Nivel Agua',
157 6 cristobal hernandez
                        alert: alertasTexto.contains("Agua"),
158 6 cristobal hernandez
                        alertText: "Rellenar Tanque",
159 6 cristobal hernandez
                      ),
160 6 cristobal hernandez
                    ],
161 6 cristobal hernandez
                  ),
162 6 cristobal hernandez
                ),
163 6 cristobal hernandez
              ),
164 6 cristobal hernandez
165 6 cristobal hernandez
              // BARRA INFERIOR (CON ALERTAS)
166 6 cristobal hernandez
              Container(
167 6 cristobal hernandez
                decoration: const BoxDecoration(
168 6 cristobal hernandez
                  border: Border(top: BorderSide(color: Colors.white24, width: 0.5))
169 6 cristobal hernandez
                ),
170 6 cristobal hernandez
                child: BottomNavigationBar(
171 6 cristobal hernandez
                  backgroundColor: const Color(0xFF00101C),
172 6 cristobal hernandez
                  selectedItemColor: Colors.white,
173 6 cristobal hernandez
                  unselectedItemColor: Colors.white54,
174 6 cristobal hernandez
                  showSelectedLabels: true,
175 6 cristobal hernandez
                  showUnselectedLabels: true,
176 6 cristobal hernandez
                  type: BottomNavigationBarType.fixed,
177 6 cristobal hernandez
                  onTap: _onTapNavigation,
178 6 cristobal hernandez
                  items: <BottomNavigationBarItem>[
179 6 cristobal hernandez
                    const BottomNavigationBarItem(
180 6 cristobal hernandez
                      icon: Icon(Icons.set_meal_rounded, color: Color(0xFFFF5400)), 
181 6 cristobal hernandez
                      label: 'Alimentación'
182 6 cristobal hernandez
                    ),
183 6 cristobal hernandez
                    
184 6 cristobal hernandez
                    // CAMPANA DE ALERTAS (Se marca si hay problemas)
185 6 cristobal hernandez
                    BottomNavigationBarItem(
186 6 cristobal hernandez
                      icon: Stack(
187 6 cristobal hernandez
                        children: [
188 6 cristobal hernandez
                          Icon(Icons.notifications, 
189 6 cristobal hernandez
                            color: hayProblemas ? Colors.redAccent : Colors.white
190 6 cristobal hernandez
                          ),
191 6 cristobal hernandez
                          if (hayProblemas)
192 6 cristobal hernandez
                            Positioned(
193 6 cristobal hernandez
                              right: 0, top: 0,
194 6 cristobal hernandez
                              child: Container(
195 6 cristobal hernandez
                                padding: const EdgeInsets.all(2),
196 6 cristobal hernandez
                                decoration: const BoxDecoration(color: Colors.red, shape: BoxShape.circle),
197 6 cristobal hernandez
                                constraints: const BoxConstraints(minWidth: 10, minHeight: 10),
198 6 cristobal hernandez
                              ),
199 6 cristobal hernandez
                            )
200 6 cristobal hernandez
                        ],
201 6 cristobal hernandez
                      ),
202 6 cristobal hernandez
                      label: 'Alertas',
203 6 cristobal hernandez
                    ),
204 6 cristobal hernandez
205 6 cristobal hernandez
                    const BottomNavigationBarItem(
206 6 cristobal hernandez
                      icon: Icon(Icons.settings_rounded, color: Color(0xFFFF5400)), 
207 6 cristobal hernandez
                      label: 'Configuración'
208 6 cristobal hernandez
                    ),
209 6 cristobal hernandez
                  ],
210 6 cristobal hernandez
                ),
211 6 cristobal hernandez
              ),
212 6 cristobal hernandez
            ],
213 6 cristobal hernandez
          );
214 6 cristobal hernandez
        }
215 6 cristobal hernandez
      ),
216 6 cristobal hernandez
    );
217 6 cristobal hernandez
  }
218 6 cristobal hernandez
219 6 cristobal hernandez
  Widget _buildNemoCard(BuildContext context, {IconData? icon, Widget? customIcon, required String value, required String label, bool alert = false, String? alertText}) {
220 6 cristobal hernandez
    Color mainColor = alert ? const Color(0xFFD32F2F) : const Color(0xFFFF5400); 
221 6 cristobal hernandez
    Color stripeColor = Colors.white;
222 6 cristobal hernandez
    Color borderColor = Colors.black;
223 6 cristobal hernandez
224 6 cristobal hernandez
    return AnimatedContainer(
225 6 cristobal hernandez
      duration: const Duration(milliseconds: 500),
226 6 cristobal hernandez
      height: 100,
227 6 cristobal hernandez
      decoration: BoxDecoration(
228 6 cristobal hernandez
        color: mainColor,
229 6 cristobal hernandez
        borderRadius: BorderRadius.circular(20),
230 6 cristobal hernandez
        boxShadow: [BoxShadow(color: alert ? Colors.red.withOpacity(0.6) : Colors.black.withOpacity(0.5), blurRadius: alert ? 20 : 10, offset: const Offset(0, 5))],
231 6 cristobal hernandez
        gradient: LinearGradient(
232 6 cristobal hernandez
          colors: alert ? [Colors.red.shade900, Colors.red.shade600] : [const Color(0xFFFF5400), const Color(0xFFFF8E00)],
233 6 cristobal hernandez
          begin: Alignment.topLeft, end: Alignment.bottomRight,
234 6 cristobal hernandez
        ),
235 6 cristobal hernandez
      ),
236 6 cristobal hernandez
      child: Row(
237 6 cristobal hernandez
        children: [
238 6 cristobal hernandez
          Container(
239 6 cristobal hernandez
            width: 90,
240 6 cristobal hernandez
            decoration: BoxDecoration(
241 6 cristobal hernandez
              color: stripeColor,
242 6 cristobal hernandez
              borderRadius: const BorderRadius.only(topLeft: Radius.circular(20), bottomLeft: Radius.circular(20)),
243 6 cristobal hernandez
              border: Border(right: BorderSide(color: borderColor, width: 6)),
244 6 cristobal hernandez
            ),
245 6 cristobal hernandez
            child: Center(child: customIcon ?? Icon(icon, size: 40, color: Colors.black87)),
246 6 cristobal hernandez
          ),
247 6 cristobal hernandez
          Expanded(
248 6 cristobal hernandez
            child: Padding(
249 6 cristobal hernandez
              padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10),
250 6 cristobal hernandez
              child: Column(
251 6 cristobal hernandez
                mainAxisAlignment: MainAxisAlignment.center,
252 6 cristobal hernandez
                crossAxisAlignment: CrossAxisAlignment.start,
253 6 cristobal hernandez
                children: [
254 6 cristobal hernandez
                  Text(value, style: const TextStyle(fontSize: 34, fontWeight: FontWeight.w900, color: Colors.white, height: 1.0, shadows: [Shadow(color: Colors.black45, offset: Offset(1,1), blurRadius: 2)])),
255 6 cristobal hernandez
                  Text(alert ? ' $alertText' : label, style: TextStyle(fontSize: alert ? 14 : 16, fontWeight: FontWeight.bold, color: alert ? Colors.white : Colors.black.withOpacity(0.4))),
256 6 cristobal hernandez
                ],
257 6 cristobal hernandez
              ),
258 6 cristobal hernandez
            ),
259 6 cristobal hernandez
          ),
260 6 cristobal hernandez
        ],
261 6 cristobal hernandez
      ),
262 6 cristobal hernandez
    );
263 6 cristobal hernandez
  }
264 6 cristobal hernandez
}
265 5 cristobal hernandez
   
266 5 cristobal hernandez
</code></pre>