import 'dart:async'; import 'dart:math' as math; import 'package:flutter/material.dart'; import '../../core/theme/app_colors.dart'; import '../../core/theme/app_decorations.dart'; import '../../core/theme/app_text_styles.dart'; class WalkGuideLoadingScreen extends StatefulWidget { final String subtitle; const WalkGuideLoadingScreen({ super.key, required this.subtitle, }); @override State createState() => _WalkGuideLoadingScreenState(); } class _WalkGuideLoadingScreenState extends State with SingleTickerProviderStateMixin { late final AnimationController _controller; late final Animation _logoScale; bool _showName = false; Timer? _nameTimer; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 1200), )..repeat(); _logoScale = TweenSequence([ TweenSequenceItem( tween: Tween(begin: 1.0, end: 1.08).chain( CurveTween(curve: Curves.easeInOut), ), weight: 50, ), TweenSequenceItem( tween: Tween(begin: 1.08, end: 1.0).chain( CurveTween(curve: Curves.easeInOut), ), weight: 50, ), ]).animate(_controller); _nameTimer = Timer(const Duration(milliseconds: 300), () { if (mounted) setState(() => _showName = true); }); } @override void dispose() { _nameTimer?.cancel(); _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.softBlueBg, body: DecoratedBox( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [AppColors.softBlueBg, Colors.white], ), ), child: SafeArea( child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 340), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 28), child: Column( mainAxisSize: MainAxisSize.min, children: [ AnimatedBuilder( animation: _logoScale, child: Container( width: 92, height: 92, decoration: BoxDecoration( gradient: AppDecorations.blueGradient, borderRadius: BorderRadius.circular(28), boxShadow: const [ BoxShadow( color: Color(0x334A90D9), blurRadius: 28, offset: Offset(0, 14), ), ], ), child: const Icon( Icons.navigation_rounded, color: Colors.white, size: 54, ), ), builder: (context, child) => Transform.scale( scale: _logoScale.value, child: child, ), ), const SizedBox(height: 24), AnimatedOpacity( opacity: _showName ? 1 : 0, duration: const Duration(milliseconds: 420), curve: Curves.easeOutCubic, child: Column( children: [ Text( 'WalkGuide', textAlign: TextAlign.center, style: AppTextStyles.heading.copyWith( fontSize: 32, fontWeight: FontWeight.w800, ), ), const SizedBox(height: 8), Text( widget.subtitle, textAlign: TextAlign.center, style: AppTextStyles.body.copyWith( color: AppColors.textMuted, fontWeight: FontWeight.w500, ), ), ], ), ), const SizedBox(height: 34), _DotWave(controller: _controller), ], ), ), ), ), ), ), ); } } class _DotWave extends StatelessWidget { final AnimationController controller; const _DotWave({required this.controller}); @override Widget build(BuildContext context) { return AnimatedBuilder( animation: controller, builder: (context, child) { return Row( mainAxisSize: MainAxisSize.min, children: List.generate( 3, (index) { final phase = (controller.value + index * 0.18) % 1.0; final y = math.sin(phase * math.pi * 2) * -5; final opacity = 0.45 + (math.sin(phase * math.pi * 2) + 1) * 0.25; return Transform.translate( offset: Offset(0, y), child: Container( width: 9, height: 9, margin: const EdgeInsets.symmetric(horizontal: 5), decoration: BoxDecoration( color: AppColors.primaryBlue.withValues(alpha: opacity), shape: BoxShape.circle, ), ), ); }, ), ); }, ); } }