Shrink HUD stat tiles, control bar buttons, and nav bar

Direct feedback: HUD stat tiles filled nearly the whole screen above the
map, and the pause/stop/discard control bar and bottom nav bar were both
oversized. Caps the HUD grid to a fixed compact band instead of filling
down to the control bar, shrinks the handheld control bar from 72dp to
56dp (mounted mode stays 96dp for glove use), shrinks its icons from 32
to 24 outside mounted mode, and trims the nav bar from Material 3's
default 80dp to 64dp.
This commit is contained in:
2026-08-25 13:02:51 -05:00
parent 3b6aed2fa2
commit a96a22aabc
2 changed files with 43 additions and 8 deletions

View File

@@ -138,6 +138,11 @@ class _ShellNavBar extends StatelessWidget {
@override
Widget build(BuildContext context) => NavigationBar(
key: const Key('shell-nav-bar'),
// Material 3's own default (80dp) reads as oversized for a 4-item bar with plain
// icon+label destinations -- direct feedback (`docs/FEEDBACK.md`) called this out.
// 64dp keeps every icon/label pair at its default size, just with less surrounding
// padding, and stays comfortably above the 48dp minimum touch target.
height: 64,
selectedIndex: currentIndex,
onDestinationSelected: onDestinationSelected,
destinations: const [

View File

@@ -156,11 +156,21 @@ class _RecordScreenState extends ConsumerState<RecordScreen> {
}
}
/// 72dp normally, 96dp mounted -- one constant height regardless of how many
/// 56dp normally, 96dp mounted -- one constant height regardless of how many
/// segments the control bar has (one/two/three), so the HUD area above it is always
/// the same size and a widget's saved fractional position never jumps between ride
/// states.
double _controlBarHeight(bool mountedMode) => mountedMode ? 96 : 72;
/// states. Mounted mode stays at 96dp (V3-05: not enough at speed, with gloves,
/// below that); the handheld case only needs a comfortable tap target, not a glove
/// target, and 56dp is still well above the 48dp accessibility floor.
double _controlBarHeight(bool mountedMode) => mountedMode ? 96 : 56;
/// The HUD stat grid used to fill the entire area down to the control bar, which
/// made even a two-row default layout balloon to take up most of the screen on a
/// tall phone -- direct feedback (`docs/FEEDBACK.md`) called this out explicitly.
/// Capping it to a fixed, compact band keeps every grid cell a sane physical size
/// regardless of screen height, leaving the rest of the screen as pure map. Mounted
/// mode gets a taller band to match its own larger text scale ([mountedTextScale]).
double _hudAreaHeight(bool mountedMode) => mountedMode ? 320 : 260;
@override
Widget build(BuildContext context) {
@@ -195,8 +205,11 @@ class _RecordScreenState extends ConsumerState<RecordScreen> {
child: Stack(
children: [
if (!ui.isIdle)
Positioned.fill(
bottom: _controlBarHeight(mountedMode),
Positioned(
top: 0,
left: 0,
right: 0,
height: _hudAreaHeight(mountedMode),
child: HudEditOverlay(
metricBuilder: (context, metric) => _HudMetricValue(
metric: metric,
@@ -237,6 +250,7 @@ class _RecordScreenState extends ConsumerState<RecordScreen> {
height: _controlBarHeight(mountedMode),
child: _ControlBar(
ui: ui,
mountedMode: mountedMode,
onStart: () => _guard(engine.start),
onPause: () => _guard(engine.pause),
onStop: () => _guard(() async => engine.stop()),
@@ -379,6 +393,7 @@ class _ControlBar extends StatelessWidget {
required this.onPause,
required this.onStop,
required this.onDiscard,
this.mountedMode = false,
});
final RecordUiState ui;
@@ -387,6 +402,11 @@ class _ControlBar extends StatelessWidget {
final VoidCallback onStop;
final VoidCallback onDiscard;
/// V3-05: mounted mode keeps full-size, glove-friendly icons. The handheld case
/// only needs a comfortable tap target, not a glove target, so its icons shrink
/// along with the bar itself (`_controlBarHeight`) -- see [_Segment.iconSize].
final bool mountedMode;
@override
Widget build(BuildContext context) {
if (ui.isIdle) {
@@ -397,6 +417,7 @@ class _ControlBar extends StatelessWidget {
background: Theme.of(context).colorScheme.tertiaryContainer,
foreground: Theme.of(context).colorScheme.onTertiaryContainer,
onTap: onStart,
mountedMode: mountedMode,
);
}
@@ -412,6 +433,7 @@ class _ControlBar extends StatelessWidget {
foreground: colors.onTertiaryContainer,
onTap: onPause,
trailingBorder: true,
mountedMode: mountedMode,
),
),
Expanded(
@@ -421,6 +443,7 @@ class _ControlBar extends StatelessWidget {
background: colors.errorContainer,
foreground: colors.onErrorContainer,
onTap: onStop,
mountedMode: mountedMode,
),
),
],
@@ -441,6 +464,7 @@ class _ControlBar extends StatelessWidget {
foreground: colors.onTertiaryContainer,
onTap: onStart,
trailingBorder: true,
mountedMode: mountedMode,
),
),
Expanded(
@@ -451,6 +475,7 @@ class _ControlBar extends StatelessWidget {
foreground: colors.onErrorContainer,
onTap: onStop,
trailingBorder: true,
mountedMode: mountedMode,
),
),
Expanded(
@@ -460,6 +485,7 @@ class _ControlBar extends StatelessWidget {
background: colors.surfaceContainerHighest,
foreground: colors.error,
onTap: onDiscard,
mountedMode: mountedMode,
),
),
],
@@ -476,6 +502,7 @@ class _Segment extends StatelessWidget {
required this.onTap,
this.label,
this.trailingBorder = false,
this.mountedMode = false,
});
final String keyName;
@@ -485,6 +512,9 @@ class _Segment extends StatelessWidget {
final VoidCallback onTap;
final String? label;
final bool trailingBorder;
final bool mountedMode;
double get _iconSize => mountedMode ? 32 : 24;
@override
Widget build(BuildContext context) => Material(
@@ -505,18 +535,18 @@ class _Segment extends StatelessWidget {
child: SizedBox.expand(
child: Center(
child: label == null
? Icon(icon, color: foreground, size: 32)
? Icon(icon, color: foreground, size: _iconSize)
: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: foreground),
Icon(icon, color: foreground, size: _iconSize),
const SizedBox(width: 8),
Text(
label!,
style: TextStyle(
color: foreground,
fontWeight: FontWeight.bold,
fontSize: 16,
fontSize: mountedMode ? 16 : 14,
),
),
],