first commit

This commit is contained in:
2026-08-11 13:16:33 +05:30
parent 18531bf46b
commit c8350563d9
388 changed files with 69656 additions and 0 deletions

View File

@@ -0,0 +1,206 @@
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,
fontFamilyFallback: const ['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.w700,
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.w700,
fontFamily: FontConstants.fontFamily,
),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: _primary,
textStyle: const TextStyle(
fontWeight: FontWeight.w600,
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,
),
),
);
}