import 'package:flutter/cupertino.dart'; import 'package:please_pay_me/core/branding/app_brand.dart'; import 'package:please_pay_me/theme/theme.dart'; import 'package:please_pay_me/ui/ui.dart'; /// Branded launch screen. Native Android/iOS splash uses the same background /// and mark so the first Flutter frame does not flash. class SplashScreen extends StatefulWidget { const SplashScreen({super.key}); @override State createState() => _SplashScreenState(); } class _SplashScreenState extends State with SingleTickerProviderStateMixin { late final AnimationController _controller; late final Animation _mark; late final Animation _copy; late final Animation _spinner; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 720), )..forward(); _mark = CurvedAnimation( parent: _controller, curve: const Interval(0, 0.55, curve: Curves.easeOutCubic), ); _copy = CurvedAnimation( parent: _controller, curve: const Interval(0.28, 0.85, curve: Curves.easeOut), ); _spinner = CurvedAnimation( parent: _controller, curve: const Interval(0.55, 1, curve: Curves.easeOut), ); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final dark = CupertinoTheme.of(context).brightness == Brightness.dark; return CupertinoPageScaffold( backgroundColor: AppColors.of(context, AppColors.groupedBackground), child: AnimatedBuilder( animation: _controller, builder: (context, _) { return Stack( fit: StackFit.expand, children: [ _Wash(dark: dark), SafeArea( child: Column( children: [ const Spacer(flex: 3), Opacity( opacity: _mark.value, child: Transform.scale( scale: 0.86 + (0.14 * _mark.value), child: const AppBrandMark(), ), ), const SizedBox(height: AppSpacing.s5), Opacity( opacity: _copy.value, child: Transform.translate( offset: Offset(0, 10 * (1 - _copy.value)), child: const Column( children: [ AppText.largeTitle(AppBrand.name, textAlign: TextAlign.center), SizedBox(height: AppSpacing.s2), AppText.subhead( 'Бюджет от зарплаты до зарплаты', color: AppColors.secondaryLabel, textAlign: TextAlign.center, ), ], ), ), ), const Spacer(flex: 4), Opacity( opacity: _spinner.value, child: const Padding( padding: EdgeInsets.only(bottom: AppSpacing.s7), child: AppSpinner(), ), ), ], ), ), ], ); }, ), ); } } class _Wash extends StatelessWidget { const _Wash({required this.dark}); final bool dark; @override Widget build(BuildContext context) { final accent = AppColors.of(context, AppColors.accent).withValues(alpha: dark ? 0.14 : 0.1); return IgnorePointer( child: DecoratedBox( decoration: BoxDecoration( gradient: RadialGradient( center: const Alignment(0, -0.18), radius: 0.85, colors: [accent, const Color(0x00000000)], ), ), ), ); } }