import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; import 'package:mobile_number/mobile_number.dart'; import 'dart:math' as math; import '../theme.dart'; import '../route_transitions.dart'; import '../feedback_helper.dart'; import 'mobile_login_screen.dart'; class WelcomeScreen extends StatefulWidget { const WelcomeScreen({super.key}); @override State createState() => _WelcomeScreenState(); } class _WelcomeScreenState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _splashScaleAnimation; late Animation _splashLogoOpacityAnimation; late Animation _splashContainerOpacityAnimation; late Animation _contentFadeAnimation; late Animation _contentSlideAnimation; String? _detectedMobileNumber; @override void initState() { super.initState(); _initMobileNumberDetection(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 1400), ); // 1. Splash Logo Zoom/Scale-down (from 3.0 to 1.0) _splashScaleAnimation = Tween(begin: 3.0, end: 1.0).animate( CurvedAnimation( parent: _controller, curve: const Interval(0.0, 0.45, curve: Curves.easeOutBack), ), ); // 2. Splash Logo Fade-in as it scales down _splashLogoOpacityAnimation = Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation( parent: _controller, curve: const Interval(0.0, 0.35, curve: Curves.easeIn), ), ); // 3. Splash Container Fade-out _splashContainerOpacityAnimation = Tween(begin: 1.0, end: 0.0).animate( CurvedAnimation( parent: _controller, curve: const Interval(0.45, 0.6, curve: Curves.easeOut), ), ); // 4. Welcome Content Fade-in _contentFadeAnimation = Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation( parent: _controller, curve: const Interval(0.55, 1.0, curve: Curves.easeIn), ), ); // 5. Welcome Content Slide-up _contentSlideAnimation = Tween( begin: const Offset(0.0, 0.05), end: Offset.zero, ).animate( CurvedAnimation( parent: _controller, curve: const Interval(0.55, 1.0, curve: Curves.easeOutCubic), ), ); _controller.forward(); Future.delayed(const Duration(milliseconds: 300), () { if (mounted) { AppFeedback.success(); } }); } Future _initMobileNumberDetection() async { try { if (!kIsWeb && defaultTargetPlatform == TargetPlatform.android) { if (await MobileNumber.hasPhonePermission) { final String? mobileNumber = await MobileNumber.mobileNumber; if (mobileNumber != null && mobileNumber.isNotEmpty) { // Extract 10 digits if possible, else just use the number final cleaned = mobileNumber.replaceAll(RegExp(r'\D'), ''); if (cleaned.length >= 10) { _detectedMobileNumber = cleaned.substring(cleaned.length - 10); } else { _detectedMobileNumber = mobileNumber; } return; } } } } catch (e) { debugPrint('Error getting mobile number: $e'); } // Fallback to mock number _detectedMobileNumber = '9988776655'; } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final isMobile = context.isMobile; return Scaffold( body: AnimatedBuilder( animation: _controller, builder: (context, child) { final showSplash = _controller.value < 0.6; return Stack( children: [ // Main Welcome Content Opacity( opacity: _contentFadeAnimation.value, child: SlideTransition( position: _contentSlideAnimation, child: IgnorePointer( ignoring: showSplash, child: child, ), ), ), // Splash Overlay (Centered Logo scaling down) if (showSplash) Opacity( opacity: _splashContainerOpacityAnimation.value, child: Container( color: Colors.white, alignment: Alignment.center, child: ScaleTransition( scale: _splashScaleAnimation, child: Opacity( opacity: _splashLogoOpacityAnimation.value, child: Container( width: 140, height: 140, decoration: BoxDecoration( borderRadius: BorderRadius.circular(32), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.08), blurRadius: 24, offset: const Offset(0, 8), ) ], ), child: ClipRRect( borderRadius: BorderRadius.circular(32), child: Image.asset( 'assets/logo/pozo_logo.jpg', fit: BoxFit.cover, ), ), ), ), ), ), ), ], ); }, child: SafeArea( child: Column( children: [ // Top Progress bar indicator - 1st segment active + Quick Feedback Settings Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), child: Row( children: [ Expanded( child: Row( children: List.generate(5, (index) { return Expanded( child: AnimatedContainer( duration: const Duration(milliseconds: 350), height: 4, margin: const EdgeInsets.symmetric(horizontal: 2.0), decoration: BoxDecoration( color: index == 0 ? theme.colorScheme.primary : theme.colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(2), ), ), ); }), ), ), const SizedBox(width: 12), _buildSettingsButton(theme), ], ), ), // Scrollable / Responsive Content Expanded( child: LayoutBuilder( builder: (context, constraints) { final maxWidth = constraints.maxWidth; if (maxWidth >= 768) { // Desktop / Tablet Double-Column Layout return SingleChildScrollView( physics: const BouncingScrollPhysics(), child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 1000), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ // Left column: content + button Expanded( flex: 5, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildLogo(theme), const SizedBox(height: 16), Text( 'Manage your business billing in 3 clicks!', style: theme.textTheme.headlineLarge?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), Text( 'The fastest, most reliable tool for Indian MSMEs to run their shop.', style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 16), _buildSeal(theme), const SizedBox(height: 16), _buildCTAColumn(context, theme), ], ), ), const SizedBox(width: 32), // Right column: mock card Expanded( flex: 4, child: Center( child: _buildMockDashboard(theme), ), ), ], ), ), ), ), ); } else { // Mobile Single Column Layout return SingleChildScrollView( physics: const BouncingScrollPhysics(), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 16), _buildLogo(theme), const SizedBox(height: 16), Text( 'Manage your business billing in 3 clicks!', textAlign: TextAlign.left, style: theme.textTheme.headlineLarge?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), Text( 'The fastest, most reliable tool for Indian MSMEs to run their shop.', textAlign: TextAlign.left, style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 16), _buildSeal(theme), const SizedBox(height: 24), Center(child: _buildMockDashboard(theme)), const SizedBox(height: 24), ], ), ), ); } }, ), ), // Pinned Bottom Button on Mobile if (isMobile) Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), child: _buildCTAColumn(context, theme), ), ], ), ), ), ); } Widget _buildLogo(ThemeData theme) { return Container( width: 80, height: 80, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.12), blurRadius: 20, offset: const Offset(0, 6), ) ], ), child: ClipRRect( borderRadius: BorderRadius.circular(20), child: Image.asset( 'assets/logo/pozo_logo.jpg', fit: BoxFit.cover, ), ), ); } Widget _buildSeal(ThemeData theme) { return Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), decoration: BoxDecoration( color: theme.colorScheme.surfaceContainerLow, borderRadius: BorderRadius.circular(20), border: Border.all(color: theme.colorScheme.outlineVariant), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.verified, size: 16, color: theme.colorScheme.tertiary, ), const SizedBox(width: 6), Text( 'Trusted by 10,000+ merchants', style: theme.textTheme.bodySmall?.copyWith( fontWeight: FontWeight.w600, color: theme.colorScheme.onSurfaceVariant, ), ), ], ), ); } Widget _buildMockDashboard(ThemeData theme) { return const Center( child: RetailUniverseVisual(), ); } Widget _buildCTAColumn(BuildContext context, ThemeData theme) { return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: context.isMobile ? CrossAxisAlignment.center : CrossAxisAlignment.start, children: [ InteractiveScale( child: ElevatedButton( onPressed: () { AppFeedback.click(); Navigator.push( context, FadeSlidePageRoute(child: MobileLoginScreen(autoFilledMobileNumber: _detectedMobileNumber)), ); }, style: ElevatedButton.styleFrom( minimumSize: const Size(260, 54), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: const [ Text('Get Started'), SizedBox(width: 8), Icon(Icons.arrow_forward, size: 18), ], ), ), ), const SizedBox(height: 16), Row( mainAxisAlignment: context.isMobile ? MainAxisAlignment.center : MainAxisAlignment.start, children: [ Container( width: 10, height: 10, decoration: BoxDecoration( color: theme.colorScheme.primary, shape: BoxShape.circle, ), ), const SizedBox(width: 6), Container( width: 8, height: 8, decoration: BoxDecoration( color: Colors.grey[300], shape: BoxShape.circle, ), ), const SizedBox(width: 6), Container( width: 8, height: 8, decoration: BoxDecoration( color: Colors.grey[300], shape: BoxShape.circle, ), ), ], ) ], ); } Widget _buildSettingsButton(ThemeData theme) { final hasSound = AppFeedback.isSoundEnabled; final hasHaptic = AppFeedback.isHapticEnabled; return Material( color: Colors.transparent, child: InkWell( onTap: () => _showFeedbackSettings(theme), borderRadius: BorderRadius.circular(20), child: Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), decoration: BoxDecoration( color: theme.colorScheme.surfaceContainerLow, borderRadius: BorderRadius.circular(20), border: Border.all(color: theme.colorScheme.outlineVariant), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( hasSound ? Icons.volume_up_rounded : Icons.volume_off_rounded, size: 16, color: theme.colorScheme.primary, ), const SizedBox(width: 6), Icon( hasHaptic ? Icons.vibration_rounded : Icons.phone_android_rounded, size: 16, color: theme.colorScheme.primary, ), ], ), ), ), ); } void _showFeedbackSettings(ThemeData theme) { AppFeedback.click(); showModalBottomSheet( context: context, backgroundColor: Colors.white, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(24)), ), builder: (context) { return StatefulBuilder( builder: (context, setModalState) { return SafeArea( child: Padding( padding: const EdgeInsets.all(24.0), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Feedback Settings', style: theme.textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, ), ), IconButton( icon: const Icon(Icons.close), onPressed: () { AppFeedback.click(); Navigator.pop(context); }, ), ], ), const SizedBox(height: 16), ListTile( leading: Icon( AppFeedback.isSoundEnabled ? Icons.volume_up : Icons.volume_off, color: theme.colorScheme.primary, ), title: const Text('Sound Effects', style: TextStyle(fontWeight: FontWeight.bold)), subtitle: const Text('Play subtle sounds on button clicks & selections'), trailing: Switch( value: AppFeedback.isSoundEnabled, activeColor: theme.colorScheme.primary, onChanged: (val) { AppFeedback.isSoundEnabled = val; AppFeedback.click(); setModalState(() {}); setState(() {}); }, ), ), const Divider(), ListTile( leading: Icon( AppFeedback.isHapticEnabled ? Icons.vibration : Icons.mobile_off, color: theme.colorScheme.primary, ), title: const Text('Haptic Vibration', style: TextStyle(fontWeight: FontWeight.bold)), subtitle: const Text('Feel tactical vibrations on user interaction'), trailing: Switch( value: AppFeedback.isHapticEnabled, activeColor: theme.colorScheme.primary, onChanged: (val) { AppFeedback.isHapticEnabled = val; AppFeedback.click(); setModalState(() {}); setState(() {}); }, ), ), const SizedBox(height: 16), ], ), ), ); }, ); }, ); } } class RetailUniverseVisual extends StatefulWidget { const RetailUniverseVisual({super.key}); @override State createState() => _RetailUniverseVisualState(); } class _RetailUniverseVisualState extends State with TickerProviderStateMixin { late AnimationController _orbitController; late AnimationController _pulseController; int _activeIndex = 0; final List> _retailUniverse = [ { 'title': 'Grocery & Retail', 'subtitle': 'Groceries, Wholesale, Dept. Stores, Xerox, Stationery', 'icon': Icons.shopping_basket_rounded, 'color': Colors.green, 'bgColor': const Color(0xFFE8F5E9), }, { 'title': 'Food & Beverages', 'subtitle': 'Restaurants, Bakeries, Dairy, Ice Cream, Coffee Shops', 'icon': Icons.restaurant_rounded, 'color': Colors.orange, 'bgColor': const Color(0xFFFFF3E0), }, { 'title': 'Wellness & Beauty', 'subtitle': 'Salon, Spa, Beauty Parlours, Cosmetics, Fitness', 'icon': Icons.spa_rounded, 'color': Colors.pink, 'bgColor': const Color(0xFFFCE4EC), }, { 'title': 'Fashion & Lifestyle', 'subtitle': 'Fashion & Jewellery Boutique, Lifestyle, Footwear Stores', 'icon': Icons.checkroom_rounded, 'color': Colors.indigo, 'bgColor': const Color(0xFFE8EAF6), }, { 'title': 'Electronics & Tech', 'subtitle': 'Electricals, Computers, Mobiles, Appliances', 'icon': Icons.computer_rounded, 'color': Colors.blue, 'bgColor': const Color(0xFFE3F2FD), }, { 'title': 'Logistics & Healthcare', 'subtitle': 'PozoHealth, Smart Parking, Boating, Transport & Logistics', 'icon': Icons.local_hospital_rounded, 'color': Colors.teal, 'bgColor': const Color(0xFFE0F2F1), }, ]; @override void initState() { super.initState(); _orbitController = AnimationController( vsync: this, duration: const Duration(seconds: 30), )..repeat(); _pulseController = AnimationController( vsync: this, duration: const Duration(milliseconds: 1500), )..repeat(reverse: true); _startAutoRotation(); } void _startAutoRotation() { Future.delayed(const Duration(milliseconds: 3500), _nextCategory); } void _nextCategory() { if (!mounted) return; setState(() { _activeIndex = (_activeIndex + 1) % _retailUniverse.length; }); _startAutoRotation(); } void _setActiveCategory(int index) { AppFeedback.select(); setState(() { _activeIndex = index; }); } @override void dispose() { _orbitController.dispose(); _pulseController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final activeData = _retailUniverse[_activeIndex]; return Container( width: 320, height: 320, decoration: const BoxDecoration( color: Colors.transparent, ), child: Stack( alignment: Alignment.center, clipBehavior: Clip.none, children: [ // 1. Orbital circular path Container( width: 200, height: 200, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( color: Colors.grey[200]!, width: 1.5, ), ), ), // 2. Rotating orbital icons AnimatedBuilder( animation: _orbitController, builder: (context, child) { final double baseAngle = _orbitController.value * 2 * math.pi; return Stack( children: List.generate(_retailUniverse.length, (index) { final double angle = baseAngle + (index * 2 * math.pi / _retailUniverse.length); const double radius = 100.0; final double x = radius * math.cos(angle); final double y = radius * math.sin(angle); final isCurrent = index == _activeIndex; final item = _retailUniverse[index]; return Positioned( left: 160.0 + x - 24.0, top: 160.0 + y - 24.0, child: InteractiveScale( onTap: () => _setActiveCategory(index), child: Container( width: 48, height: 48, decoration: BoxDecoration( color: isCurrent ? item['bgColor'] : Colors.white, shape: BoxShape.circle, border: Border.all( color: isCurrent ? item['color'] : Colors.grey[300]!, width: isCurrent ? 2 : 1, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 6, offset: const Offset(0, 2), ), ], ), child: Icon( item['icon'], color: isCurrent ? item['color'] : Colors.grey[500], size: 20, ), ), ), ); }), ); }, ), // 3. Central active node with pulse animation ScaleTransition( scale: Tween(begin: 0.95, end: 1.05).animate( CurvedAnimation( parent: _pulseController, curve: Curves.easeInOut, ), ), child: Container( width: 110, height: 110, decoration: BoxDecoration( color: activeData['bgColor'], shape: BoxShape.circle, border: Border.all( color: activeData['color'], width: 3, ), boxShadow: [ BoxShadow( color: activeData['color'].withOpacity(0.2), blurRadius: 24, spreadRadius: 4, ), ], ), child: Center( child: Icon( activeData['icon'], color: activeData['color'], size: 38, ), ), ), ), // 4. Floating category text label at the bottom Positioned( bottom: -32, child: AnimatedSwitcher( duration: const Duration(milliseconds: 300), transitionBuilder: (child, animation) => FadeTransition( opacity: animation, child: SlideTransition( position: Tween( begin: const Offset(0, 0.2), end: Offset.zero, ).animate(animation), child: child, ), ), child: KeyedSubtree( key: ValueKey(_activeIndex), child: Container( width: 280, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all(color: Colors.grey[100]!), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.04), blurRadius: 12, offset: const Offset(0, 4), ) ], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( activeData['title'], textAlign: TextAlign.center, style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: activeData['color'], ), ), const SizedBox(height: 4), Text( activeData['subtitle'], textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 10, color: Colors.grey[600], height: 1.3, ), ), ], ), ), ), ), ), ], ), ); } }