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>
213 lines
8.9 KiB
Dart
213 lines
8.9 KiB
Dart
import 'package:flutter/cupertino.dart' show CupertinoPageTransitionsBuilder;
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter/services.dart';
|
||
import 'package:miler/views/helpers/constants/Colorconstants.dart';
|
||
import 'package:miler/views/helpers/constants/design_constants.dart';
|
||
import 'package:miler/views/helpers/constants/Font_constant.dart';
|
||
|
||
/// Centralized app theme (single source of truth for colors, type, and
|
||
/// component styling). Screens should lean on this instead of re-specifying
|
||
/// `ColorConstants.*` + `FontConstants.fontFamily` on every widget.
|
||
///
|
||
/// Everything below maps to the existing red/maroon brand via [ColorConstants]
|
||
/// — no colors changed, just consolidated and extended with component themes
|
||
/// (buttons, inputs, snackbars, sheets, dividers) so inline styling can be
|
||
/// retired incrementally.
|
||
class AppTheme {
|
||
AppTheme._();
|
||
|
||
static const Color _primary = ColorConstants.primary;
|
||
|
||
/// The app's one theme.
|
||
static ThemeData get light => _build();
|
||
|
||
static ThemeData _build() => ThemeData(
|
||
useMaterial3: true,
|
||
brightness: Brightness.light,
|
||
// The floor for page motion. Named routes and any route pushed without
|
||
// going through `openScreen`/`openSheet` still slide horizontally with
|
||
// the parallax and edge-swipe-back, instead of Android's default
|
||
// fade-through zoom — which, on the mid-range phones riders carry,
|
||
// reads as the new screen simply appearing.
|
||
pageTransitionsTheme: const PageTransitionsTheme(
|
||
builders: {
|
||
TargetPlatform.android: CupertinoPageTransitionsBuilder(),
|
||
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
|
||
},
|
||
),
|
||
fontFamily: FontConstants.fontFamily,
|
||
// Manrope first: it is bundled, so its coverage is guaranteed on every
|
||
// device, and it is what backs Poppins up for anything Poppins does not
|
||
// carry. The platform families behind it are the last resort. Poppins is
|
||
// missing U+2192 among others — a glyph the app no longer renders as a
|
||
// character, but the net belongs here rather than in a rule nobody
|
||
// remembers.
|
||
fontFamilyFallback: const ['Manrope', 'Roboto', 'sans-serif'],
|
||
textTheme: ThemeData.light().textTheme.apply(
|
||
fontFamily: FontConstants.fontFamily,
|
||
),
|
||
primaryTextTheme: ThemeData.light().primaryTextTheme.apply(
|
||
fontFamily: FontConstants.fontFamily,
|
||
),
|
||
colorScheme: const ColorScheme(
|
||
brightness: Brightness.light,
|
||
primary: ColorConstants.primary,
|
||
onPrimary: ColorConstants.onPrimary,
|
||
primaryContainer: ColorConstants.primaryContainer,
|
||
onPrimaryContainer: ColorConstants.onPrimaryContainer,
|
||
secondary: ColorConstants.secondary,
|
||
onSecondary: ColorConstants.onSecondary,
|
||
secondaryContainer: ColorConstants.secondaryContainer,
|
||
onSecondaryContainer: ColorConstants.onSurface,
|
||
tertiary: ColorConstants.tertiary,
|
||
onTertiary: ColorConstants.onTertiary,
|
||
tertiaryContainer: ColorConstants.tertiaryContainer,
|
||
onTertiaryContainer: ColorConstants.onPrimary,
|
||
error: ColorConstants.error,
|
||
onError: ColorConstants.onError,
|
||
errorContainer: ColorConstants.errorContainer,
|
||
onErrorContainer: ColorConstants.onSurface,
|
||
surface: ColorConstants.surface,
|
||
onSurface: ColorConstants.onSurface,
|
||
surfaceContainerHighest: ColorConstants.surfaceContainerHighest,
|
||
outline: ColorConstants.outline,
|
||
outlineVariant: ColorConstants.outlineVariant,
|
||
),
|
||
appBarTheme: const AppBarTheme(
|
||
backgroundColor: ColorConstants.pureSurface,
|
||
elevation: 0,
|
||
foregroundColor: ColorConstants.onSurface,
|
||
systemOverlayStyle: SystemUiOverlayStyle(
|
||
statusBarColor: Colors.transparent,
|
||
statusBarIconBrightness: Brightness.dark,
|
||
statusBarBrightness: Brightness.light,
|
||
),
|
||
),
|
||
scaffoldBackgroundColor: ColorConstants.surface,
|
||
progressIndicatorTheme: const ProgressIndicatorThemeData(
|
||
color: _primary,
|
||
linearTrackColor: ColorConstants.surfaceContainerHigh,
|
||
),
|
||
dividerTheme: const DividerThemeData(
|
||
color: ColorConstants.dividerColor,
|
||
thickness: 1,
|
||
space: 1,
|
||
),
|
||
cardTheme: CardThemeData(
|
||
color: Colors.white,
|
||
surfaceTintColor: Colors.transparent,
|
||
elevation: 0,
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(DesignConstants.radiusXl),
|
||
side: const BorderSide(color: ColorConstants.outlineVariant, width: 1),
|
||
),
|
||
),
|
||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||
style: ElevatedButton.styleFrom(
|
||
backgroundColor: _primary,
|
||
foregroundColor: Colors.white,
|
||
disabledBackgroundColor: _primary.withValues(alpha: 0.35),
|
||
disabledForegroundColor: Colors.white.withValues(alpha: 0.9),
|
||
elevation: 0,
|
||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 18),
|
||
minimumSize: const Size(double.infinity, ButtonSizes.primary),
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(ButtonSizes.radius),
|
||
),
|
||
textStyle: const TextStyle(
|
||
fontSize: 16,
|
||
fontWeight: FontWeight.w600,
|
||
fontFamily: FontConstants.fontFamily,
|
||
),
|
||
),
|
||
),
|
||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||
style: OutlinedButton.styleFrom(
|
||
foregroundColor: _primary,
|
||
side: const BorderSide(color: _primary, width: 1.4),
|
||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
||
// ── Why this line exists ──
|
||
//
|
||
// `ElevatedButton` above pins its height to 56. `OutlinedButton`
|
||
// used to pin nothing, so it fell back to Material's default 40dp
|
||
// floor and sized itself from its own padding instead. That is
|
||
// invisible until the two appear together — which is exactly where
|
||
// they always appear, as the Cancel/Confirm pair of a dialog. Each
|
||
// of those rows overrode padding on both buttons and assumed that
|
||
// settled the height; it did not, because `styleFrom` only replaces
|
||
// the keys you hand it. Cancel came out 44–49 and Confirm came out
|
||
// 56, side by side, in three separate dialogs.
|
||
//
|
||
// Matching the height here means a plain `OutlinedButton` and a
|
||
// plain `ElevatedButton` agree by default, and nobody has to know
|
||
// about the merge order to get a straight row.
|
||
//
|
||
// Deliberately `64` wide and not `double.infinity`: outlined
|
||
// buttons are the ones that share a row or hug their label, so
|
||
// forcing full width would be wrong more often than right. A
|
||
// button that must be shorter than 56 has to say so explicitly
|
||
// with `minimumSize: Size.zero` — see [SecondaryButton].
|
||
minimumSize: const Size(64, ButtonSizes.primary),
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(ButtonSizes.radius),
|
||
),
|
||
textStyle: const TextStyle(
|
||
fontSize: 15,
|
||
fontWeight: FontWeight.w600,
|
||
fontFamily: FontConstants.fontFamily,
|
||
),
|
||
),
|
||
),
|
||
textButtonTheme: TextButtonThemeData(
|
||
style: TextButton.styleFrom(
|
||
foregroundColor: _primary,
|
||
textStyle: const TextStyle(
|
||
fontWeight: FontWeight.w500,
|
||
fontFamily: FontConstants.fontFamily,
|
||
),
|
||
),
|
||
),
|
||
snackBarTheme: SnackBarThemeData(
|
||
behavior: SnackBarBehavior.floating,
|
||
backgroundColor: ColorConstants.onSurface,
|
||
contentTextStyle: const TextStyle(
|
||
color: Colors.white,
|
||
fontFamily: FontConstants.fontFamily,
|
||
),
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(DesignConstants.radiusLg),
|
||
),
|
||
),
|
||
bottomSheetTheme: BottomSheetThemeData(
|
||
backgroundColor: ColorConstants.pureSurface,
|
||
surfaceTintColor: Colors.transparent,
|
||
shape: const RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
|
||
),
|
||
),
|
||
inputDecorationTheme: InputDecorationTheme(
|
||
filled: true,
|
||
fillColor: ColorConstants.pureSurface,
|
||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||
border: OutlineInputBorder(
|
||
borderRadius: BorderRadius.circular(DesignConstants.radiusXl),
|
||
borderSide: const BorderSide(color: ColorConstants.outlineVariant),
|
||
),
|
||
enabledBorder: OutlineInputBorder(
|
||
borderRadius: BorderRadius.circular(DesignConstants.radiusXl),
|
||
borderSide: const BorderSide(color: ColorConstants.outlineVariant),
|
||
),
|
||
focusedBorder: OutlineInputBorder(
|
||
borderRadius: BorderRadius.circular(DesignConstants.radiusXl),
|
||
borderSide: const BorderSide(color: _primary, width: 2),
|
||
),
|
||
labelStyle: const TextStyle(
|
||
color: ColorConstants.onSurfaceVariant,
|
||
fontFamily: FontConstants.fontFamily,
|
||
fontSize: 14,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
);
|
||
}
|