Files
ZenXercise/lib/src/design/page_header.dart
T
CedricandClaude Opus 5.5 bbdca95541 Stage 3: Plan editor with Steps, Supersets and reordering
- Plan model with immutable edits: add with Mode defaults, move, remove,
  Superset with next, take a member out, ungroup; Stretching length
  estimate from holds, sides, rounds and Transitions
- drift database (plans table, items as JSON, ADR 0005) and
  PlanRepository; Plans list per Mode with rename/duplicate/delete
- Editor: drag handles, step editor sheet (sets, reps or time, optional
  kg; hold, both sides, rounds; Superset sets and rest between),
  pause between holds for Stretching Plans
- Exercise picker reusing the Library browser; Add to plan from the
  Exercise detail page
- Theme: Mode-styled snackbar, FAB, menus, segmented buttons, switches

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 23:48:07 +01:00

78 lines
2.3 KiB
Dart

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, this.action});
final String title;
final String? subtitle;
/// A button beside the title, such as "New plan".
final Widget? action;
@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: [
Row(
children: [
Expanded(child: Text(title, style: text.headlineLarge)),
if (action != null) ...[const SizedBox(width: 12), action!],
],
),
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)),
),
],
),
);
}
}