feat: complete 20-screen onboarding flow

Full quiz flow s01-s20: splash, intro, value prop, social proof,
name/skin-type/age/concerns/goal quiz with insights, analysing loader,
result with skin score, AM/PM routines, ingredient science, signup, welcome.

Duolingo-style design: flat cards, pressable SButton (Stack trick),
4px progress bar, mascot moods (wave/think/analyze/celebrate),
confetti on result+welcome, personalized content throughout.
This commit is contained in:
mav
2026-05-28 17:11:27 +02:00
parent 6311088256
commit 93042cbb4c
26 changed files with 2582 additions and 2 deletions

View File

@@ -0,0 +1,99 @@
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import '../../theme.dart';
import '../../widgets/mascot.dart';
import '../../widgets/s_button.dart';
import '../../router.dart';
import 's03_value_prop.dart';
class HiImShayneeScreen extends StatelessWidget {
const HiImShayneeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: SColors.bgSurface,
body: SafeArea(
child: Column(
children: [
// Illustration zone (top 45%)
Expanded(
flex: 45,
child: Stack(
alignment: Alignment.center,
children: [
Mascot(mood: MascotMood.wave, size: 180)
.animate()
.fadeIn(duration: 600.ms, delay: 200.ms)
.slideY(begin: 0.08, end: 0, duration: 600.ms, curve: Curves.easeOut),
// Speech bubble
Positioned(
top: 30,
right: 40,
child: _SpeechBubble(text: 'Hi there! 👋')
.animate()
.fadeIn(duration: 500.ms, delay: 800.ms)
.scaleXY(begin: 0.6, end: 1.0, duration: 400.ms, curve: Curves.elasticOut),
),
],
),
),
// Content zone
Expanded(
flex: 55,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 8),
Text("I'm Shaynee,\nyour skin bestie ✨",
style: SText.headline2(SColors.textHead))
.animate().fadeIn(duration: 400.ms, delay: 300.ms)
.slideY(begin: 0.06, end: 0, duration: 400.ms),
const SizedBox(height: 10),
Text("I'll analyse your skin, build a routine that\nactually works, and guide you daily.",
style: SText.subtitle(SColors.textSub))
.animate().fadeIn(duration: 400.ms, delay: 420.ms),
const Spacer(),
SButton(
label: "LET'S GO",
onTap: () => Navigator.of(context).push(slideRoute(const ValuePropScreen())),
)
.animate().fadeIn(duration: 400.ms, delay: 600.ms)
.slideY(begin: 0.1, end: 0, duration: 400.ms),
const SizedBox(height: 20),
],
),
),
),
],
),
),
);
}
}
class _SpeechBubble extends StatelessWidget {
final String text;
const _SpeechBubble({required this.text});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(
color: SColors.bgSurface,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
bottomRight: Radius.circular(16),
bottomLeft: Radius.circular(4),
),
border: Border.all(color: SColors.borderLight, width: 1.5),
boxShadow: [BoxShadow(color: Colors.black.withAlpha(10), blurRadius: 8, offset: const Offset(0, 2))],
),
child: Text(text, style: SText.cardLabel(SColors.textHead).copyWith(fontSize: 14)),
);
}
}