import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; import '../../about/internal/application/DeploymentRecord.dart'; import '../../about/internal/application/NavigatorType.dart'; import '../../about/internal/application/ServerProfile.dart'; import '../../about/internal/application/TextType.dart'; import '../../configs/Navigator.dart'; import '../../configs/ThemeController.dart'; import '../../designs/text/Text.dart'; import '../../utils/Colors.dart'; import '../home/Home.dart'; import 'ConnectSplash.dart'; import 'Splash.dart'; import 'ViewSplash.dart'; class SplashState extends State with SingleTickerProviderStateMixin implements ConnectSplash { ViewSplash? _model; late final AnimationController _pulse; @override void initState() { super.initState(); _pulse = AnimationController( vsync: this, duration: const Duration(milliseconds: 900), )..repeat(reverse: true); } @override void dispose() { _pulse.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return ViewModelBuilder.reactive( viewModelBuilder: () => ViewSplash(context, this), onViewModelReady: _onReady, builder: (context, viewModel, child) => Scaffold( body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ _brandMark(), const SizedBox(height: 24), text( 'Peach', 36, TextType.Bold, context: context, letterSpacing: -0.8, ), const SizedBox(height: 6), text( 'DEPLOYMENT COCKPIT', 11.5, TextType.Bold, color: context.jp.textFaint, letterSpacing: 4, ), const SizedBox(height: 40), SizedBox( width: 150, child: ClipRRect( borderRadius: BorderRadius.circular(10), child: LinearProgressIndicator( minHeight: 3, backgroundColor: context.jp.surface2, ), ), ), ], ), ), ), ); } void _onReady(ViewSplash model) { _model = model; // onViewModelReady fires mid-build; a synchronous setState from the // first load would dirty this State while it is still being built. WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) _initiate(); }); } Future _initiate() async { _model?.bootstrap(); } // ── ConnectSplash ────────────────────────────────────────────────────────── @override void onBootstrapped( List servers, List history, String themeMode, ) { if (!mounted) return; // Apply the stored theme before the dashboard paints, so it never flashes // the default and then switches. ThemeController.apply(themeMode); PeachNavigation().navigateToPage( NavigatorType.makeNewMain, Home(servers: servers, history: history), context, ); } // ── screen-only widgets ──────────────────────────────────────────────────── /// Layered logo: a soft pulsing halo behind a beveled brand tile. Widget _brandMark() { return SizedBox( width: 132, height: 132, child: Stack( alignment: Alignment.center, children: [ ScaleTransition( scale: Tween( begin: 0.9, end: 1.12, ).animate(CurvedAnimation(parent: _pulse, curve: Curves.easeInOut)), child: Container( width: 132, height: 132, decoration: BoxDecoration( shape: BoxShape.circle, gradient: RadialGradient( colors: [ PeachTheme.seed.withValues(alpha: 0.28), PeachTheme.seed.withValues(alpha: 0), ], ), ), ), ), Container( width: 90, height: 90, decoration: BoxDecoration( gradient: PeachTheme.brandGradient, borderRadius: BorderRadius.circular(26), border: Border.all( color: colorWhite.withValues(alpha: 0.18), width: 1, ), boxShadow: PeachTheme.glow( PeachTheme.seed, opacity: 0.5, blur: 44, spread: 0, ), ), child: Icon(Icons.rocket_launch, color: colorWhite, size: 44), ), ], ), ); } }