import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import 'package:qpay/models/responsive_policy.dart'; import 'package:qpay/screens/onboarding/onboarding_controller.dart'; class LandingScreen extends StatefulWidget { const LandingScreen({super.key}); @override State createState() => _LandingScreenState(); } class _LandingScreenState extends State { late OnboardingController onboardingController; @override void initState() { super.initState(); onboardingController = Provider.of( context, listen: false, ); } void _goToRegister() { // Reset the registration state so a fresh sign-up starts cleanly. onboardingController.resetState(); context.go('/onboarding/phone'); } // Max content width on large screens (desktop / web). static const double _maxContentWidth = 1200; // Max content width on tablet — the current mobile cap lifted slightly // so the page breathes a bit on landscape phones / small tablets. static const double _maxTabletWidth = 760; @override Widget build(BuildContext context) { final theme = Theme.of(context); final colorScheme = theme.colorScheme; final width = MediaQuery.of(context).size.width; final isTablet = width >= ResponsivePolicy.md; final isDesktop = width >= ResponsivePolicy.lg; return Scaffold( // Use a Stack so the gradient background is guaranteed to cover the // full viewport (including the area under the SafeArea) regardless // of how tall the content ends up being. body: Stack( children: [ Positioned.fill( child: DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ colorScheme.primary.withValues(alpha: 0.08), colorScheme.surface, colorScheme.secondary.withValues(alpha: 0.05), ], ), ), ), ), SafeArea( child: SingleChildScrollView( child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: _maxContentWidth), child: Padding( padding: EdgeInsets.symmetric( horizontal: isDesktop ? 48 : 20, vertical: isDesktop ? 32 : 16, ), child: LayoutBuilder( builder: (context, constraints) { final contentMaxWidth = isDesktop ? _maxContentWidth : (isTablet ? _maxTabletWidth : constraints.maxWidth); return SizedBox( width: contentMaxWidth, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SizedBox(height: 12), _buildTopBar(theme, colorScheme), SizedBox(height: isDesktop ? 40 : 24), _buildHero(theme, colorScheme, isDesktop), SizedBox(height: isDesktop ? 72 : 36), _buildValuePropsHeadline(theme, colorScheme), const SizedBox(height: 28), _buildValueProps(isTablet), SizedBox(height: isDesktop ? 64 : 32), _buildCallToActions( theme, colorScheme, isDesktop, ), const SizedBox(height: 8), TextButton( onPressed: () { context.go('/'); }, child: Text( 'Continue as guest?', style: TextStyle( fontSize: 14, color: colorScheme.onSurface.withValues( alpha: 0.7, ), fontWeight: FontWeight.w500, ), ), ), const SizedBox(height: 24), _buildFooter(theme, colorScheme), const SizedBox(height: 12), ], ), ); }, ), ), ), ), ), ), ], ), ); } Widget _buildTopBar(ThemeData theme, ColorScheme colorScheme) { return Row( children: [ Image(image: AssetImage('assets/velocity.png'), width: 150), const Spacer(), TextButton( onPressed: () { context.go('/onboarding/login'); }, style: TextButton.styleFrom( foregroundColor: colorScheme.primary, textStyle: const TextStyle( fontWeight: FontWeight.w600, fontSize: 14, ), ), child: const Text('Sign in'), ), ], ); } Widget _buildHero(ThemeData theme, ColorScheme colorScheme, bool isDesktop) { final textBlock = Column( crossAxisAlignment: isDesktop ? CrossAxisAlignment.start : CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: isDesktop ? Colors.white.withValues(alpha: 0.18) : colorScheme.primary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(20), border: isDesktop ? Border.all(color: Colors.white.withValues(alpha: 0.25)) : null, ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.flash_on_rounded, size: 14, color: isDesktop ? Colors.white : colorScheme.primary, ), const SizedBox(width: 6), Text( 'Fast. Secure. Local.', style: theme.textTheme.labelMedium?.copyWith( color: isDesktop ? Colors.white : colorScheme.primary, fontWeight: FontWeight.w700, letterSpacing: 0.4, ), ), ], ), ), SizedBox(height: isDesktop ? 20 : 16), Text( 'Welcome to Velocity', textAlign: isDesktop ? TextAlign.start : TextAlign.center, style: (isDesktop ? theme.textTheme.displaySmall : theme.textTheme.headlineMedium) ?.copyWith( color: isDesktop ? null : colorScheme.primary, fontWeight: FontWeight.w800, letterSpacing: -0.5, height: 1.1, ), ), SizedBox(height: isDesktop ? 16 : 8), Text( 'The fastest way to pay for local goods and services.', textAlign: isDesktop ? TextAlign.start : TextAlign.center, style: theme.textTheme.titleMedium?.copyWith( color: isDesktop ? colorScheme.onSurface.withValues(alpha: 0.75) : colorScheme.onSurface.withValues(alpha: 0.8), height: 1.4, fontWeight: FontWeight.w400, ), ), if (isDesktop) ...[ const SizedBox(height: 28), Wrap( spacing: 12, runSpacing: 12, children: [ _buildHeroCta(context, colorScheme), _buildHeroSecondaryCta(context, colorScheme), ], ), ], ], ); final imageBlock = ClipRRect( borderRadius: BorderRadius.circular(24), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(24), boxShadow: [ BoxShadow( color: colorScheme.primary.withValues(alpha: 0.25), blurRadius: 32, offset: const Offset(0, 18), ), ], ), child: Image.asset( 'assets/signup.png', width: isDesktop ? 360 : 260, fit: BoxFit.contain, ), ), ); if (isDesktop) { return Container( padding: const EdgeInsets.all(40), decoration: BoxDecoration( borderRadius: BorderRadius.circular(32), gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ colorScheme.primary.withValues(alpha: 0.06), colorScheme.secondary.withValues(alpha: 0.04), ], ), border: Border.all( color: colorScheme.onSurface.withValues(alpha: 0.06), ), ), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded(flex: 5, child: textBlock), const SizedBox(width: 32), Expanded(flex: 4, child: Center(child: imageBlock)), ], ), ); } // Mobile / tablet — original vertical hero with gradient background. return Container( padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 20), decoration: BoxDecoration( borderRadius: BorderRadius.circular(28), gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [colorScheme.primary, colorScheme.secondary], ), boxShadow: [ BoxShadow( color: colorScheme.primary.withValues(alpha: 0.25), blurRadius: 24, offset: const Offset(0, 12), ), ], ), child: Column( children: [ Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.15), shape: BoxShape.circle, ), child: const Icon( Icons.bolt_rounded, color: Colors.white, size: 44, ), ), const SizedBox(height: 16), Text( 'Welcome to Velocity', textAlign: TextAlign.center, style: theme.textTheme.headlineMedium?.copyWith( color: Colors.white, fontWeight: FontWeight.w800, letterSpacing: -0.5, ), ), const SizedBox(height: 8), Text( 'The fastest way to pay for local goods\nand services', textAlign: TextAlign.center, style: theme.textTheme.bodyLarge?.copyWith( color: Colors.white.withValues(alpha: 0.92), height: 1.4, ), ), const SizedBox(height: 20), imageBlock, ], ), ); } Widget _buildHeroCta(BuildContext context, ColorScheme colorScheme) { return SizedBox( height: 52, child: ElevatedButton( onPressed: _goToRegister, style: ElevatedButton.styleFrom( backgroundColor: colorScheme.primary, foregroundColor: Colors.white, elevation: 0, padding: const EdgeInsets.symmetric(horizontal: 28), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14), ), textStyle: const TextStyle( fontSize: 16, fontWeight: FontWeight.w700, letterSpacing: 0.3, ), ), child: const Row( mainAxisSize: MainAxisSize.min, children: [ Text('Create Free Account'), SizedBox(width: 8), Icon(Icons.arrow_forward_rounded, size: 20), ], ), ), ); } Widget _buildHeroSecondaryCta(BuildContext context, ColorScheme colorScheme) { return SizedBox( height: 52, child: OutlinedButton( onPressed: () { context.go('/onboarding/login'); }, style: OutlinedButton.styleFrom( side: BorderSide(color: colorScheme.primary, width: 1.5), foregroundColor: colorScheme.primary, padding: const EdgeInsets.symmetric(horizontal: 28), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14), ), textStyle: const TextStyle( fontSize: 16, fontWeight: FontWeight.w700, letterSpacing: 0.3, ), ), child: const Text('Sign in'), ), ); } Widget _buildValuePropsHeadline(ThemeData theme, ColorScheme colorScheme) { return Column( children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6), decoration: BoxDecoration( color: colorScheme.primary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(20), ), child: Text( 'Why register?', style: theme.textTheme.labelLarge?.copyWith( color: colorScheme.primary, fontWeight: FontWeight.w700, letterSpacing: 0.5, ), ), ), const SizedBox(height: 14), Text( 'Unlock the full Velocity experience', textAlign: TextAlign.center, style: theme.textTheme.headlineSmall?.copyWith( color: colorScheme.onSurface, fontWeight: FontWeight.w700, ), ), const SizedBox(height: 8), ConstrainedBox( constraints: const BoxConstraints(maxWidth: 600), child: Text( 'Create a free account to access powerful features designed to make your payments effortless.', textAlign: TextAlign.center, style: theme.textTheme.bodyLarge?.copyWith( color: colorScheme.onSurface.withValues(alpha: 0.65), height: 1.45, ), ), ), ], ); } Widget _buildValueProps(bool isWide) { final props = const [ _ValueProp( icon: Icons.groups_2_rounded, title: 'Batch Pay Groups', description: 'Send payments to many recipients at once. Perfect for splitting bills, paying teams, or settling group purchases in a single tap.', accent: Color(0xFF9D6711), ), _ValueProp( icon: Icons.devices_rounded, title: 'Access Anywhere', description: 'Your recipients and full transaction history sync seamlessly across all your devices — pick up right where you left off.', accent: Color(0xFF8B0000), ), _ValueProp( icon: Icons.account_balance_wallet_rounded, title: 'Prefund Your Wallet', description: 'Top up your wallet today and use the balance whenever you need it. No rushing, no last-minute transfers.', accent: Color(0xFFB7791F), ), ]; if (isWide) { return IntrinsicHeight( child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: props .map( (p) => Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: p, ), ), ) .toList(), ), ); } return Column( children: props .map( (p) => Padding(padding: const EdgeInsets.only(bottom: 12), child: p), ) .toList(), ); } Widget _buildCallToActions( ThemeData theme, ColorScheme colorScheme, bool isDesktop, ) { final registerButton = SizedBox( width: double.infinity, height: 54, child: ElevatedButton( onPressed: _goToRegister, style: ElevatedButton.styleFrom( backgroundColor: colorScheme.primary, foregroundColor: Colors.white, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14), ), textStyle: const TextStyle( fontSize: 16, fontWeight: FontWeight.w700, letterSpacing: 0.3, ), ), child: const Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Create Free Account'), SizedBox(width: 8), Icon(Icons.arrow_forward_rounded, size: 20), ], ), ), ); final loginButton = SizedBox( width: double.infinity, height: 54, child: OutlinedButton( onPressed: () { context.go('/onboarding/login'); }, style: OutlinedButton.styleFrom( side: BorderSide(color: colorScheme.primary, width: 1.5), foregroundColor: colorScheme.primary, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14), ), textStyle: const TextStyle( fontSize: 16, fontWeight: FontWeight.w700, letterSpacing: 0.3, ), ), child: const Text('I already have an account'), ), ); if (isDesktop) { return Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 520), child: Column( children: [registerButton, const SizedBox(height: 12), loginButton], ), ), ); } return Column( children: [registerButton, const SizedBox(height: 12), loginButton], ); } Widget _buildFooter(ThemeData theme, ColorScheme colorScheme) { return Center( child: Text( '© Velocity · Fast, secure local payments', style: theme.textTheme.bodySmall?.copyWith( color: colorScheme.onSurface.withValues(alpha: 0.45), letterSpacing: 0.2, ), ), ); } } class _ValueProp extends StatelessWidget { const _ValueProp({ required this.icon, required this.title, required this.description, required this.accent, }); final IconData icon; final String title; final String description; final Color accent; @override Widget build(BuildContext context) { final theme = Theme.of(context); final colorScheme = theme.colorScheme; return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: colorScheme.surface, borderRadius: BorderRadius.circular(20), border: Border.all( color: colorScheme.onSurface.withValues(alpha: 0.06), width: 1, ), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.04), blurRadius: 16, offset: const Offset(0, 4), ), ], ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: accent.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(14), ), child: Icon(icon, color: accent, size: 26), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w700, color: colorScheme.onSurface, ), ), const SizedBox(height: 6), Text( description, style: theme.textTheme.bodyMedium?.copyWith( color: colorScheme.onSurface.withValues(alpha: 0.68), height: 1.5, ), ), ], ), ), ], ), ); } }