- Nunito font replacing Plus Jakarta Sans - SButton: Stack-based 3D pressable (dark shadow layer + top layer translates on press) - SCard: flat border-based selection, no shadows - SProgressBar: 4px thin bar, animated fill - All screens: white bg, no gradients - Duolingo layout: illustration top zone, content below, sticky button
216 lines
6.7 KiB
Dart
216 lines
6.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_animate/flutter_animate.dart';
|
|
import '../theme.dart';
|
|
import '../widgets/s_button.dart';
|
|
import 'quiz/quiz_flow.dart';
|
|
|
|
class IntroScreen extends StatelessWidget {
|
|
const IntroScreen({super.key});
|
|
|
|
void _start(BuildContext context) {
|
|
Navigator.of(context).push(PageRouteBuilder(
|
|
pageBuilder: (_, a, __) => const QuizFlow(),
|
|
transitionsBuilder: (_, a, __, child) => SlideTransition(
|
|
position: Tween<Offset>(
|
|
begin: const Offset(1, 0),
|
|
end: Offset.zero,
|
|
).animate(CurvedAnimation(parent: a, curve: Curves.easeOut)),
|
|
child: child,
|
|
),
|
|
transitionDuration: const Duration(milliseconds: 300),
|
|
));
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: SColors.bgSurface,
|
|
body: SafeArea(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
// Illustration zone (top 42%)
|
|
Expanded(
|
|
flex: 42,
|
|
child: _illustrationZone()
|
|
.animate()
|
|
.fadeIn(duration: 500.ms),
|
|
),
|
|
|
|
// Content zone (bottom 58%)
|
|
Expanded(
|
|
flex: 58,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const SizedBox(height: 8),
|
|
|
|
// Trust badge
|
|
_badge()
|
|
.animate()
|
|
.fadeIn(duration: 400.ms, delay: 150.ms),
|
|
const SizedBox(height: 16),
|
|
|
|
// Headline
|
|
Text(
|
|
'Skin that glows,\nroutine that works.',
|
|
style: SText.headline2(SColors.textHead),
|
|
)
|
|
.animate()
|
|
.fadeIn(duration: 400.ms, delay: 200.ms)
|
|
.slideY(begin: 0.06, end: 0, duration: 400.ms),
|
|
const SizedBox(height: 10),
|
|
|
|
// Sub
|
|
Text(
|
|
'Answer 5 quick questions and get your\npersonalised skincare plan in 60 seconds.',
|
|
style: SText.subtitle(SColors.textSub),
|
|
)
|
|
.animate()
|
|
.fadeIn(duration: 400.ms, delay: 280.ms),
|
|
|
|
const Spacer(),
|
|
|
|
// Stats row
|
|
_statsRow()
|
|
.animate()
|
|
.fadeIn(duration: 400.ms, delay: 360.ms),
|
|
const SizedBox(height: 24),
|
|
|
|
// CTA button
|
|
SButton(
|
|
label: 'GET STARTED',
|
|
onTap: () => _start(context),
|
|
)
|
|
.animate()
|
|
.fadeIn(duration: 400.ms, delay: 440.ms)
|
|
.slideY(begin: 0.1, end: 0, duration: 400.ms),
|
|
const SizedBox(height: 16),
|
|
|
|
// Sign in link (plain text, no button)
|
|
Center(
|
|
child: GestureDetector(
|
|
onTap: () {},
|
|
child: Text(
|
|
'Already have an account? Sign in',
|
|
style: SText.caption(SColors.textSub),
|
|
),
|
|
),
|
|
)
|
|
.animate()
|
|
.fadeIn(duration: 400.ms, delay: 500.ms),
|
|
const SizedBox(height: 20),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _illustrationZone() {
|
|
return Stack(
|
|
children: [
|
|
// Light tinted background for art area
|
|
Container(color: const Color(0xFFF7F5FF)),
|
|
// Stats visual — abstract feature cards
|
|
Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
_featureChip(Icons.auto_awesome_rounded, 'AI skin analysis'),
|
|
const SizedBox(height: 10),
|
|
_featureChip(Icons.science_rounded, 'Ingredient science'),
|
|
const SizedBox(height: 10),
|
|
_featureChip(Icons.lock_outline_rounded, 'Private & secure'),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _featureChip(IconData icon, String label) {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
|
decoration: BoxDecoration(
|
|
color: SColors.bgSurface,
|
|
borderRadius: BorderRadius.circular(14),
|
|
border: Border.all(color: SColors.borderLight, width: 1.5),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(icon, color: SColors.primary, size: 20),
|
|
const SizedBox(width: 10),
|
|
Text(label, style: SText.cardLabel(SColors.textHead)),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _badge() {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
|
decoration: BoxDecoration(
|
|
color: SColors.primaryLight,
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Container(
|
|
width: 7,
|
|
height: 7,
|
|
decoration: const BoxDecoration(
|
|
color: SColors.accent,
|
|
shape: BoxShape.circle,
|
|
),
|
|
),
|
|
const SizedBox(width: 6),
|
|
Text(
|
|
'Trusted by 50,000+ women',
|
|
style: SText.caption(SColors.primary).copyWith(fontWeight: FontWeight.w700),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _statsRow() {
|
|
return Row(
|
|
children: [
|
|
_statBox('50K+', 'Users'),
|
|
const SizedBox(width: 10),
|
|
_statBox('4.9★', 'Rating'),
|
|
const SizedBox(width: 10),
|
|
_statBox('94%', 'Results'),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _statBox(String value, String label) {
|
|
return Expanded(
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(vertical: 14),
|
|
decoration: BoxDecoration(
|
|
color: SColors.bgSurface,
|
|
borderRadius: BorderRadius.circular(14),
|
|
border: Border.all(color: SColors.borderLight, width: 1.5),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
Text(value, style: SText.title(SColors.primary)),
|
|
const SizedBox(height: 2),
|
|
Text(label, style: SText.caption(SColors.textSub)),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|