first commit
This commit is contained in:
206
lib/views/helpers/constants/app_theme.dart
Normal file
206
lib/views/helpers/constants/app_theme.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user