import 'package:flutter/material.dart'; import '../about/internal/application/TextType.dart'; import '../utils/Colors.dart'; import 'text/Text.dart'; /// The font families, wrapped so no screen ever names a family directly. /// General Sans — see fonts/LICENSE-GeneralSans.txt. String getTextType(TextType type) { switch (type) { case TextType.Bold: return "GroundedBold"; case TextType.Light: return "GroundedLight"; case TextType.Regular: return "GroundedRegular"; case TextType.Medium: return "GroundedMedium"; } } /// A labelled pill — the standing chip, the class chip, the proof chip. One /// implementation so they stay visually identical everywhere. Widget pill( String label, Color foreground, Color background, { IconData? icon, double textSize = 10, }) { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), decoration: BoxDecoration( color: background, borderRadius: BorderRadius.circular(999), border: Border.all(color: foreground.withValues(alpha: 0.20), width: 1), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ if (icon != null) ...[ Icon(icon, size: textSize + 2, color: foreground), const SizedBox(width: 5), ], text( label.toUpperCase(), textSize, TextType.Bold, color: foreground, letterSpacing: 0.7, ), ], ), ); } /// The standard card surface. Widget card({ required Widget child, EdgeInsets padding = const EdgeInsets.all(16), EdgeInsets margin = EdgeInsets.zero, Color? background, Color? borderColor, double radius = 16, VoidCallback? onTap, }) { final Widget body = Container( width: double.infinity, padding: padding, margin: margin, decoration: BoxDecoration( color: background ?? colorCard, borderRadius: BorderRadius.circular(radius), border: Border.all(color: borderColor ?? colorBorder, width: 1), boxShadow: const [ BoxShadow( color: Color(0x08000000), blurRadius: 18, offset: Offset(0, 4), ), ], ), child: child, ); if (onTap == null) { return body; } return GestureDetector(onTap: onTap, child: body); } /// Section heading — small all-caps label over a large light title, the /// house style used on every screen header. Widget sectionHeader(String label, String title, {Color? titleColor}) { return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ text(label.toUpperCase(), 11, TextType.Bold, color: colorGrey2, letterSpacing: 1.2), const SizedBox(height: 8), text(title, 32, TextType.Light, color: titleColor ?? colorPrimaryDark), ], ); } /// A labelled statistic, used across the report card and the debt header. Widget statTile( String label, String value, { Color? valueColor, String? caption, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ text(label.toUpperCase(), 10, TextType.Bold, color: colorGrey2, letterSpacing: 0.8), const SizedBox(height: 6), text(value, 26, TextType.Bold, color: valueColor ?? colorPrimaryDark), if (caption != null) ...[ const SizedBox(height: 2), text(caption, 11, TextType.Regular, color: colorGrey2), ], ], ); } /// A hairline divider at the house opacity. Widget hairline({EdgeInsets margin = EdgeInsets.zero}) { return Container( height: 1, margin: margin, color: colorDivider, ); } /// Empty state. Deliberately plain — an empty queue is good news and should /// not be celebrated with confetti. Widget emptyState( IconData icon, String title, String description, { Color? accent, }) { final Color tone = accent ?? colorGrey2; return Center( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 48), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( width: 76, height: 76, decoration: BoxDecoration( color: tone.withValues(alpha: 0.08), shape: BoxShape.circle, ), child: Icon(icon, size: 32, color: tone), ), const SizedBox(height: 20), text(title, 18, TextType.Bold, color: colorPrimaryDark, align: TextAlign.center), const SizedBox(height: 8), text(description, 13, TextType.Regular, color: colorGrey2, align: TextAlign.center, height: 1.5), ], ), ), ); } /// A progress bar with the house geometry. Widget meter( double fraction, { Color? fill, Color? track, double height = 8, }) { final double clamped = fraction.isNaN ? 0 : fraction < 0 ? 0 : fraction > 1 ? 1 : fraction; return ClipRRect( borderRadius: BorderRadius.circular(999), child: LinearProgressIndicator( value: clamped, minHeight: height, backgroundColor: track ?? colorMuted, valueColor: AlwaysStoppedAnimation(fill ?? colorPrimary), ), ); }