Stage 1: project setup, Exercise Library, both Mode designs and the Mode switch

- Flutter Android app on CookIT's stack (provider, shared_preferences, gen-l10n)
- tools/exercise-import turns free-exercise-db into 876 bundled Exercises
  with 1,746 WebP images (20 MB); stretching -> Stretching, rest -> Workout
- ZenStyle tokens per Mode: calm rounded sage/teal with breath ripples vs.
  dark ember-to-red with floor-tape slashes; the theme morphs at each
  Mode's own pace
- Remembered global Mode switch, Plans / Library / History tabs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
CedricandClaude Opus 5.5 committed 2026-10-28 23:18:03 +01:00
1 parent 5dfa995d55
commit 1289fadb98
1822 files changed
+2819

No files matched your search

+69
View File
@@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import 'zen_style.dart';
/// A tab's big title and an optional line under it.
class PageHeader extends StatelessWidget {
const PageHeader({super.key, required this.title, this.subtitle});
final String title;
final String? subtitle;
@override
Widget build(BuildContext context) {
final s = ZenStyle.of(context);
final text = Theme.of(context).textTheme;
return Padding(
padding: const EdgeInsets.fromLTRB(20, 28, 20, 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: text.headlineLarge),
if (subtitle != null) ...[
const SizedBox(height: 6),
Text(subtitle!, style: text.bodyLarge?.copyWith(color: s.inkMuted)),
],
],
),
);
}
}
/// What an empty tab shows: what will appear here and how to get it there.
class EmptyState extends StatelessWidget {
const EmptyState({super.key, required this.icon, required this.title, required this.body});
final IconData icon;
final String title;
final String body;
@override
Widget build(BuildContext context) {
final s = ZenStyle.of(context);
final text = Theme.of(context).textTheme;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
color: s.surfaceSoft,
borderRadius: BorderRadius.circular(s.controlRadius.clamp(0, 28)),
),
child: Icon(icon, color: s.accent),
),
const SizedBox(height: 16),
Text(title, style: text.titleLarge),
const SizedBox(height: 6),
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 340),
child: Text(body, style: text.bodyMedium?.copyWith(color: s.inkMuted)),
),
],
),
);
}
}