feat(train): a recording under way above the bar on every tab, and a saved one opens its page (ADR 0030, D60)

halkyon_ui's NavigationFrame takes a status, standing above the floating
bar below medium windows and at the foot of the screen's panel from there
on. Train's RecordingBar fills it while a recording runs or waits to be
saved: its activity, moving time and distance, paused or stopped, a tap
back to it. Saving now opens the workout's page in Home's tab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-31 12:12:55 +01:00
commit 7473366f66
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -54,6 +54,12 @@ class const NavigationFrame({
/// top-level screens and the actions of those below in a toolbar, never
/// both at once (as Vaia does). The rail of wider windows stays.
final bool hidesNavigationBar = false,
/// What stands above the bar on every tab while it matters (Train's
/// recording under way, as Strava's): floating above the bar below
/// medium windows, at the foot of the screen's panel from there on; none
/// while null.
final Widget? status,
super.key,
}) extends StatefulWidget {
@override
@ -102,7 +108,15 @@ class _NavigationFrameState() extends State<NavigationFrame> {
),
],
),
child: widget.child,
child: switch (widget.status) {
final status? => Column(
children: [
Expanded(child: widget.child),
Padding(padding: const EdgeInsets.all(12), child: status),
],
),
null => widget.child,
},
),
);
}
@ -142,9 +156,30 @@ class _NavigationFrameState() extends State<NavigationFrame> {
// Without the bar, a strip as high as the system's own bar, in
// the screen's background, keeps every screen inside clear of
// it.
bottomNavigationBar: widget.hidesNavigationBar
? SizedBox(height: MediaQuery.paddingOf(context).bottom)
: bar,
bottomNavigationBar: switch (widget.status) {
final status? => Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(
FloatingNavigationBar.sideMargin,
0,
FloatingNavigationBar.sideMargin,
FloatingNavigationBar.lift,
),
child: status,
),
if (widget.hidesNavigationBar)
SizedBox(height: MediaQuery.paddingOf(context).bottom)
else
bar,
],
),
null when widget.hidesNavigationBar => SizedBox(
height: MediaQuery.paddingOf(context).bottom,
),
null => bar,
},
),
),
),