import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import '../../theme.dart'; import '../../widgets/s_button.dart'; import '../../widgets/s_card.dart'; import '../../widgets/s_progress_bar.dart'; import '../result_screen.dart'; class QuizData { String name = ''; String skinType = ''; String age = ''; List concerns = []; String goal = ''; } class QuizFlow extends StatefulWidget { const QuizFlow({super.key}); @override State createState() => _QuizFlowState(); } class _QuizFlowState extends State { final _data = QuizData(); int _step = 0; final _pageCtrl = PageController(); final _nameCtrl = TextEditingController(); static const _totalSteps = 5; void _next() { if (_step < _totalSteps - 1) { setState(() => _step++); _pageCtrl.animateToPage( _step, duration: const Duration(milliseconds: 300), curve: Curves.easeOut, ); } else { Navigator.of(context).pushReplacement(PageRouteBuilder( pageBuilder: (_, a, __) => ResultScreen(data: _data), transitionsBuilder: (_, a, __, child) => FadeTransition(opacity: a, child: child), transitionDuration: const Duration(milliseconds: 400), )); } } void _back() { if (_step > 0) { setState(() => _step--); _pageCtrl.animateToPage( _step, duration: const Duration(milliseconds: 280), curve: Curves.easeOut, ); } else { Navigator.of(context).pop(); } } bool get _canProceed => switch (_step) { 0 => _data.name.trim().length >= 2, 1 => _data.skinType.isNotEmpty, 2 => _data.age.isNotEmpty, 3 => _data.concerns.isNotEmpty, 4 => _data.goal.isNotEmpty, _ => false, }; String get _buttonLabel => _step == _totalSteps - 1 ? 'SEE MY RESULTS' : 'CONTINUE'; @override void dispose() { _nameCtrl.dispose(); _pageCtrl.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: SColors.bgSurface, body: SafeArea( child: Column( children: [ // Nav row _navRow(), const SizedBox(height: 10), // Progress bar Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: SProgressBar(value: (_step + 1) / _totalSteps), ), const SizedBox(height: 16), // Pages Expanded( child: PageView( controller: _pageCtrl, physics: const NeverScrollableScrollPhysics(), children: [ _NameStep( ctrl: _nameCtrl, onChanged: (v) => setState(() => _data.name = v), ), _SkinTypeStep( selected: _data.skinType, onSelect: (v) => setState(() => _data.skinType = v), ), _AgeStep( selected: _data.age, onSelect: (v) => setState(() => _data.age = v), ), _ConcernsStep( selected: _data.concerns, onToggle: (v) => setState(() { if (_data.concerns.contains(v)) { _data.concerns.remove(v); } else if (_data.concerns.length < 3) { _data.concerns.add(v); } }), ), _GoalStep( selected: _data.goal, onSelect: (v) => setState(() => _data.goal = v), ), ], ), ), // Sticky bottom button Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 20), child: SButton( label: _buttonLabel, onTap: _canProceed ? _next : null, enabled: _canProceed, ), ), ], ), ), ); } Widget _navRow() { return Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 0), child: Row( children: [ // Back button GestureDetector( onTap: _back, child: Container( width: 36, height: 36, decoration: BoxDecoration( color: SColors.bgSurface, borderRadius: BorderRadius.circular(10), border: Border.all(color: SColors.borderLight, width: 1.5), ), child: const Icon( Icons.arrow_back_ios_new_rounded, size: 15, color: SColors.textBody, ), ), ), const Spacer(), // Step counter Text( '${_step + 1} of $_totalSteps', style: SText.caption(SColors.textSub).copyWith(fontWeight: FontWeight.w600), ), ], ), ); } } // ── Step: Name ──────────────────────────────────────────────────────────────── class _NameStep extends StatelessWidget { final TextEditingController ctrl; final ValueChanged onChanged; const _NameStep({required this.ctrl, required this.onChanged}); @override Widget build(BuildContext context) { return SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Small illustration zone Center( child: Image.asset( 'assets/images/shaynee_hero.png', height: 100, fit: BoxFit.contain, ) .animate() .fadeIn(duration: 400.ms) .slideY(begin: 0.05, end: 0), ), const SizedBox(height: 28), Text( 'Hey! What\'s\nyour name? 👋', style: SText.headline2(SColors.textHead), ).animate().fadeIn(duration: 350.ms), const SizedBox(height: 8), Text( 'I\'ll personalise your routine just for you.', style: SText.subtitle(SColors.textSub), ).animate().fadeIn(duration: 350.ms, delay: 80.ms), const SizedBox(height: 28), // Input — custom, no Material underline Container( decoration: BoxDecoration( color: SColors.bgSurface, borderRadius: BorderRadius.circular(14), border: Border.all(color: SColors.borderMedium, width: 1.5), ), child: TextField( controller: ctrl, onChanged: onChanged, autofocus: true, textCapitalization: TextCapitalization.words, style: SText.cardLabel(SColors.textHead).copyWith(fontSize: 18), decoration: InputDecoration( hintText: 'Your first name', hintStyle: SText.cardLabel(SColors.textDisabled).copyWith( fontSize: 18, fontWeight: FontWeight.w500, ), border: InputBorder.none, contentPadding: const EdgeInsets.symmetric( horizontal: 18, vertical: 16, ), ), ), ).animate().fadeIn(duration: 350.ms, delay: 160.ms), ], ), ); } } // ── Step: Skin Type ─────────────────────────────────────────────────────────── class _SkinTypeStep extends StatelessWidget { final String selected; final ValueChanged onSelect; const _SkinTypeStep({required this.selected, required this.onSelect}); static const _opts = [ (Icons.water_drop_rounded, 'Oily', 'Shiny by midday, large pores'), (Icons.grass_rounded, 'Dry', 'Tight, flaky, rough patches'), (Icons.blur_on_rounded, 'Combination', 'Oily T-zone, dry cheeks'), (Icons.sentiment_satisfied_alt_rounded, 'Normal', 'Balanced, minimal issues'), (Icons.help_outline_rounded, 'Not sure', 'Help me figure it out'), ]; @override Widget build(BuildContext context) { return SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('What\'s your\nskin type?', style: SText.headline2(SColors.textHead)) .animate().fadeIn(duration: 350.ms), const SizedBox(height: 6), Text('Pick the one that fits best.', style: SText.subtitle(SColors.textSub)) .animate().fadeIn(duration: 350.ms, delay: 80.ms), const SizedBox(height: 20), ], ), ), ..._opts.asMap().entries.map((e) { final i = e.key; final (icon, label, sub) = e.value; return Padding( padding: const EdgeInsets.only(bottom: 8), child: SCard( icon: icon, label: label, sub: sub, isSelected: selected == label, onTap: () => onSelect(label), ).animate().fadeIn(duration: 280.ms, delay: (80 + i * 50).ms) .slideX(begin: 0.03, end: 0, duration: 280.ms), ); }), ], ), ); } } // ── Step: Age ───────────────────────────────────────────────────────────────── class _AgeStep extends StatelessWidget { final String selected; final ValueChanged onSelect; const _AgeStep({required this.selected, required this.onSelect}); static const _opts = [ (Icons.star_rounded, 'Under 25', 'Young & preventive care'), (Icons.hourglass_bottom_rounded, '25–35', 'Early anti-aging focus'), (Icons.wb_sunny_rounded, '35–45', 'Targeted treatments'), (Icons.nightlight_round, '45–60', 'Deep renewal & firming'), (Icons.diamond_rounded, '60+', 'Intensive care & radiance'), ]; @override Widget build(BuildContext context) { return SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('How old\nare you?', style: SText.headline2(SColors.textHead)) .animate().fadeIn(duration: 350.ms), const SizedBox(height: 6), Text('Skin changes with age — helps us tailor advice.', style: SText.subtitle(SColors.textSub)) .animate().fadeIn(duration: 350.ms, delay: 80.ms), const SizedBox(height: 20), ], ), ), ..._opts.asMap().entries.map((e) { final i = e.key; final (icon, label, sub) = e.value; return Padding( padding: const EdgeInsets.only(bottom: 8), child: SCard( icon: icon, label: label, sub: sub, isSelected: selected == label, onTap: () => onSelect(label), ).animate().fadeIn(duration: 280.ms, delay: (80 + i * 50).ms) .slideX(begin: 0.03, end: 0, duration: 280.ms), ); }), ], ), ); } } // ── Step: Concerns ──────────────────────────────────────────────────────────── class _ConcernsStep extends StatelessWidget { final List selected; final ValueChanged onToggle; const _ConcernsStep({required this.selected, required this.onToggle}); static const _opts = [ (Icons.face_retouching_natural, 'Breakouts', 'Acne, blackheads, congestion'), (Icons.cloud_rounded, 'Dullness', 'Tired, uneven, grey skin'), (Icons.air_rounded, 'Dryness', 'Tight, rough, flaking'), (Icons.format_align_justify_rounded, 'Fine lines', 'Wrinkles, loss of firmness'), (Icons.adjust_rounded, 'Dark spots', 'Hyperpigmentation, melasma'), (Icons.local_fire_department_rounded, 'Redness', 'Sensitivity, flushing, rosacea'), ]; @override Widget build(BuildContext context) { return SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text('Your skin concerns?', style: SText.headline2(SColors.textHead)) .animate().fadeIn(duration: 350.ms), const Spacer(), // counter badge if (selected.isNotEmpty) Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: SColors.primary, borderRadius: BorderRadius.circular(20), ), child: Text( '${selected.length}/3', style: SText.caption(Colors.white) .copyWith(fontWeight: FontWeight.w700), ), ), ], ), const SizedBox(height: 6), Text('Pick up to 3 that bother you most.', style: SText.subtitle(SColors.textSub)) .animate().fadeIn(duration: 350.ms, delay: 80.ms), const SizedBox(height: 20), ], ), ), ..._opts.asMap().entries.map((e) { final i = e.key; final (icon, label, sub) = e.value; final isSel = selected.contains(label); final disabled = !isSel && selected.length >= 3; return Padding( padding: const EdgeInsets.only(bottom: 8), child: Opacity( opacity: disabled ? 0.35 : 1.0, child: SCard( icon: icon, label: label, sub: sub, isSelected: isSel, onTap: disabled ? () {} : () => onToggle(label), ).animate().fadeIn(duration: 280.ms, delay: (80 + i * 50).ms) .slideX(begin: 0.03, end: 0, duration: 280.ms), ), ); }), ], ), ); } } // ── Step: Goal ──────────────────────────────────────────────────────────────── class _GoalStep extends StatelessWidget { final String selected; final ValueChanged onSelect; const _GoalStep({required this.selected, required this.onSelect}); static const _opts = [ (Icons.auto_awesome_rounded, 'Healthy glow', 'Radiant, dewy, lit-from-within'), (Icons.shield_rounded, 'Clear skin', 'Blemish-free, smooth texture'), (Icons.access_time_rounded, 'Slow aging', 'Firm, youthful, preventive'), (Icons.palette_rounded, 'Even tone', 'Balanced, bright complexion'), ]; @override Widget build(BuildContext context) { return SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Your main\nskin goal?', style: SText.headline2(SColors.textHead)) .animate().fadeIn(duration: 350.ms), const SizedBox(height: 6), Text('We\'ll build your plan around it.', style: SText.subtitle(SColors.textSub)) .animate().fadeIn(duration: 350.ms, delay: 80.ms), const SizedBox(height: 20), ], ), ), ..._opts.asMap().entries.map((e) { final i = e.key; final (icon, label, sub) = e.value; return Padding( padding: const EdgeInsets.only(bottom: 8), child: SCard( icon: icon, label: label, sub: sub, isSelected: selected == label, onTap: () => onSelect(label), ).animate().fadeIn(duration: 280.ms, delay: (80 + i * 50).ms) .slideX(begin: 0.03, end: 0, duration: 280.ms), ); }), ], ), ); } }