From 36bc41509da3111acf1d558f49490727cd0d6fdf Mon Sep 17 00:00:00 2001 From: alvocool Date: Mon, 2 Nov 2026 12:59:34 +0300 Subject: [PATCH] Animate the splash and fix the web preview hanging on the logo The splash now raises the sun in the mark and over a painted valley, floats four feature chips around the mark, shimmers the wordmark, shows a three-beat "how it works" card, drifts light motes upward and walks a status line through the session check. A tap skips it. The web build hung on the native splash because the asset manifest could not be published: "assets/icons/splash_orb.png" is 27 characters, so its length prefix in AssetManifest.bin is an ESC byte. Renamed it to splash_mark.png and removed the unused anchor assets. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KhQh614DMeYoQEH9SoMRUw --- frontend/assets/icons/logo.svg | 17 - frontend/assets/icons/splash.png | Bin 35932 -> 0 bytes .../icons/{splash_orb.png => splash_mark.png} | Bin .../lib/Grounded/designs/scene/SceneArt.dart | 7 +- .../Grounded/designs/scene/ScenePainter.dart | 11 +- .../lib/Grounded/see/splash/SplashState.dart | 536 ++++++++++++++---- frontend/lib/Grounded/utils/Images.dart | 2 +- frontend/pubspec.yaml | 4 +- frontend/web/index.html | 3 + 9 files changed, 443 insertions(+), 137 deletions(-) delete mode 100644 frontend/assets/icons/logo.svg delete mode 100644 frontend/assets/icons/splash.png rename frontend/assets/icons/{splash_orb.png => splash_mark.png} (100%) diff --git a/frontend/assets/icons/logo.svg b/frontend/assets/icons/logo.svg deleted file mode 100644 index cca97ca..0000000 --- a/frontend/assets/icons/logo.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/frontend/assets/icons/splash.png b/frontend/assets/icons/splash.png deleted file mode 100644 index f0347965d8d564aac412f4c9a9ac51ac0146b371..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35932 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 implements ConnectSplash { ViewSplash? _model; + Timer? _statusTimer; + + int _status = 0; + + /// Set once the session check has been asked for, so a tap and the timer + /// never both fire it. + bool _launched = false; + + /// What the loader says while it fills — the app's three jobs, in order. + static const List _statuses = [ + "Loading your plan", + "Checking what's overdue", + "Lining up today's routines", + "Ready", + ]; + + @override + void dispose() { + _statusTimer?.cancel(); + super.dispose(); + } + @override Widget build(BuildContext context) { return ViewModelBuilder.reactive( @@ -51,136 +76,329 @@ class SplashState extends State implements ConnectSplash { } Future _initiate() async { - // Long enough for the mark to land and take one breath, then the session - // decides where we land. - await Future.delayed(const Duration(milliseconds: 2200)); + _statusTimer = Timer.periodic( + const Duration(milliseconds: 1000), _onStatusTick); + + // Long enough to watch the sun come up and read the screen once; a tap + // skips straight on. + await Future.delayed(const Duration(milliseconds: 4200)); + _launch(); + } + + void _onStatusTick(Timer timer) { + if (!mounted) { + return; + } + setState(() { + _status = (_status + 1).clamp(0, _statuses.length - 1); + }); + } + + void _onSkip() { + _launch(); + } + + void _launch() { + if (_launched || !mounted) { + return; + } + _launched = true; + _statusTimer?.cancel(); _model?.initialize(); } + String _greeting() { + final int hour = DateTime.now().hour; + if (hour < 5) { + return "Still up"; + } + if (hour < 12) { + return "Good morning"; + } + if (hour < 18) { + return "Good afternoon"; + } + return "Good evening"; + } + Widget _mobileView(BoxConstraints constraints) { - return AmbientBackground( - child: Stack( - children: [ - // The landscape rising from the bottom edge — the same painted world - // the routine cards live in. - Positioned( - left: 0, - right: 0, - bottom: 0, - height: constraints.maxHeight * 0.34, - child: ShaderMask( - shaderCallback: (Rect bounds) => const LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [Color(0x00000000), Color(0xFF000000)], - stops: [0.0, 0.45], - ).createShader(bounds), - blendMode: BlendMode.dstIn, - child: const SceneArt(palette: sceneWorkout, seed: 11), - ) - .animate() - .fadeIn(duration: 1200.ms, curve: Curves.easeOut) - .slideY(begin: 0.25, end: 0, duration: 1400.ms, - curve: Curves.easeOutCubic), - ), - SafeArea( - child: Padding( - padding: const EdgeInsets.fromLTRB(28, 18, 28, 32), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - text("Grounded", 17, TextType.Regular, color: colorInk), - text("Daily discipline", 17, TextType.Regular, - color: colorInkSoft), - ], - ).animate().fadeIn(duration: 600.ms, delay: 200.ms), - const Spacer(flex: 3), - Center(child: _mark()), - const SizedBox(height: 40), - _wordmark(), - const Spacer(flex: 4), - _loader(), - ], + return GestureDetector( + onTap: _onSkip, + behavior: HitTestBehavior.opaque, + child: AmbientBackground( + child: Stack( + children: [ + _landscape(constraints), + ..._motes(constraints), + SafeArea( + child: Padding( + padding: const EdgeInsets.fromLTRB(24, 14, 24, 22), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _topRow(), + const Spacer(flex: 2), + _hero(), + const SizedBox(height: 26), + _wordmark(), + const Spacer(flex: 2), + _howItWorks(), + const Spacer(flex: 1), + _loader(), + ], + ), ), ), - ), - ], + ], + ), ), ); } - /// The mark on glass, inside rings that breathe outward. - Widget _mark() { + /// The painted valley rising from the bottom edge while its sun comes up. + Widget _landscape(BoxConstraints constraints) { + return Positioned( + left: 0, + right: 0, + bottom: 0, + height: constraints.maxHeight * 0.42, + child: ShaderMask( + shaderCallback: (Rect bounds) => const LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Color(0x00000000), Color(0xFF000000)], + stops: [0.0, 0.4], + ).createShader(bounds), + blendMode: BlendMode.dstIn, + child: TweenAnimationBuilder( + tween: Tween(begin: 0, end: 1), + duration: const Duration(milliseconds: 3200), + curve: Curves.easeOutCubic, + builder: (context, lift, child) => SceneArt( + palette: sceneWorkout, + seed: 11, + sunLift: lift, + ), + ), + ) + .animate() + .fadeIn(duration: 900.ms) + .slideY(begin: 0.3, end: 0, duration: 1400.ms, + curve: Curves.easeOutCubic), + ); + } + + /// Specks of light drifting up out of the valley. + List _motes(BoxConstraints constraints) { + const List xs = [0.12, 0.27, 0.41, 0.58, 0.7, 0.84, 0.93]; + + return List.generate(xs.length, (index) { + final double size = 4.0 + (index % 3) * 2; + + return Positioned( + left: constraints.maxWidth * xs[index], + bottom: constraints.maxHeight * (0.08 + (index % 4) * 0.05), + child: IgnorePointer( + child: Container( + width: size, + height: size, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: colorWhite, + boxShadow: [ + BoxShadow( + color: colorWhite.withValues(alpha: 0.8), blurRadius: 8), + ], + ), + ) + .animate(onPlay: (controller) => controller.repeat()) + .fadeIn(duration: 600.ms, delay: (index * 260).ms) + .moveY( + begin: 0, + end: -140 - index * 12.0, + duration: (2600 + index * 240).ms, + delay: (index * 260).ms, + curve: Curves.easeOut, + ) + .fadeOut( + duration: 900.ms, delay: (2000 + index * 240).ms), + ), + ); + }); + } + + Widget _topRow() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + const GroundedMark(size: 26), + const SizedBox(width: 8), + text("Grounded", 16, TextType.Medium, color: colorInk), + ], + ), + GlassSurface( + radius: BorderRadius.circular(99), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 7), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(CupertinoIcons.sun_max_fill, + size: 13, color: colorStandingWarned), + const SizedBox(width: 6), + text(_greeting(), 12, TextType.Medium, color: colorInk), + ], + ), + ), + ], + ).animate().fadeIn(duration: 600.ms, delay: 150.ms).slideY( + begin: -0.4, end: 0, duration: 600.ms, delay: 150.ms); + } + + /// The mark in breathing rings, with the four things the app holds you to + /// floating around it. + Widget _hero() { return SizedBox( - width: 230, - height: 230, + height: 270, child: Stack( alignment: Alignment.center, children: [ for (int ring = 0; ring < 3; ring++) Container( - width: 120, - height: 120, + width: 140, + height: 140, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( - color: colorWhite.withValues(alpha: 0.9), width: 1.4), + color: colorWhite.withValues(alpha: 0.95), width: 1.4), ), ) .animate(onPlay: (controller) => controller.repeat()) .scale( begin: const Offset(1, 1), - end: const Offset(1.9, 1.9), - duration: 2400.ms, - delay: (ring * 800).ms, + end: const Offset(1.85, 1.85), + duration: 2700.ms, + delay: (ring * 900).ms, curve: Curves.easeOutCubic, ) - .fadeOut(duration: 2400.ms, delay: (ring * 800).ms), - GlassSurface( - radius: BorderRadius.circular(44), - padding: const EdgeInsets.all(10), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(32), - boxShadow: [ - BoxShadow( - color: colorPrimary.withValues(alpha: 0.35), - blurRadius: 40, - offset: const Offset(0, 16), - ), - ], - ), - // The sun rises out of the ground line, then the arcs light. - child: TweenAnimationBuilder( - tween: Tween(begin: 0, end: 1), - duration: const Duration(milliseconds: 1700), - builder: (context, t, child) => GroundedMark( - size: 132, - rise: Curves.easeOutCubic - .transform((t / 0.6).clamp(0.0, 1.0)), - arcs: Curves.easeOut - .transform(((t - 0.45) / 0.55).clamp(0.0, 1.0)), - ), - ), - ), - ) - .animate() - .scale( - begin: const Offset(0.6, 0.6), - end: const Offset(1, 1), - duration: 900.ms, - curve: Curves.easeOutBack, - ) - .fadeIn(duration: 500.ms), + .fadeOut(duration: 2700.ms, delay: (ring * 900).ms), + _mark(), + _chip(const Alignment(-1, -1), sceneWorkout, + kindIcon(RoutineKind.Workout), "Workout routines", 0), + _chip(const Alignment(1, -0.62), sceneMeditation, + kindIcon(RoutineKind.Meditation), "Breathing sessions", 1), + _chip(const Alignment(-1, 0.62), sceneMustDo, + kindIcon(RoutineKind.MustDo), "Must-do chains", 2), + _chip(const Alignment(1, 1), goalScene(goalAccents[4]), + CupertinoIcons.flag_fill, "Goals with deadlines", 3), ], ), ); } - /// Two-tone, like the greeting on the home screen. + Widget _mark() { + return GlassSurface( + radius: BorderRadius.circular(46), + padding: const EdgeInsets.all(10), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(34), + boxShadow: [ + BoxShadow( + color: colorPrimary.withValues(alpha: 0.4), + blurRadius: 40, + offset: const Offset(0, 16), + ), + ], + ), + // The sun rises out of the ground line, then the arcs light. + child: TweenAnimationBuilder( + tween: Tween(begin: 0, end: 1), + duration: const Duration(milliseconds: 1800), + builder: (context, t, child) => GroundedMark( + size: 128, + rise: Curves.easeOutCubic.transform((t / 0.6).clamp(0.0, 1.0)), + arcs: Curves.easeOut + .transform(((t - 0.45) / 0.55).clamp(0.0, 1.0)), + ), + ), + ), + ) + .animate() + .scale( + begin: const Offset(0.6, 0.6), + end: const Offset(1, 1), + duration: 900.ms, + curve: Curves.easeOutBack, + ) + .fadeIn(duration: 500.ms); + } + + /// A frosted chip with a painted thumbnail. It drifts in, then bobs. + Widget _chip(Alignment alignment, List palette, IconData icon, + String label, int index) { + return Align( + alignment: alignment, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(99), + boxShadow: const [ + BoxShadow( + color: Color(0x142A3A30), + blurRadius: 16, + offset: Offset(0, 6), + ), + ], + ), + child: GlassSurface( + radius: BorderRadius.circular(99), + tint: colorGlassStrong, + padding: const EdgeInsets.fromLTRB(4, 4, 12, 4), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 28, + height: 28, + child: ClipOval( + child: SceneArt( + palette: palette, + seed: 5 + index * 9, + figure: icon, + figureScale: 0.52, + figureAlignment: const Alignment(0, 0.2), + ), + ), + ), + const SizedBox(width: 8), + text(label, 12, TextType.Medium, color: colorInk), + ], + ), + ), + ) + .animate() + .fadeIn(duration: 500.ms, delay: (900 + index * 180).ms) + .slideX( + begin: alignment.x < 0 ? -0.3 : 0.3, + end: 0, + duration: 600.ms, + delay: (900 + index * 180).ms, + curve: Curves.easeOutCubic, + ) + .then() + .animate(onPlay: (controller) => controller.repeat(reverse: true)) + .moveY( + begin: -3, + end: 3, + duration: (1800 + index * 260).ms, + curve: Curves.easeInOut, + ), + ); + } + + /// Two tones, like the greeting on the home screen; the name catches the + /// light once as it lands. Widget _wordmark() { return Column( children: [ @@ -189,9 +407,11 @@ class SplashState extends State implements ConnectSplash { .animate() .fadeIn(duration: 700.ms, delay: 350.ms) .slideY(begin: 0.3, end: 0, duration: 700.ms, delay: 350.ms, - curve: Curves.easeOutCubic), - const SizedBox(height: 10), - text("A to-do app that\ndoesn't believe you.", 22, TextType.Light, + curve: Curves.easeOutCubic) + .then(delay: 300.ms) + .shimmer(duration: 1200.ms, color: colorLime), + const SizedBox(height: 8), + text("A to-do app that\ndoesn't believe you.", 21, TextType.Regular, color: colorInkSoft, align: TextAlign.center, height: 1.3) .animate() .fadeIn(duration: 700.ms, delay: 650.ms) @@ -201,7 +421,80 @@ class SplashState extends State implements ConnectSplash { ); } - /// A glass capsule that fills while the session resolves. + /// The whole idea in three beats, on glass over the valley. + Widget _howItWorks() { + return GlassSurface( + radius: BorderRadius.circular(24), + tint: colorGlassStrong, + padding: const EdgeInsets.fromLTRB(14, 14, 14, 14), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: _beat(CupertinoIcons.hand_raised_fill, "Commit", + "Say it and set a window", 0)), + _beatArrow(0), + Expanded( + child: _beat(CupertinoIcons.timer, "Do it", + "Inside the window, with proof", 1)), + _beatArrow(1), + Expanded( + child: _beat(CupertinoIcons.exclamationmark_triangle_fill, + "Or owe it", "Misses turn into debt", 2)), + ], + ), + ).animate().fadeIn(duration: 600.ms, delay: 1500.ms).slideY( + begin: 0.25, end: 0, duration: 700.ms, delay: 1500.ms, + curve: Curves.easeOutCubic); + } + + Widget _beat(IconData icon, String title, String caption, int index) { + final List tones = [ + colorPrimary, + colorKindMeditation, + colorStandingGrounded, + ]; + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 36, + height: 36, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: tones[index].withValues(alpha: 0.12), + ), + child: Icon(icon, size: 16, color: tones[index]), + ) + .animate() + .scale( + begin: const Offset(0.4, 0.4), + end: const Offset(1, 1), + duration: 500.ms, + delay: (1700 + index * 250).ms, + curve: Curves.easeOutBack, + ), + const SizedBox(height: 8), + text(title, 13, TextType.Medium, color: colorInk), + const SizedBox(height: 2), + text(caption, 10, TextType.Regular, + color: colorInkSoft, align: TextAlign.center, maxLines: 2), + ], + ); + } + + Widget _beatArrow(int index) { + return Padding( + padding: const EdgeInsets.only(top: 12), + child: Icon(CupertinoIcons.chevron_right, size: 12, color: colorInkSoft) + .animate() + .fadeIn(duration: 400.ms, delay: (1900 + index * 250).ms), + ); + } + + /// A glass capsule that fills while the session resolves, and a status + /// line that walks through what is being checked. Widget _loader() { return Column( children: [ @@ -209,33 +502,50 @@ class SplashState extends State implements ConnectSplash { radius: BorderRadius.circular(99), padding: const EdgeInsets.all(3), child: SizedBox( - width: 140, + width: 150, height: 6, child: Align( alignment: Alignment.centerLeft, child: Container( - width: 140, + width: 150, decoration: BoxDecoration( color: colorNight, borderRadius: BorderRadius.circular(99), ), - ) - .animate() - .scaleX( + ).animate().scaleX( begin: 0.04, end: 1, alignment: Alignment.centerLeft, - duration: 2000.ms, + duration: 4000.ms, curve: Curves.easeInOutCubic, ), ), ), ), - const SizedBox(height: 12), - text("Checking what you promised", 12, TextType.Regular, - color: colorWhite.withValues(alpha: 0.9)), + const SizedBox(height: 10), + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: (child, animation) => FadeTransition( + opacity: animation, + child: SlideTransition( + position: Tween( + begin: const Offset(0, 0.4), + end: Offset.zero, + ).animate(animation), + child: child, + ), + ), + child: KeyedSubtree( + key: ValueKey(_status), + child: text(_statuses[_status], 12, TextType.Medium, + color: colorWhite), + ), + ), + const SizedBox(height: 4), + text("Tap anywhere to skip", 10, TextType.Regular, + color: colorWhite.withValues(alpha: 0.7)), ], - ).animate().fadeIn(duration: 500.ms, delay: 900.ms); + ).animate().fadeIn(duration: 500.ms, delay: 600.ms); } @override diff --git a/frontend/lib/Grounded/utils/Images.dart b/frontend/lib/Grounded/utils/Images.dart index 108789a..774f3c0 100644 --- a/frontend/lib/Grounded/utils/Images.dart +++ b/frontend/lib/Grounded/utils/Images.dart @@ -3,7 +3,7 @@ const String imagePath = "assets/images"; const String iconPath = "assets/icons"; /// The mark on a night disc, as the native splash shows it. -const String splashMark = "$iconPath/splash_orb.png"; +const String splashMark = "$iconPath/splash_mark.png"; const String logoMark = "$iconPath/icon.png"; diff --git a/frontend/pubspec.yaml b/frontend/pubspec.yaml index dfd45e3..60484de 100644 --- a/frontend/pubspec.yaml +++ b/frontend/pubspec.yaml @@ -122,10 +122,10 @@ flutter_launcher_icons: # the OS screen to the app is invisible. flutter_native_splash: color: "#EAEFE6" - image: "assets/icons/splash_orb.png" + image: "assets/icons/splash_mark.png" android_12: color: "#EAEFE6" - image: "assets/icons/splash_orb.png" + image: "assets/icons/splash_mark.png" icon_background_color: "#EAEFE6" android: true ios: true diff --git a/frontend/web/index.html b/frontend/web/index.html index e54d59a..f2e3b1f 100644 --- a/frontend/web/index.html +++ b/frontend/web/index.html @@ -34,6 +34,8 @@ + +