Redesign the UI after the fitness reference and replace the logo

Light sage-to-cream ambient backdrop, frosted glass controls and painted
SceneArt landscapes in place of photography. Home becomes a dashboard with a
two-tone greeting, a frosted summary ring, category portraits, an up-next rail
of image-led cards and a floating glass tab bar. The routine library gets an
image header and painted cards; the player gets a scene stage, steps on an
arc, a dotted clock and a night pill with a lime spark. Goals become painted
cards in their own colour.

New logo: a lime sun resting on a ground line under two arcs. The splash
raises the sun and lights the arcs; launcher icons and native splash are
regenerated to match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KhQh614DMeYoQEH9SoMRUw
This commit is contained in:
alvocool
2026-11-02 12:33:13 +03:00
parent b31bc47cc0
commit fe268cb8aa
100 changed files with 3394 additions and 1387 deletions

View File

@@ -0,0 +1,117 @@
import 'dart:math' as math;
import 'package:flutter/material.dart';
import '../utils/Colors.dart';
/// The Grounded mark: a lime sun resting on a ground line beneath two
/// breathing arcs, on a night-green tile. Drawn, not bitmapped, so the splash
/// can raise the sun and light the arcs. Geometry matches the launcher icon.
class GroundedMark extends StatelessWidget {
final double size;
/// 0 → the sun is below the line; 1 → fully risen.
final double rise;
/// 0 → arcs hidden; 1 → both lit. The inner arc lights first.
final double arcs;
final bool tile;
const GroundedMark({
super.key,
this.size = 120,
this.rise = 1,
this.arcs = 1,
this.tile = true,
});
@override
Widget build(BuildContext context) {
return CustomPaint(
size: Size.square(size),
painter: _MarkPainter(rise, arcs, tile),
);
}
}
class _MarkPainter extends CustomPainter {
final double rise;
final double arcs;
final bool tile;
_MarkPainter(this.rise, this.arcs, this.tile);
@override
void paint(Canvas canvas, Size size) {
final double u = size.width / 1024;
final Rect bounds = Offset.zero & size;
if (tile) {
canvas.drawRRect(
RRect.fromRectAndRadius(bounds, Radius.circular(size.width * 0.235)),
Paint()
..shader = const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: <Color>[colorNightLift, colorNight],
).createShader(bounds),
);
}
final double cx = 512 * u;
final double horizon = 630 * u;
// Arcs — inner then outer.
final List<double> radii = <double>[250, 330];
final List<double> alphas = <double>[0.6, 0.28];
for (int i = 0; i < 2; i++) {
final double lit = ((arcs * 2) - i).clamp(0.0, 1.0);
if (lit <= 0) {
continue;
}
final double r = radii[i] * u;
canvas.drawArc(
Rect.fromCircle(center: Offset(cx, horizon), radius: r - 15 * u),
math.pi * 198 / 180,
math.pi * 144 / 180,
false,
Paint()
..style = PaintingStyle.stroke
..strokeWidth = 30 * u
..strokeCap = StrokeCap.round
..color = colorAmbientMid.withValues(alpha: alphas[i] * lit),
);
}
// The sun, clipped at the horizon so it rises out of the ground.
final double radius = 168 * u;
canvas.save();
canvas.clipRect(Rect.fromLTRB(0, 0, size.width, horizon));
canvas.drawCircle(
Offset(cx, horizon + radius * 2 * (1 - rise)),
radius,
Paint()..color = colorLime,
);
canvas.restore();
// Ground line.
final double ground = horizon + 52 * u;
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromLTRB(250 * u, ground - 20 * u, 774 * u, ground + 20 * u),
Radius.circular(20 * u),
),
Paint()..color = colorAmbientMid,
);
}
@override
bool shouldRepaint(covariant _MarkPainter oldDelegate) {
return oldDelegate.rise != rise ||
oldDelegate.arcs != arcs ||
oldDelegate.tile != tile;
}
}