import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/assets.dart'; import 'package:didvan/main.dart'; import 'package:didvan/providers/media.dart'; import 'package:didvan/providers/server_data.dart'; import 'package:didvan/providers/theme.dart'; import 'package:didvan/providers/user.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/services/app_initalizer.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/storage/storage.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; import 'package:universal_html/html.dart' as html; class Splash extends StatefulWidget { const Splash({Key? key}) : super(key: key); @override State createState() => _SplashState(); } class _SplashState extends State with TickerProviderStateMixin { bool _errorOccured = false; late ThemeProvider themeProvider; late UserProvider userProvider; late MediaProvider mediaProvider; late AnimationController _pulseController; late Animation _pulseAnimation; @override void initState() { super.initState(); // Initialize animation controller _pulseController = AnimationController( duration: const Duration(milliseconds: 1500), vsync: this, ); _pulseAnimation = Tween( begin: 1.0, end: 1.1, ).animate(CurvedAnimation( parent: _pulseController, curve: Curves.easeInOut, )); // Start pulse animation _pulseController.repeat(reverse: true); themeProvider = context.read(); userProvider = context.read(); mediaProvider = context.read(); _initialize(themeProvider, userProvider, mediaProvider); } @override void dispose() { _pulseController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; final isDark = Theme.of(context).brightness == Brightness.light; return AnnotatedRegion( value: DesignConfig.systemUiOverlayStyle.copyWith( systemNavigationBarColor: colorScheme.background, statusBarColor: Colors.transparent, statusBarIconBrightness: isDark ? Brightness.light : Brightness.dark, ), child: Scaffold( backgroundColor: colorScheme.background, body: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ colorScheme.background, colorScheme.background.withOpacity(0.8), colorScheme.primary.withOpacity(0.05), ], ), ), child: Stack( children: [ // Floating particles background _buildFloatingParticles(colorScheme), SafeArea( child: Column( children: [ // Top section with logo Expanded( flex: 3, child: Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 32), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // Logo with subtle animation TweenAnimationBuilder( duration: const Duration(milliseconds: 800), tween: Tween(begin: 0.0, end: 1.0), builder: (context, value, child) { return Transform.scale( scale: 0.7 + (0.3 * value), child: Opacity( opacity: value, child: AnimatedBuilder( animation: _pulseAnimation, builder: (context, child) { return Transform.scale( scale: _pulseAnimation.value, child: Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), gradient: RadialGradient( colors: [ colorScheme.primary.withOpacity(0.05), Colors.transparent, ], ), boxShadow: [ BoxShadow( color: colorScheme.primary.withOpacity(0.15), blurRadius: 30, spreadRadius: 8, offset: const Offset(0, 15), ), ], ), child: SvgPicture.asset( Assets.horizontalLogoWithText, height: 80, ), ), ); }, ), ), ); }, ), ], ), ), ), // Bottom section with loading/error Expanded( flex: 2, child: Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 48), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ if (!_errorOccured) ...[ // Modern loading indicator _buildModernLoader(colorScheme), const SizedBox(height: 24), // Loading text with fade animation AnimatedBuilder( animation: _pulseController, builder: (context, child) { return Opacity( opacity: 0.4 + (0.4 * _pulseController.value), child: Text( 'در حال بارگذاری...', style: TextStyle( fontSize: 16, color: colorScheme.checkFav.withOpacity(0.7), fontWeight: FontWeight.w400, letterSpacing: 0.8, ), ), ); }, ), ], if (_errorOccured) ...[ // Error state with simple and clean design Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // Simple error icon Container( width: 60, height: 60, decoration: BoxDecoration( color: colorScheme.error.withOpacity(0.1), borderRadius: BorderRadius.circular(30), ), child: Icon( Icons.wifi_off_rounded, size: 28, color: colorScheme.error, ), ), const SizedBox(height: 20), // Clean error message Text( 'مشکل در اتصال', style: TextStyle( fontSize: 16, color: colorScheme.checkFav, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 8), Text( 'لطفاً اتصال خود را بررسی کنید', style: TextStyle( fontSize: 14, color: colorScheme.checkFav.withOpacity(0.6), fontWeight: FontWeight.w400, ), ), const SizedBox(height: 24), // Simple retry button _buildRetryButton(colorScheme), ], ), ], ], ), ), ), // Brand tagline at bottom Padding( padding: const EdgeInsets.only(bottom: 32), child: TweenAnimationBuilder( duration: const Duration(milliseconds: 1000), tween: Tween(begin: 0.0, end: 1.0), builder: (context, value, child) { return Opacity( opacity: value * 0.6, child: Text( 'توسعه یافته توسط فرتاک', style: TextStyle( fontSize: 14, color: colorScheme.checkFav, fontWeight: FontWeight.w500, letterSpacing: 1, ), ), ); }, ), ), ], ), ), ], ), ), ), ); } Widget _buildFloatingParticles(ColorScheme colorScheme) { return AnimatedBuilder( animation: _pulseController, builder: (context, child) { return Stack( children: [ // Particle 1 Positioned( top: 100 + (20 * _pulseController.value), right: 50 + (10 * _pulseController.value), child: Container( width: 6, height: 6, decoration: BoxDecoration( color: colorScheme.primary.withOpacity(0.3), shape: BoxShape.circle, ), ), ), // Particle 2 Positioned( top: 200 + (15 * (1 - _pulseController.value)), left: 80 + (8 * _pulseController.value), child: Container( width: 4, height: 4, decoration: BoxDecoration( color: colorScheme.secondary.withOpacity(0.4), shape: BoxShape.circle, ), ), ), // Particle 3 Positioned( bottom: 180 + (25 * _pulseController.value), right: 100 + (12 * (1 - _pulseController.value)), child: Container( width: 8, height: 8, decoration: BoxDecoration( color: colorScheme.primary.withOpacity(0.2), shape: BoxShape.circle, boxShadow: [ BoxShadow( color: colorScheme.primary.withOpacity(0.1), blurRadius: 10, spreadRadius: 3, ), ], ), ), ), // Particle 4 Positioned( bottom: 120 + (18 * (1 - _pulseController.value)), left: 60 + (15 * _pulseController.value), child: Container( width: 5, height: 5, decoration: BoxDecoration( color: colorScheme.tertiary.withOpacity(0.35), shape: BoxShape.circle, ), ), ), ], ); }, ); } Widget _buildModernLoader(ColorScheme colorScheme) { // استفاده از loading animation اصلی و قدیمی دیدوان return SizedBox( width: 60, height: 60, child: Image.asset( Assets.loadingAnimation, width: 60, height: 60, fit: BoxFit.contain, ), ); } Widget _buildRetryButton(ColorScheme colorScheme) { return SizedBox( width: 140, height: 44, child: ElevatedButton( onPressed: () { setState(() { _errorOccured = false; }); _initialize(themeProvider, userProvider, mediaProvider); }, style: ElevatedButton.styleFrom( backgroundColor: colorScheme.primary, foregroundColor: colorScheme.onPrimary, elevation: 0, shadowColor: Colors.transparent, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(22), ), ), child: const Text( 'تلاش مجدد', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), ), ); } Future _initialize(ThemeProvider themeProvider, UserProvider userProvider, MediaProvider mediaProvider) async { try { print("checking if is in browser"); if (kIsWeb) { print("detected browser"); html.window.onBeforeUnload.listen((event) { StorageService.webStorage .removeWhere((key, value) => key == 'image-cache'); }); } final settingsData = await AppInitializer.initilizeSettings(); themeProvider.themeMode = settingsData.themeMode; themeProvider.fontFamily = settingsData.fontFamily; themeProvider.fontScale = settingsData.fontScale; await AppInitializer.setupServices(navigatorKey.currentContext!); final String? token = await userProvider.setAndGetToken(); print("detected token as $token"); if (token != null) { RequestService.token = token; if (!kIsWeb) { await mediaProvider.getDownloadsList(); } final result = await userProvider.getUserInfo(); if (!result) { print("no results were returned for user info"); try { StorageService.delete(key: 'token'); } catch (e) { print("error in case of no user info result: $e"); // catch } navigatorKey.currentState!.pushNamedAndRemoveUntil( Routes.splash, (_) => false, ); return; } print("got results for user info: $result"); await ServerDataProvider.getData(); } print("token route is $token"); String extractedPath = initialURI?.path.toString() == '/' ? Routes.home : initialURI?.path.toString() ?? Routes.home; final String destinationRoute = token == null ? Routes.authenticaion : extractedPath; // Set showDialogs to false to prevent welcome popups dynamic routeArguments = token == null ? {'isResetPassword': false} : {'showDialogs': false}; if (destinationRoute == Routes.home) { print("destination route was home and init uri is $initialURI"); // (routeArguments as Map)['deepLinkUri'] = initialURI; initialURI = null; } if(destinationRoute == Routes.authenticaion){ print("destination route is auth route"); routeArguments = false; } print("destination route: $destinationRoute, route args: $routeArguments"); await navigatorKey.currentState!.pushReplacementNamed( destinationRoute, arguments: routeArguments, ); } catch (e) { setState(() { _errorOccured = true; }); } } }