Files
ProxyTrash/lib/screens/address_screen.dart
2026-05-23 05:33:39 -06:00

255 lines
9.2 KiB
Dart

import 'package:flutter/material.dart';
import '../app_config.dart';
import '../models/address_entry.dart';
import '../models/auth_session.dart';
import '../services/auth_repository.dart';
import '../services/address_repository.dart';
import 'dashboard_screen.dart';
class AddressScreen extends StatefulWidget {
const AddressScreen({
super.key,
required this.authRepository,
required this.addressRepository,
required this.session,
this.enableLiveFeatures = true,
});
final AuthRepository authRepository;
final AddressRepository addressRepository;
final AuthSession session;
final bool enableLiveFeatures;
@override
State<AddressScreen> createState() => _AddressScreenState();
}
class _AddressScreenState extends State<AddressScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController _houseNumberController = TextEditingController();
final TextEditingController _coloniaController = TextEditingController();
final TextEditingController _streetController = TextEditingController();
bool _isSaving = false;
String? _errorMessage;
@override
void dispose() {
_houseNumberController.dispose();
_coloniaController.dispose();
_streetController.dispose();
super.dispose();
}
Future<void> _saveAddress() async {
if (!(_formKey.currentState?.validate() ?? false)) {
return;
}
setState(() {
_isSaving = true;
_errorMessage = null;
});
try {
await widget.addressRepository.saveAddress(
session: widget.session,
address: AddressEntry(
houseNumber: _houseNumberController.text.trim(),
colonia: _coloniaController.text.trim(),
street: _streetController.text.trim(),
),
);
if (!mounted) {
return;
}
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (_) => DashboardScreen(
authRepository: widget.authRepository,
addressRepository: widget.addressRepository,
session: widget.session,
savedAddress: AddressEntry(
houseNumber: _houseNumberController.text.trim(),
colonia: _coloniaController.text.trim(),
street: _streetController.text.trim(),
),
enableLiveFeatures: widget.enableLiveFeatures,
),
),
(route) => false,
);
} on AddressException catch (error) {
if (!mounted) {
return;
}
setState(() {
_errorMessage = error.message;
});
} catch (_) {
if (!mounted) {
return;
}
setState(() {
_errorMessage = 'No se pudo guardar la dirección. Revisa el backend.';
});
} finally {
if (mounted) {
setState(() {
_isSaving = false;
});
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xFFF8FAFC), Color(0xFFE2E8F0), Color(0xFFCCFBF1)],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
child: SafeArea(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 520),
child: Card(
elevation: 12,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(28)),
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
'Dirección',
style: TextStyle(fontSize: 30, fontWeight: FontWeight.w800),
),
const SizedBox(height: 8),
Text(
'Ingresa la dirección de tu casa y se enviará al backend para guardarla en PostgreSQL.',
style: TextStyle(color: Colors.grey.shade700, height: 1.4),
),
const SizedBox(height: 20),
if (_errorMessage != null) ...[
_AddressStatusBanner(message: _errorMessage!),
const SizedBox(height: 16),
],
Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _houseNumberController,
keyboardType: TextInputType.text,
decoration: const InputDecoration(
labelText: 'Número de casa',
prefixIcon: Icon(Icons.home_outlined),
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Ingresa el número de casa';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _coloniaController,
decoration: const InputDecoration(
labelText: 'Colonia',
prefixIcon: Icon(Icons.location_city_outlined),
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Ingresa la colonia';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _streetController,
decoration: const InputDecoration(
labelText: 'Calle',
prefixIcon: Icon(Icons.signpost_outlined),
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Ingresa la calle';
}
return null;
},
),
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
height: 52,
child: FilledButton(
onPressed: _isSaving ? null : _saveAddress,
child: _isSaving
? const SizedBox(
width: 22,
height: 22,
child: CircularProgressIndicator(strokeWidth: 2.2, color: Colors.white),
)
: const Text('Guardar dirección'),
),
),
const SizedBox(height: 12),
Text(
'Base URL configurada: ${AppConfig.apiBaseUrl}',
style: TextStyle(fontSize: 12, color: Colors.grey.shade600),
textAlign: TextAlign.center,
),
],
),
),
],
),
),
),
),
),
),
),
),
);
}
}
class _AddressStatusBanner extends StatelessWidget {
const _AddressStatusBanner({required this.message});
final String message;
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: const Color(0xFFFEE2E2),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFFCA5A5)),
),
child: Text(
message,
style: const TextStyle(color: Color(0xFF991B1B), height: 1.35),
),
);
}
}