import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; import '../services/firebase_service.dart'; import 'auth/login_screen.dart'; import 'home/home_screen.dart'; import 'onboarding_screen.dart'; import 'onboarding/early_setup_screen.dart'; class SplashScreen extends ConsumerStatefulWidget { const SplashScreen({super.key}); @override ConsumerState createState() => _SplashScreenState(); } class _SplashScreenState extends ConsumerState with SingleTickerProviderStateMixin { @override void initState() { super.initState(); _initializeApp(); } Future _initializeApp() async { try { debugPrint('🚀 Starting app initialization...'); // Start minimum timer final minTimer = Future.delayed(const Duration(seconds: 3)); // Attempt to load resources with a timeout limit try { await Future.any([ Future.wait([ _checkConnectivity(), _checkServerHealth(), _preloadData(), _precacheNetworkImages(), ]), Future.delayed(const Duration(seconds: 5)), ]); } catch (e) { debugPrint('Resource loading error/timeout: $e'); } // Ensure minimum splash time has passed await minTimer; debugPrint('✅ Splash timer finished, navigating...'); if (mounted) { // Check if onboarding has been completed final prefs = await SharedPreferences.getInstance(); final isFirstLaunch = prefs.getBool('is_first_launch') ?? true; if (isFirstLaunch) { // FIRST LAUNCH: Always show Onboarding, sign out any stale sessions debugPrint('🆕 First launch -> Showing Onboarding'); final user = FirebaseService().currentUser; if (user != null) { debugPrint('⚠️ Stale auth session found -> Signing out'); await FirebaseService().signOut(); } if (mounted) { Navigator.of(context).pushReplacement( PageRouteBuilder( pageBuilder: (_, __, ___) => const OnboardingScreen(), transitionDuration: const Duration(milliseconds: 500), transitionsBuilder: (_, a, __, c) => FadeTransition(opacity: a, child: c), ), ); } return; } final user = FirebaseService().currentUser; debugPrint( '👤 User status: ${user != null ? "Logged In (${user.uid})" : "Guest"}', ); if (user != null) { // Check if user data exists and setup is complete final userData = await FirebaseService().getUserData(user.uid); if (userData == null) { debugPrint('⚠️ User data missing -> Forcing Sign Out'); await FirebaseService().signOut(); if (mounted) { Navigator.of(context).pushReplacement( PageRouteBuilder( pageBuilder: (_, __, ___) => const OnboardingScreen(), transitionDuration: const Duration(milliseconds: 500), transitionsBuilder: (_, a, __, c) => FadeTransition(opacity: a, child: c), ), ); } } else if (!userData.isSetupComplete) { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (_) => const EarlySetupScreen()), ); } else { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (_) => const HomeScreen()), ); } } else { Navigator.of(context).pushReplacement( PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => const OnboardingScreen(), transitionDuration: const Duration(milliseconds: 500), transitionsBuilder: (context, animation, secondaryAnimation, child) { return FadeTransition( opacity: animation.drive( Tween( begin: 0.0, end: 1.0, ).chain(CurveTween(curve: Curves.easeOut)), ), child: child, ); }, ), ); } } } catch (e) { debugPrint('Splash initialization error: $e'); if (mounted) { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (context) => const LoginScreen()), ); } } } /// Check network connectivity silently in background Future _checkConnectivity() async { try { final result = await Connectivity().checkConnectivity(); final isConnected = result != ConnectivityResult.none; debugPrint( '🌐 Network connectivity: ${isConnected ? "Connected" : "Offline"}', ); return isConnected; } catch (e) { debugPrint('⚠️ Connectivity check failed: $e'); return false; } } /// Verify server health and API accessibility Future _checkServerHealth() async { try { // Get API URL from config final apiUrl = 'https://dev-api.muningkofie.com'; // Send health check request with timeout final response = await http .get(Uri.parse('$apiUrl/health')) .timeout( const Duration(seconds: 5), onTimeout: () { debugPrint('⚠️ Server health check timeout'); return http.Response('Timeout', 408); }, ); final isHealthy = response.statusCode == 200; debugPrint( '🏥 Server health: ${isHealthy ? "OK" : "Unavailable (${response.statusCode})"}', ); return isHealthy; } catch (e) { debugPrint('⚠️ Server health check failed: $e'); return false; } } /// Preload essential data and resources Future _preloadData() async { try { debugPrint('📦 Preloading essential resources...'); // Preload user preferences from local storage await _loadUserPreferences(); // Cache important assets await _precacheAssets(); debugPrint('✅ Resources preloaded successfully'); } catch (e) { debugPrint('⚠️ Resource preloading failed: $e'); } } /// Load user preferences from SharedPreferences Future _loadUserPreferences() async { try { final sharedPrefs = await SharedPreferences.getInstance(); // Check if first launch final isFirstLaunch = sharedPrefs.getBool('is_first_launch') ?? true; debugPrint('📱 First launch: $isFirstLaunch'); } catch (e) { debugPrint('⚠️ Failed to load preferences: $e'); } } /// Precache important image assets Future _precacheAssets() async { try { if (mounted) { // Precache commonly used images to improve performance // Currently only GIF is being used, but can add more await precacheImage( const AssetImage('assets/animations/loginsplashscreen.gif'), context, ); } } catch (e) { debugPrint('⚠️ Asset precaching failed: $e'); } } /// Precache network images for faster loading on subsequent screens Future _precacheNetworkImages() async { try { if (mounted) { debugPrint('🌐 Precaching network images...'); // Google logo for login screen - use ImageStream to ensure full load const googleLogoUrl = 'https://developers.google.com/identity/images/g-logo.png'; final imageProvider = NetworkImage(googleLogoUrl); final completer = Completer(); final imageStream = imageProvider.resolve(ImageConfiguration.empty); late ImageStreamListener listener; listener = ImageStreamListener( (ImageInfo info, bool synchronousCall) { debugPrint( '✅ Google logo loaded: ${info.image.width}x${info.image.height}', ); imageStream.removeListener(listener); if (!completer.isCompleted) completer.complete(); }, onError: (exception, stackTrace) { debugPrint('⚠️ Failed to load Google logo: $exception'); imageStream.removeListener(listener); if (!completer.isCompleted) completer.complete(); }, ); imageStream.addListener(listener); // Wait for image to fully load await completer.future; // Also precache using Flutter's built-in method for caching if (mounted) { await precacheImage(imageProvider, context); } debugPrint('✅ Network images precached successfully'); } } catch (e) { debugPrint('⚠️ Network image precaching failed: $e'); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, body: SafeArea( child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: 300, height: 300, child: Image.asset( 'assets/animations/loginsplashscreen.gif', fit: BoxFit.contain, ), ) .animate() .fade(duration: 800.ms) .scale( delay: 200.ms, duration: 800.ms, curve: Curves.easeOutBack, ), ], ), ), ), ); } }