modificaciones en el sistema
This commit is contained in:
636
lib/login.dart
636
lib/login.dart
@@ -11,18 +11,13 @@ class LoginScreen extends StatefulWidget {
|
||||
class _LoginScreenState extends State<LoginScreen> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
final TextEditingController emailController =
|
||||
TextEditingController();
|
||||
|
||||
final TextEditingController passwordController =
|
||||
TextEditingController();
|
||||
final TextEditingController emailController = TextEditingController();
|
||||
final TextEditingController passwordController = TextEditingController();
|
||||
|
||||
bool ocultarPassword = true;
|
||||
|
||||
void iniciarSesion() {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
|
||||
// Simulación de login exitoso
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Inicio de sesión exitoso'),
|
||||
@@ -30,12 +25,10 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
),
|
||||
);
|
||||
|
||||
// Navegar a la pantalla principal
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
const GestionDomiciliosScreen(),
|
||||
builder: (context) => const GestionDomiciliosScreen(),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -44,190 +37,455 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: Stack(
|
||||
children: [
|
||||
// ── Fondo azul cielo ──
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
color: const Color(0xFF5BB8E8),
|
||||
),
|
||||
|
||||
body: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(25),
|
||||
// ── 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)),
|
||||
],
|
||||
),
|
||||
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
||||
children: [
|
||||
|
||||
// Logo
|
||||
CircleAvatar(
|
||||
radius: 50,
|
||||
backgroundColor: Colors.green.shade100,
|
||||
|
||||
child: Icon(
|
||||
Icons.recycling,
|
||||
size: 60,
|
||||
color: Colors.green.shade700,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
|
||||
const Text(
|
||||
'EcoRecolección',
|
||||
style: TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 10),
|
||||
|
||||
Text(
|
||||
'Inicia sesión para continuar',
|
||||
style: TextStyle(
|
||||
color: Colors.grey.shade600,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 40),
|
||||
|
||||
// Campo correo
|
||||
TextFormField(
|
||||
controller: emailController,
|
||||
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Correo Electrónico',
|
||||
hintText: 'ejemplo@correo.com',
|
||||
|
||||
prefixIcon: const Icon(Icons.email),
|
||||
|
||||
border: OutlineInputBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(15),
|
||||
),
|
||||
),
|
||||
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Ingresa tu correo';
|
||||
}
|
||||
|
||||
if (!value.contains('@')) {
|
||||
return 'Correo inválido';
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Campo contraseña
|
||||
TextFormField(
|
||||
controller: passwordController,
|
||||
obscureText: ocultarPassword,
|
||||
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Contraseña',
|
||||
|
||||
prefixIcon:
|
||||
const Icon(Icons.lock),
|
||||
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
ocultarPassword
|
||||
? Icons.visibility_off
|
||||
: Icons.visibility,
|
||||
),
|
||||
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
ocultarPassword =
|
||||
!ocultarPassword;
|
||||
});
|
||||
},
|
||||
),
|
||||
|
||||
border: OutlineInputBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(15),
|
||||
),
|
||||
),
|
||||
|
||||
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: 30),
|
||||
|
||||
// Botón iniciar sesión
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 55,
|
||||
|
||||
child: ElevatedButton(
|
||||
onPressed: iniciarSesion,
|
||||
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.green,
|
||||
foregroundColor: Colors.white,
|
||||
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(15),
|
||||
),
|
||||
),
|
||||
|
||||
child: const Text(
|
||||
'Iniciar Sesión',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Registro
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
|
||||
children: [
|
||||
const Text(
|
||||
'¿No tienes cuenta?',
|
||||
),
|
||||
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
|
||||
child: const Text(
|
||||
'Registrarse',
|
||||
style: TextStyle(
|
||||
color: Colors.green,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
// ── 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;
|
||||
}
|
||||
Reference in New Issue
Block a user