redesign: duolingo-pattern onboarding, no gradient, pressable button

- 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
This commit is contained in:
mav
2026-05-28 16:34:04 +02:00
parent 30cee5a486
commit 6311088256
9 changed files with 1168 additions and 638 deletions

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:google_fonts/google_fonts.dart';
import '../theme.dart';
import 'intro_screen.dart';
@@ -15,7 +14,7 @@ class _SplashScreenState extends State<SplashScreen> {
@override
void initState() {
super.initState();
Future.delayed(const Duration(milliseconds: 2600), _goNext);
Future.delayed(const Duration(milliseconds: 2400), _goNext);
}
void _goNext() {
@@ -25,7 +24,7 @@ class _SplashScreenState extends State<SplashScreen> {
pageBuilder: (_, a, __) => const IntroScreen(),
transitionsBuilder: (_, a, __, child) =>
FadeTransition(opacity: a, child: child),
transitionDuration: const Duration(milliseconds: 500),
transitionDuration: const Duration(milliseconds: 400),
),
);
}
@@ -33,76 +32,42 @@ class _SplashScreenState extends State<SplashScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xFF3D32A8), Color(0xFF5B4FCF), Color(0xFF8B5CF6)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: SafeArea(
child: Column(
children: [
const Spacer(flex: 2),
Image.asset(
'assets/images/shaynee_hero.png',
height: 240,
fit: BoxFit.contain,
)
.animate()
.fadeIn(duration: 700.ms, delay: 200.ms)
.slideY(begin: 0.15, end: 0, duration: 700.ms, curve: Curves.easeOut),
const SizedBox(height: 28),
Text(
'shaynee',
style: GoogleFonts.plusJakartaSans(
fontSize: 42,
fontWeight: FontWeight.w800,
color: Colors.white,
letterSpacing: -1.0,
),
)
.animate()
.fadeIn(duration: 600.ms, delay: 500.ms),
const SizedBox(height: 8),
Text(
'your AI skincare companion',
style: GoogleFonts.plusJakartaSans(
fontSize: 16,
fontWeight: FontWeight.w400,
color: Colors.white.withAlpha(180),
letterSpacing: 0.2,
),
)
.animate()
.fadeIn(duration: 600.ms, delay: 700.ms),
const Spacer(flex: 3),
Padding(
padding: const EdgeInsets.only(bottom: 40),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(3, (i) => _dot(i)),
)
.animate()
.fadeIn(duration: 500.ms, delay: 1200.ms),
backgroundColor: SColors.bgSurface,
body: SafeArea(
child: Column(
children: [
const Spacer(flex: 2),
// Mascot — centered, large
Image.asset(
'assets/images/shaynee_hero.png',
height: 260,
fit: BoxFit.contain,
)
.animate()
.fadeIn(duration: 600.ms, delay: 200.ms)
.slideY(begin: 0.08, end: 0, duration: 600.ms, curve: Curves.easeOut),
const SizedBox(height: 32),
// Wordmark
Text(
'shaynee',
style: SText.headline1(SColors.textHead).copyWith(
fontSize: 42,
letterSpacing: -0.5,
),
],
),
)
.animate()
.fadeIn(duration: 500.ms, delay: 500.ms),
const SizedBox(height: 8),
Text(
'your ai skincare companion',
style: SText.subtitle(SColors.textSub),
)
.animate()
.fadeIn(duration: 500.ms, delay: 700.ms),
const Spacer(flex: 3),
],
),
),
);
}
Widget _dot(int i) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 4),
width: i == 0 ? 24 : 8,
height: 8,
decoration: BoxDecoration(
color: i == 0 ? Colors.white : Colors.white.withAlpha(80),
borderRadius: BorderRadius.circular(4),
),
);
}
}