home_page.dart¶
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'config_page.dart';
import 'feeding_page.dart';
import 'history_page.dart';
class SaveNemoHomePage extends StatefulWidget {
final String aquariumId;
const SaveNemoHomePage({super.key, required this.aquariumId});
@override
State<SaveNemoHomePage> createState() => _SaveNemoHomePageState();
}
class _SaveNemoHomePageState extends State<SaveNemoHomePage> {
late Stream<DocumentSnapshot> _sensorStream;
String _selectedFish = 'Cargando...';
@override
void initState() {
super.initState();
_sensorStream = FirebaseFirestore.instance
.collection('acuarios')
.doc(widget.aquariumId)
.collection('data')
.doc('estado')
.snapshots();
}
void _onTapNavigation(int index) {
if (index == 0) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => FeedingPage(aquariumId: widget.aquariumId))
);
} else if (index == 1) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HistoryPage(aquariumId: widget.aquariumId),
),
);
} else if (index == 2) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ConfigPage(
aquariumId: widget.aquariumId,
onFishSelected: (name, params) {},
currentFish: _selectedFish,
),
),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Column(
children: [
const Text('SaveNemo',
style: TextStyle(fontWeight: FontWeight.w900, letterSpacing: 1.2, color: Colors.white)),
Text("ID: ${widget.aquariumId}",
style: const TextStyle(fontSize: 12, color: Colors.white54, letterSpacing: 1.0)),
],
),
centerTitle: true,
backgroundColor: Colors.transparent,
elevation: 0,
iconTheme: const IconThemeData(color: Colors.white),
),
body: StreamBuilder<DocumentSnapshot>(
stream: _sensorStream,
builder: (context, snapshot) {
if (snapshot.hasError) return const Center(child: Text("Error de conexión", style: TextStyle(color: Colors.white)));
if (snapshot.connectionState == ConnectionState.waiting) return const Center(child: CircularProgressIndicator(color: Color(0xFFFF5400)));
Map<String, dynamic> data = snapshot.data!.data() as Map<String, dynamic>? ?? {};
double currentTemp = (data['temp_actual'] ?? 0.0).toDouble();
double currentPh = (data['ph_actual'] ?? 7.0).toDouble();
int currentLight = (data['luz_actual'] ?? 0).toInt();
int currentWater = (data['agua_nivel'] ?? 0).toInt();
List<dynamic> alertas = data['alertas'] ?? [];
String alertasTexto = alertas.toString();
bool hayProblemas = alertas.isNotEmpty;
return Column(
children: [
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
_buildNemoCard(
context,
icon: Icons.thermostat_rounded,
value: '${currentTemp.toStringAsFixed(1)}°C',
label: 'Temperatura',
alert: alertasTexto.contains("Temp") || alertasTexto.contains("T."),
alertText: "Fuera de Rango",
),
const SizedBox(height: 20),
_buildNemoCard(
context,
customIcon: Container(
alignment: Alignment.center,
decoration: BoxDecoration(shape: BoxShape.circle, border: Border.all(color: Colors.black87, width: 2.5)),
width: 50, height: 50,
child: const Text('pH', style: TextStyle(fontSize: 20, fontWeight: FontWeight.w900, color: Colors.black87)),
),
value: currentPh.toStringAsFixed(1),
label: 'pH Actual',
alert: alertasTexto.contains("pH"),
alertText: "Peligro Químico",
),
const SizedBox(height: 20),
_buildNemoCard(
context,
icon: Icons.lightbulb_circle,
value: '$currentLight%',
label: 'Iluminación',
alert: alertasTexto.contains("Luz") || alertasTexto.contains("Iluminación"),
alertText: "Problema Luz",
),
const SizedBox(height: 20),
_buildNemoCard(
context,
icon: Icons.waves,
value: '$currentWater%',
label: 'Nivel Agua',
alert: alertasTexto.contains("Agua"),
alertText: "Rellenar Tanque",
),
],
),
),
),
Container(
decoration: const BoxDecoration(
border: Border(top: BorderSide(color: Colors.white24, width: 0.5))
),
child: BottomNavigationBar(
backgroundColor: const Color(0xFF00101C),
selectedItemColor: Colors.white,
unselectedItemColor: Colors.white54,
showSelectedLabels: true,
showUnselectedLabels: true,
type: BottomNavigationBarType.fixed,
onTap: _onTapNavigation,
items: <BottomNavigationBarItem>[
const BottomNavigationBarItem(
icon: Icon(Icons.set_meal_rounded, color: Color(0xFFFF5400)),
label: 'Alimentación'
),
BottomNavigationBarItem(
icon: Stack(
children: [
Icon(Icons.notifications,
color: hayProblemas ? Colors.redAccent : Colors.white
),
if (hayProblemas)
Positioned(
right: 0, top: 0,
child: Container(
padding: const EdgeInsets.all(2),
decoration: const BoxDecoration(color: Colors.red, shape: BoxShape.circle),
constraints: const BoxConstraints(minWidth: 10, minHeight: 10),
),
)
],
),
label: 'Alertas',
),
const BottomNavigationBarItem(
icon: Icon(Icons.settings_rounded, color: Color(0xFFFF5400)),
label: 'Configuración'
),
],
),
),
],
);
}
),
);
}
Widget _buildNemoCard(BuildContext context, {IconData? icon, Widget? customIcon, required String value, required String label, bool alert = false, String? alertText}) {
Color mainColor = alert ? const Color(0xFFD32F2F) : const Color(0xFFFF5400);
Color stripeColor = Colors.white;
Color borderColor = Colors.black;
return AnimatedContainer(
duration: const Duration(milliseconds: 500),
height: 100,
decoration: BoxDecoration(
color: mainColor,
borderRadius: BorderRadius.circular(20),
boxShadow: [BoxShadow(color: alert ? Colors.red.withOpacity(0.6) : Colors.black.withOpacity(0.5), blurRadius: alert ? 20 : 10, offset: const Offset(0, 5))],
gradient: LinearGradient(
colors: alert ? [Colors.red.shade900, Colors.red.shade600] : [const Color(0xFFFF5400), const Color(0xFFFF8E00)],
begin: Alignment.topLeft, end: Alignment.bottomRight,
),
),
child: Row(
children: [
Container(
width: 90,
decoration: BoxDecoration(
color: stripeColor,
borderRadius: const BorderRadius.only(topLeft: Radius.circular(20), bottomLeft: Radius.circular(20)),
border: Border(right: BorderSide(color: borderColor, width: 6)),
),
child: Center(child: customIcon ?? Icon(icon, size: 40, color: Colors.black87)),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
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)])),
Text(alert ? ' $alertText' : label, style: TextStyle(fontSize: alert ? 14 : 16, fontWeight: FontWeight.bold, color: alert ? Colors.white : Colors.black.withOpacity(0.4))),
],
),
),
),
],
),
);
}
}