Design system - MilerSurface ladder (canvas → working → raised → floating) with MilerPanel as layer 1; canvas moved to #DEE3EA so white separates at 1.290:1. - Visible vocabulary applied across Home, Deliveries, Activity, Account and the sheets: hero heads (tabular numeral + small caption, clamped at 1.3x), canvas wells for anything that opens, small filled tags for shelf labels, demoted placeholders. Recorded in DESIGN_SYSTEM.md §6. - One icon family: 222 Material glyphs migrated to Lucide; none left outside lib/xpress. - Colour semantics corrected: amber only for what is genuinely owed, brand red reserved for the live stop, disabled primaries go neutral rather than pale. Data and lifecycle - lib/data/lifecycle.dart reads mutations for what they prove; route_order.dart makes admin sequence the single ordering authority; service_day.dart, and stop_area.dart rewritten against live Coimbatore addresses (digit-token stripping, city stoplist, street suffixes, stammer collapse). - countLabel states the load once, in bags. Testing - 1440 tests passing; golden shot harnesses for Home, Deliveries, Activity, sheets and verify, with test/failures/ now gitignored (diff debris). - New pins: home_gutter_test, stop_area_test, plus updated structural bounds. Note: this commit also carries pre-existing working-tree deletions that were present before this work (API_SPEC.md, README.md, demo test fixtures). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
169 lines
5.7 KiB
Dart
169 lines
5.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
class FontConstants {
|
|
/// ── The app's typeface ──
|
|
///
|
|
/// Poppins: geometric, OFL-licensed, and the one every screen now reads from.
|
|
/// It replaced Manrope, which was itself a stand-in for Uber Move.
|
|
///
|
|
/// **Read this before changing it.** The family was already declared in
|
|
/// `pubspec.yaml` as `poppins`, with a lowercase path on every one of its
|
|
/// seven assets, while the files on disk are `assets/fonts/Poppins/Poppins-*`.
|
|
/// Asset bundling is case-sensitive on both platforms, so not one of them
|
|
/// resolved: anything asking for the family fell back to Roboto and no error
|
|
/// was raised anywhere. The declaration is fixed and all seven static weights
|
|
/// are registered — Poppins ships as separate files rather than a variable
|
|
/// font, and without explicit 800/900 entries Flutter synthesises them by
|
|
/// smearing SemiBold, which is what makes a heading look muddy at exactly the
|
|
/// size it is meant to be read from.
|
|
/// ── The weight scale is calibrated for THIS family ──
|
|
///
|
|
/// Poppins runs optically heavier than Manrope at every grade: it is
|
|
/// geometric, its bowls are wide and its stems thick, so `w800` here is far
|
|
/// darker on the page than `w800` was in a variable Manrope. The whole scale
|
|
/// was tuned against Manrope and came across a grade too heavy — headings
|
|
/// read as shouting and body copy as bold.
|
|
///
|
|
/// Every weight in `lib/` (outside `lib/xpress`, which is Proxima Nova) was
|
|
/// stepped down one grade: 900→800, 800→700, 700→600, 600→500. The *relative*
|
|
/// hierarchy is untouched — each level is still one step from its neighbour —
|
|
/// so nothing lost its rank; the whole run just sits where it should for this
|
|
/// typeface.
|
|
///
|
|
/// **If the family changes again, re-tune this.** A weight is a number, not a
|
|
/// design: what it looks like belongs to the font.
|
|
static const String fontFamily = 'Poppins';
|
|
|
|
/// Only ever reached for a glyph Poppins does not carry. Manrope covers the
|
|
/// Latin and currency sets the app uses, so this is a safety net rather than
|
|
/// a second typeface in the design.
|
|
static String fontFamilyFallback = 'Manrope';
|
|
|
|
// Typography scale
|
|
static const double displayLg = 32.0;
|
|
static const double headlineMd = 24.0;
|
|
static const double headlineSm = 20.0;
|
|
static const double bodyLg = 18.0;
|
|
static const double bodyMd = 16.0;
|
|
static const double labelBold = 14.0;
|
|
static const double labelSm = 12.0;
|
|
|
|
static TextStyle displayLgStyle = const TextStyle(
|
|
fontSize: 32,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: -0.64,
|
|
fontFamily: fontFamily,
|
|
);
|
|
|
|
static TextStyle headlineMdStyle = const TextStyle(
|
|
fontSize: 24,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: -0.24,
|
|
fontFamily: fontFamily,
|
|
);
|
|
|
|
static TextStyle headlineSmStyle = const TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.w500,
|
|
letterSpacing: -0.2,
|
|
fontFamily: fontFamily,
|
|
);
|
|
|
|
static TextStyle bodyLgStyle = const TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w500,
|
|
fontFamily: fontFamily,
|
|
);
|
|
|
|
static TextStyle bodyMdStyle = const TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w400,
|
|
fontFamily: fontFamily,
|
|
);
|
|
|
|
static TextStyle labelBoldStyle = const TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
fontFamily: fontFamily,
|
|
);
|
|
|
|
static TextStyle labelSmStyle = const TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
fontFamily: fontFamily,
|
|
);
|
|
|
|
static double getResponsiveFontSize(BuildContext context, double baseSize) {
|
|
return baseSize;
|
|
}
|
|
|
|
static double extraSmall(BuildContext context) =>
|
|
getResponsiveFontSize(context, 10);
|
|
static double small(BuildContext context) =>
|
|
getResponsiveFontSize(context, labelSm);
|
|
static double medium(BuildContext context) =>
|
|
getResponsiveFontSize(context, bodyMd);
|
|
static double regular(BuildContext context) =>
|
|
getResponsiveFontSize(context, bodyLg);
|
|
static double large(BuildContext context) =>
|
|
getResponsiveFontSize(context, headlineSm);
|
|
static double xLarge(BuildContext context) =>
|
|
getResponsiveFontSize(context, headlineMd);
|
|
static double xxLarge(BuildContext context) =>
|
|
getResponsiveFontSize(context, 28);
|
|
static double xxxLarge(BuildContext context) =>
|
|
getResponsiveFontSize(context, 30);
|
|
static double huge(BuildContext context) =>
|
|
getResponsiveFontSize(context, displayLg);
|
|
}
|
|
|
|
class ReusableTextWidget extends StatelessWidget {
|
|
final String text;
|
|
final double? fontSize;
|
|
final double? textHeight;
|
|
final String? fontFamily;
|
|
final FontWeight? fontWeight;
|
|
final FontStyle? fontStyle;
|
|
final Color? color;
|
|
final TextAlign? textAlign;
|
|
final int? maxLines;
|
|
final TextDecoration? isUnderText;
|
|
|
|
const ReusableTextWidget({
|
|
super.key,
|
|
required this.text,
|
|
this.fontSize,
|
|
this.textHeight,
|
|
this.fontFamily,
|
|
this.fontWeight,
|
|
this.fontStyle,
|
|
this.color,
|
|
this.textAlign,
|
|
this.maxLines,
|
|
this.isUnderText,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Text(
|
|
text,
|
|
softWrap: true,
|
|
style: TextStyle(
|
|
fontSize: fontSize ?? FontConstants.medium(context),
|
|
decoration: isUnderText,
|
|
fontFamily: fontFamily ?? FontConstants.fontFamily,
|
|
decorationColor: color,
|
|
fontWeight: fontWeight ?? FontWeight.normal,
|
|
fontStyle: fontStyle ?? FontStyle.normal,
|
|
color: color ?? Colors.grey.shade900,
|
|
overflow: TextOverflow.ellipsis,
|
|
decorationStyle: TextDecorationStyle.solid,
|
|
decorationThickness: 1,
|
|
height: textHeight,
|
|
),
|
|
maxLines: maxLines,
|
|
textAlign: textAlign ?? TextAlign.start,
|
|
);
|
|
}
|
|
}
|