Initial commit: SIBU 2.0 MISSION

This commit is contained in:
2026-02-21 09:53:31 -05:00
commit 0c7aa53c8b
400 changed files with 67708 additions and 0 deletions

View File

@ -0,0 +1,357 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sizer/sizer.dart';
import '../../../core/app_export.dart';
class SplashScreen extends StatefulWidget {
const SplashScreen({super.key});
@override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen>
with TickerProviderStateMixin {
late AnimationController _logoAnimationController;
late AnimationController _loadingAnimationController;
late Animation<double> _logoFadeAnimation;
late Animation<double> _logoScaleAnimation;
late Animation<double> _loadingOpacityAnimation;
bool _showLoading = false;
bool _initializationComplete = false;
String _statusMessage = 'Iniciando SIBU...';
@override
void initState() {
super.initState();
_setupAnimations();
_startInitialization();
}
void _setupAnimations() {
// Logo animation controller
_logoAnimationController = AnimationController(
duration: const Duration(milliseconds: 1500),
vsync: this,
);
// Loading animation controller
_loadingAnimationController = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
);
// Logo fade in animation
_logoFadeAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _logoAnimationController,
curve: const Interval(0.0, 0.6, curve: Curves.easeOut),
));
// Logo scale animation
_logoScaleAnimation = Tween<double>(
begin: 0.8,
end: 1.0,
).animate(CurvedAnimation(
parent: _logoAnimationController,
curve: const Interval(0.0, 0.8, curve: Curves.elasticOut),
));
// Loading indicator opacity animation
_loadingOpacityAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _loadingAnimationController,
curve: Curves.easeIn,
));
// Start logo animation
_logoAnimationController.forward();
}
Future<void> _startInitialization() async {
try {
// Show loading indicator after logo animation
await Future.delayed(const Duration(milliseconds: 1000));
setState(() {
_showLoading = true;
});
_loadingAnimationController.forward();
// Simulate initialization tasks
await _performInitializationTasks();
// Mark initialization as complete
setState(() {
_initializationComplete = true;
_statusMessage = 'Listo para usar';
});
// Navigate to main screen after brief delay
await Future.delayed(const Duration(milliseconds: 500));
_navigateToMainScreen();
} catch (e) {
_handleInitializationError(e);
}
}
Future<void> _performInitializationTasks() async {
// Task 1: Check GPS permissions
setState(() {
_statusMessage = 'Verificando permisos GPS...';
});
await Future.delayed(const Duration(milliseconds: 600));
// Task 2: Load cached route data
setState(() {
_statusMessage = 'Cargando datos de rutas...';
});
await Future.delayed(const Duration(milliseconds: 700));
// Task 3: Fetch latest bus schedules
setState(() {
_statusMessage = 'Actualizando horarios...';
});
await Future.delayed(const Duration(milliseconds: 800));
// Task 4: Prepare map tiles
setState(() {
_statusMessage = 'Preparando mapas...';
});
await Future.delayed(const Duration(milliseconds: 500));
}
void _navigateToMainScreen() {
// Navigate to map screen (main tab)
Navigator.pushReplacementNamed(context, '/map-screen');
}
void _handleInitializationError(dynamic error) {
setState(() {
_statusMessage = 'Error de conexión';
});
// Show continue offline option after 5 seconds
Future.delayed(const Duration(seconds: 5), () {
if (mounted && !_initializationComplete) {
_showContinueOfflineDialog();
}
});
}
void _showContinueOfflineDialog() {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: AppTheme.lightTheme.colorScheme.surface,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
title: Text(
'Continuar sin conexión',
style: AppTheme.lightTheme.textTheme.titleLarge?.copyWith(
color: AppTheme.lightTheme.colorScheme.onSurface,
),
),
content: Text(
'No se pudo conectar al servidor. ¿Deseas continuar con datos guardados?',
style: AppTheme.lightTheme.textTheme.bodyMedium?.copyWith(
color: AppTheme.lightTheme.colorScheme.onSurface,
),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
_startInitialization(); // Retry
},
child: Text(
'Reintentar',
style: TextStyle(
color: AppTheme.lightTheme.colorScheme.primary,
),
),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
_navigateToMainScreen();
},
style: ElevatedButton.styleFrom(
backgroundColor: AppTheme.accentYellow,
foregroundColor: AppTheme.primaryBlack,
),
child: const Text('Continuar'),
),
],
);
},
);
}
Widget _buildLogo() {
return AnimatedBuilder(
animation: _logoAnimationController,
builder: (context, child) {
return Transform.scale(
scale: _logoScaleAnimation.value,
child: Opacity(
opacity: _logoFadeAnimation.value,
child: Container(
width: 35.w,
height: 35.w,
decoration: BoxDecoration(
color: AppTheme.accentYellow,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: AppTheme.primaryBlack.withValues(alpha: 0.2),
blurRadius: 20,
offset: const Offset(0, 8),
),
],
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomIconWidget(
iconName: 'directions_bus',
color: AppTheme.primaryBlack,
size: 12.w,
),
SizedBox(height: 1.h),
Text(
'SIBU',
style:
AppTheme.lightTheme.textTheme.headlineSmall?.copyWith(
color: AppTheme.primaryBlack,
fontWeight: FontWeight.bold,
letterSpacing: 2,
),
),
],
),
),
),
),
);
},
);
}
Widget _buildLoadingIndicator() {
return _showLoading
? AnimatedBuilder(
animation: _loadingAnimationController,
builder: (context, child) {
return Opacity(
opacity: _loadingOpacityAnimation.value,
child: Column(
children: [
SizedBox(height: 8.h),
SizedBox(
width: 8.w,
height: 8.w,
child: CircularProgressIndicator(
strokeWidth: 3,
valueColor: AlwaysStoppedAnimation<Color>(
AppTheme.accentYellow,
),
backgroundColor:
AppTheme.accentYellow.withValues(alpha: 0.3),
),
),
SizedBox(height: 3.h),
Text(
_statusMessage,
style: AppTheme.lightTheme.textTheme.bodyMedium?.copyWith(
color: AppTheme.surfaceWhite.withValues(alpha: 0.8),
),
textAlign: TextAlign.center,
),
],
),
);
},
)
: const SizedBox.shrink();
}
Widget _buildVersionInfo() {
return Positioned(
bottom: 8.h,
left: 0,
right: 0,
child: Column(
children: [
Text(
'Transporte Público Boquete',
style: AppTheme.lightTheme.textTheme.bodySmall?.copyWith(
color: AppTheme.surfaceWhite.withValues(alpha: 0.6),
),
),
SizedBox(height: 1.h),
Text(
'Versión 1.0.0',
style: AppTheme.lightTheme.textTheme.labelSmall?.copyWith(
color: AppTheme.surfaceWhite.withValues(alpha: 0.4),
),
),
],
),
);
}
@override
void dispose() {
_logoAnimationController.dispose();
_loadingAnimationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
statusBarBrightness: Brightness.dark,
systemNavigationBarColor: AppTheme.primaryBlack,
systemNavigationBarIconBrightness: Brightness.light,
),
child: Scaffold(
backgroundColor: AppTheme.primaryBlack,
body: SafeArea(
child: SizedBox(
width: double.infinity,
height: double.infinity,
child: Stack(
children: [
// Main content
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildLogo(),
_buildLoadingIndicator(),
],
),
),
// Version info
_buildVersionInfo(),
],
),
),
),
),
);
}
}