initial commit
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'app_colors.dart';
|
||||
import 'app_data_styles.dart';
|
||||
import 'status_colors.dart';
|
||||
|
||||
/// Builds the app's light/dark [ThemeData]. Colors are constructed
|
||||
/// explicitly from [AppColors] rather than via [ColorScheme.fromSeed] so the
|
||||
/// approved mockup's exact hex values can't drift through algorithmic tone
|
||||
/// generation.
|
||||
abstract final class AppTheme {
|
||||
static ThemeData light() => _build(brightness: Brightness.light);
|
||||
static ThemeData dark() => _build(brightness: Brightness.dark);
|
||||
|
||||
static ThemeData _build({required Brightness brightness}) {
|
||||
final isDark = brightness == Brightness.dark;
|
||||
|
||||
final colorScheme = isDark
|
||||
? const ColorScheme.dark(
|
||||
surface: AppColors.darkBg,
|
||||
onSurface: AppColors.darkText,
|
||||
surfaceContainerHighest: AppColors.darkSurfaceAlt,
|
||||
primary: AppColors.darkAccent,
|
||||
onPrimary: AppColors.darkAccentInk,
|
||||
secondary: AppColors.darkAccentStrong,
|
||||
onSecondary: AppColors.darkAccentInk,
|
||||
error: AppColors.critical,
|
||||
onError: Colors.white,
|
||||
outline: AppColors.darkBorder,
|
||||
outlineVariant: AppColors.darkBorder,
|
||||
)
|
||||
: const ColorScheme.light(
|
||||
surface: AppColors.lightBg,
|
||||
onSurface: AppColors.lightText,
|
||||
surfaceContainerHighest: AppColors.lightSurfaceAlt,
|
||||
primary: AppColors.lightAccent,
|
||||
onPrimary: AppColors.lightAccentInk,
|
||||
secondary: AppColors.lightAccentStrong,
|
||||
onSecondary: AppColors.lightAccentInk,
|
||||
error: AppColors.critical,
|
||||
onError: Colors.white,
|
||||
outline: AppColors.lightBorder,
|
||||
outlineVariant: AppColors.lightBorder,
|
||||
);
|
||||
|
||||
final surfaceCard = isDark ? AppColors.darkSurface : AppColors.lightSurface;
|
||||
final textColor = isDark ? AppColors.darkText : AppColors.lightText;
|
||||
final textMuted = isDark ? AppColors.darkTextMuted : AppColors.lightTextMuted;
|
||||
final textFaint = isDark ? AppColors.darkTextFaint : AppColors.lightTextFaint;
|
||||
final border = isDark ? AppColors.darkBorder : AppColors.lightBorder;
|
||||
|
||||
final textTheme = TextTheme(
|
||||
displaySmall: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 30, color: textColor, letterSpacing: -0.2),
|
||||
headlineSmall: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 22, color: textColor, letterSpacing: -0.1),
|
||||
titleLarge: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 17, color: textColor, letterSpacing: -0.1),
|
||||
titleMedium: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w600, fontSize: 14, color: textColor),
|
||||
titleSmall: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 11, color: textFaint, letterSpacing: 0.6),
|
||||
bodyLarge: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w400, fontSize: 15, color: textColor, height: 1.5),
|
||||
bodyMedium: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w400, fontSize: 13.5, color: textColor, height: 1.45),
|
||||
bodySmall: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w500, fontSize: 12, color: textMuted),
|
||||
labelLarge: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w600, fontSize: 13, color: textColor),
|
||||
labelMedium: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w600, fontSize: 12, color: textMuted),
|
||||
labelSmall: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 11, color: textFaint, letterSpacing: 0.5),
|
||||
);
|
||||
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: brightness,
|
||||
colorScheme: colorScheme,
|
||||
scaffoldBackgroundColor: colorScheme.surface,
|
||||
canvasColor: colorScheme.surface,
|
||||
dividerColor: border,
|
||||
fontFamily: 'Inter',
|
||||
textTheme: textTheme,
|
||||
cardTheme: CardThemeData(
|
||||
color: surfaceCard,
|
||||
elevation: 0,
|
||||
margin: EdgeInsets.zero,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
side: BorderSide(color: border),
|
||||
),
|
||||
),
|
||||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: colorScheme.surface,
|
||||
foregroundColor: textColor,
|
||||
elevation: 0,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
),
|
||||
navigationRailTheme: NavigationRailThemeData(
|
||||
backgroundColor: isDark ? AppColors.darkSurfaceAlt : AppColors.lightSurfaceAlt,
|
||||
indicatorColor: colorScheme.primary.withValues(alpha: 0.16),
|
||||
selectedIconTheme: IconThemeData(color: colorScheme.secondary),
|
||||
unselectedIconTheme: IconThemeData(color: textMuted),
|
||||
selectedLabelTextStyle: TextStyle(color: colorScheme.secondary, fontWeight: FontWeight.w700, fontSize: 12.5),
|
||||
unselectedLabelTextStyle: TextStyle(color: textMuted, fontWeight: FontWeight.w500, fontSize: 12.5),
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: colorScheme.primary,
|
||||
foregroundColor: colorScheme.onPrimary,
|
||||
elevation: 0,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(7)),
|
||||
textStyle: const TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w600, fontSize: 13),
|
||||
),
|
||||
),
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: textColor,
|
||||
side: BorderSide(color: border),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(7)),
|
||||
textStyle: const TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w600, fontSize: 13),
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
filled: true,
|
||||
fillColor: colorScheme.surface,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
borderSide: BorderSide(color: border),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
borderSide: BorderSide(color: border),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
borderSide: BorderSide(color: colorScheme.primary, width: 1.5),
|
||||
),
|
||||
labelStyle: TextStyle(color: textMuted, fontSize: 12.5, fontWeight: FontWeight.w600),
|
||||
),
|
||||
dataTableTheme: DataTableThemeData(
|
||||
headingTextStyle: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 11, color: textFaint, letterSpacing: 0.5),
|
||||
dataTextStyle: TextStyle(fontFamily: 'Inter', fontSize: 13, color: textColor),
|
||||
dividerThickness: 1,
|
||||
),
|
||||
extensions: [
|
||||
isDark ? StatusColors.dark : StatusColors.light,
|
||||
AppDataStyles.resolve(textColor: textColor, mutedColor: textMuted),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user