Files
hackathon-heavy-gears-7cc00…/lib/login.dart
2026-05-23 09:30:42 -06:00

491 lines
15 KiB
Dart

import 'package:flutter/material.dart';
import 'domicilios.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _formKey = GlobalKey<FormState>();
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
bool ocultarPassword = true;
void iniciarSesion() {
if (_formKey.currentState!.validate()) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Inicio de sesión exitoso'),
backgroundColor: Colors.green,
),
);
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const GestionDomiciliosScreen(),
),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
// ── Fondo azul cielo ──
Container(
width: double.infinity,
height: double.infinity,
color: const Color(0xFF5BB8E8),
),
// ── Barras de colores en la parte superior ──
Column(
children: [
Container(height: 10, color: const Color(0xFFE8534A)),
Container(height: 8, color: const Color(0xFFF5A623)),
Container(height: 8, color: const Color(0xFFFFD700)),
Container(height: 8, color: const Color(0xFF4CAF50)),
],
),
// ── Forma orgánica azul oscuro detrás del contenido ──
Positioned(
top: 60,
left: -40,
right: -40,
child: Container(
height: 420,
decoration: BoxDecoration(
color: const Color(0xFF4AA8D8).withValues(alpha:0.6),
borderRadius: BorderRadius.circular(200),
),
),
),
// ── Montañas verdes atrás ──
Positioned(
bottom: 80,
right: -10,
child: CustomPaint(
size: const Size(160, 130),
painter: _MountainPainter(color: const Color(0xFF2E7D32)),
),
),
Positioned(
bottom: 80,
right: 60,
child: CustomPaint(
size: const Size(120, 100),
painter: _MountainPainter(color: const Color(0xFF388E3C)),
),
),
// ── Suelo verde ──
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
height: 90,
color: const Color(0xFF4CAF50),
),
),
// ── Carretera ──
Positioned(
bottom: 30,
left: 0,
right: 0,
child: Container(
height: 18,
color: const Color(0xFF424242),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: List.generate(
12,
(_) => Container(
width: 20,
height: 3,
color: Colors.white,
),
),
),
),
),
// ── Casas pequeñas ──
Positioned(
bottom: 45,
left: 20,
child: _buildHouse(const Color(0xFF90A4AE), 40),
),
Positioned(
bottom: 45,
left: 80,
child: _buildHouse(const Color(0xFF795548), 35),
),
Positioned(
bottom: 45,
right: 20,
child: _buildHouse(const Color(0xFFFFCC80), 45),
),
// ── Camiones de basura ──
Positioned(
bottom: 33,
left: 110,
child: _buildTruck(const Color(0xFFE53935)),
),
Positioned(
bottom: 33,
right: 80,
child: _buildTruck(const Color(0xFFE53935)),
),
// ── Basura (montón gris izquierda) ──
Positioned(
bottom: 90,
left: 10,
child: CustomPaint(
size: const Size(60, 35),
painter: _TrashPilePainter(),
),
),
// ── Contenido principal ──
SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Form(
key: _formKey,
child: Column(
children: [
const SizedBox(height: 50),
// Título ECOUBICEL
const Text(
'ECOUBICEL.',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.w900,
color: Colors.black87,
letterSpacing: 2,
),
),
const SizedBox(height: 16),
// Botes de basura
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildTrashCan(const Color(0xFF2E7D32), Icons.recycling),
const SizedBox(width: 8),
_buildTrashCan(const Color(0xFFF9A825), Icons.recycling),
const SizedBox(width: 8),
_buildTrashCan(const Color(0xFFC62828), Icons.recycling),
const SizedBox(width: 12),
// Ícono de reciclaje
Icon(
Icons.recycling,
size: 48,
color: Colors.green.shade600,
),
],
),
const SizedBox(height: 28),
// Campo correo — barra verde
_buildColorField(
controller: emailController,
hint: 'Correo Electrónico',
icon: Icons.email,
color: const Color(0xFF4CAF50),
obscure: false,
validator: (value) {
if (value == null || value.isEmpty) return 'Ingresa tu correo';
if (!value.contains('@')) return 'Correo inválido';
return null;
},
),
const SizedBox(height: 12),
// Campo contraseña — barra azul
_buildColorField(
controller: passwordController,
hint: 'Contraseña',
icon: Icons.lock,
color: const Color(0xFF1E88E5),
obscure: ocultarPassword,
suffixIcon: IconButton(
icon: Icon(
ocultarPassword ? Icons.visibility_off : Icons.visibility,
color: Colors.white70,
),
onPressed: () => setState(() => ocultarPassword = !ocultarPassword),
),
validator: (value) {
if (value == null || value.isEmpty) return 'Ingresa tu contraseña';
if (value.length < 6) return 'Mínimo 6 caracteres';
return null;
},
),
const SizedBox(height: 12),
// Botón iniciar sesión — barra naranja
SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: iniciarSesion,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFE65100),
foregroundColor: Colors.white,
shape: const StadiumBorder(),
elevation: 3,
),
child: const Text(
'Iniciar Sesión',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(height: 12),
// Botón registrarse — barra gris
SizedBox(
width: double.infinity,
height: 48,
child: OutlinedButton(
onPressed: () {},
style: OutlinedButton.styleFrom(
foregroundColor: Colors.white,
side: const BorderSide(color: Colors.white54, width: 2),
backgroundColor: Colors.white24,
shape: const StadiumBorder(),
),
child: const Text(
'¿No tienes cuenta? Registrarse',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
),
const SizedBox(height: 120),
],
),
),
),
),
),
],
),
);
}
// Widget bote de basura
Widget _buildTrashCan(Color color, IconData icon) {
return Container(
width: 52,
height: 64,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(color: Colors.black26, blurRadius: 4, offset: const Offset(2, 2)),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 8,
margin: const EdgeInsets.symmetric(horizontal: 4),
decoration: BoxDecoration(
color: color.withValues(alpha:0.7),
borderRadius: BorderRadius.circular(4),
),
),
const SizedBox(height: 4),
Icon(icon, color: Colors.white, size: 28),
Container(
height: 6,
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: Colors.black26,
borderRadius: BorderRadius.circular(3),
),
),
],
),
);
}
// Campo de texto con fondo de color tipo barra
Widget _buildColorField({
required TextEditingController controller,
required String hint,
required IconData icon,
required Color color,
required bool obscure,
Widget? suffixIcon,
required String? Function(String?) validator,
}) {
return TextFormField(
controller: controller,
obscureText: obscure,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w600),
decoration: InputDecoration(
hintText: hint,
hintStyle: const TextStyle(color: Colors.white70),
prefixIcon: Icon(icon, color: Colors.white),
suffixIcon: suffixIcon,
filled: true,
fillColor: color,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: BorderSide.none,
),
errorStyle: const TextStyle(color: Colors.yellow),
contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
),
validator: validator,
);
}
// Casa pequeña
Widget _buildHouse(Color color, double size) {
return CustomPaint(
size: Size(size, size * 0.9),
painter: _HousePainter(color: color),
);
}
// Camión pequeño
Widget _buildTruck(Color color) {
return Container(
width: 36,
height: 18,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(3),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
width: 12,
height: 14,
margin: const EdgeInsets.only(right: 2, top: 0),
decoration: BoxDecoration(
color: color.withValues(alpha:0.7),
borderRadius: const BorderRadius.only(
topRight: Radius.circular(3),
topLeft: Radius.circular(2),
),
),
),
],
),
);
}
}
// Pintor de montañas
class _MountainPainter extends CustomPainter {
final Color color;
_MountainPainter({required this.color});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color;
final path = Path()
..moveTo(0, size.height)
..lineTo(size.width / 2, 0)
..lineTo(size.width, size.height)
..close();
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(_) => false;
}
// Pintor de casa
class _HousePainter extends CustomPainter {
final Color color;
_HousePainter({required this.color});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color;
// Cuerpo
canvas.drawRect(
Rect.fromLTWH(0, size.height * 0.4, size.width, size.height * 0.6),
paint,
);
// Techo
final roof = Paint()..color = const Color(0xFFB71C1C);
final path = Path()
..moveTo(0, size.height * 0.4)
..lineTo(size.width / 2, 0)
..lineTo(size.width, size.height * 0.4)
..close();
canvas.drawPath(path, roof);
// Ventana
canvas.drawRect(
Rect.fromLTWH(size.width * 0.25, size.height * 0.55, size.width * 0.2, size.height * 0.2),
Paint()..color = const Color(0xFFB3E5FC),
);
// Puerta
canvas.drawRect(
Rect.fromLTWH(size.width * 0.55, size.height * 0.65, size.width * 0.2, size.height * 0.35),
Paint()..color = const Color(0xFF5D4037),
);
}
@override
bool shouldRepaint(_) => false;
}
// Pintor de montón de basura
class _TrashPilePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = const Color(0xFF757575);
canvas.drawOval(
Rect.fromLTWH(0, size.height * 0.3, size.width, size.height * 0.7),
paint,
);
canvas.drawOval(
Rect.fromLTWH(size.width * 0.1, 0, size.width * 0.5, size.height * 0.6),
Paint()..color = const Color(0xFF616161),
);
canvas.drawOval(
Rect.fromLTWH(size.width * 0.5, size.height * 0.1, size.width * 0.4, size.height * 0.5),
Paint()..color = const Color(0xFF9E9E9E),
);
}
@override
bool shouldRepaint(_) => false;
}