// The boxed-widget pattern from smart-serow's HUD (pi/ui/lib/widgets/stat_box*.dart): // every readout is a box that takes its share of a row (`flex`) and sizes its type // from its own height, so a layout is a grid of boxes, and a value changing digits // never moves anything. Value in foreground with tabular figures, unit and label in // subdued; a warning turns the value to alarm. // // Added for Yukart: [dim]. A missing or stale value is a normal state in a car // (app guideline 2), shown in subdued rather than hidden. import 'package:flutter/material.dart'; import '../theme/palette.dart'; /// A labeled stat box: the small one, for rows of readouts. class StatBox extends StatelessWidget { const StatBox({ super.key, required this.value, this.unit, required this.label, this.flex = 1, this.isWarning, this.dim = false, }); final String value; final String? unit; final String label; final int flex; /// If it returns true, the value shows in alarm. final bool Function()? isWarning; /// Missing or stale: the value shows in subdued. final bool dim; @override Widget build(BuildContext context) { final theme = AppTheme.of(context); final warning = isWarning?.call() ?? false; final valueColor = dim ? theme.subdued : warning ? theme.alarm : theme.foreground; return Expanded( flex: flex, child: LayoutBuilder( builder: (context, constraints) { final baseSize = constraints.maxHeight * 0.4; // Shrinks (never wraps) when a label is wider than its box. return Center( child: FittedBox( fit: BoxFit.scaleDown, child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ // Value + optional unit Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.baseline, textBaseline: TextBaseline.alphabetic, children: [ Text( value, style: TextStyle( fontFamily: hudFont, fontSize: baseSize, fontWeight: FontWeight.w400, fontFeatures: const [FontFeature.tabularFigures()], color: valueColor, height: 1, ), ), SizedBox(width: baseSize * 0.1), if (unit != null) ...[ const SizedBox(width: 4), Text( unit!, style: TextStyle( fontFamily: hudFont, fontSize: baseSize * 0.5, fontWeight: FontWeight.w400, color: theme.subdued, height: 1, ), ), ], ], ), SizedBox(height: baseSize * 0.1), // Label Text( label, style: TextStyle( fontFamily: hudFont, fontSize: baseSize * 0.6, fontWeight: FontWeight.w400, color: theme.subdued, letterSpacing: 1, ), ), ], ), ), ); }, ), ); } } /// The large stat box, for the main area. Fixed-size: content changes don't move the layout. class StatBoxMain extends StatelessWidget { const StatBoxMain({ super.key, required this.value, this.unit, required this.label, this.flex = 1, this.isWarning, this.dim = false, }); final String value; final String? unit; final String label; final int flex; final bool Function()? isWarning; final bool dim; @override Widget build(BuildContext context) { final theme = AppTheme.of(context); final warning = isWarning?.call() ?? false; return Expanded( flex: flex, child: LayoutBuilder( builder: (context, constraints) { // Scale fonts relative to box height for consistent proportions final baseSize = constraints.maxHeight * 0.4; // Shrinks (never wraps) when a label is wider than its box. return Center( child: FittedBox( fit: BoxFit.scaleDown, child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ // Value + optional unit row Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.baseline, textBaseline: TextBaseline.alphabetic, children: [ Text( value, style: TextStyle( fontFamily: hudFont, fontSize: baseSize, fontWeight: FontWeight.w300, fontFeatures: const [FontFeature.tabularFigures()], color: dim ? theme.subdued : warning ? theme.alarm : theme.foreground, height: 1, ), ), if (unit != null) ...[ const SizedBox(width: 8), Text( unit!, style: TextStyle( fontFamily: hudFont, fontSize: baseSize * 0.4, fontWeight: FontWeight.w400, color: theme.subdued, height: 1, ), ), ], ], ), SizedBox(height: baseSize * 0.1), // Label Text( label, style: TextStyle( fontFamily: hudFont, fontSize: baseSize * 0.35, fontWeight: FontWeight.w400, color: theme.subdued, letterSpacing: 2, ), ), ], ), ), ); }, ), ); } }