Files
xtremobile/lib/core/theme/app_theme.dart
T

624 lines
20 KiB
Dart

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'app_colors.dart';
/// XtremFlow Modern Theme
///
/// Complete theme with typography, spacing, and component theming
class AppTheme {
AppTheme._();
// ============ SPACING SCALE ============
static const double spacing4 = 4.0;
static const double spacing8 = 8.0;
static const double spacing12 = 12.0;
static const double spacing16 = 16.0;
static const double spacing24 = 24.0;
static const double spacing32 = 32.0;
static const double spacing48 = 48.0;
// ============ BORDER RADIUS ============
static const double radiusSm = 8.0;
static const double radiusMd = 12.0;
static const double radiusLg = 16.0;
static const double radiusXl = 24.0;
static const double radiusFull = 999.0;
// ============ ANIMATION DURATIONS ============
static const Duration durationFast = Duration(milliseconds: 150);
static const Duration durationNormal = Duration(milliseconds: 250);
static const Duration durationSlow = Duration(milliseconds: 400);
// ============ ANIMATION CURVES ============
static const Curve curveDefault = Curves.easeOutCubic;
static const Curve curveEmphasized = Curves.easeInOutCubic;
// ============ DARK THEME ============
static ThemeData get darkTheme {
final baseTheme = ThemeData.dark(useMaterial3: true);
return baseTheme.copyWith(
colorScheme: AppColors.darkColorScheme,
scaffoldBackgroundColor: AppColors.background,
// AppBar
appBarTheme: AppBarTheme(
backgroundColor: AppColors.surface,
surfaceTintColor: Colors.transparent,
elevation: 0,
centerTitle: false,
titleTextStyle: GoogleFonts.inter(
fontSize: 20,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
iconTheme: const IconThemeData(color: AppColors.textPrimary),
),
// Navigation Bar (bottom)
navigationBarTheme: NavigationBarThemeData(
backgroundColor: AppColors.surface,
surfaceTintColor: Colors.transparent,
indicatorColor: AppColors.primary.withOpacity(0.2),
labelTextStyle: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w600,
color: AppColors.primary,
);
}
return GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w500,
color: AppColors.textSecondary,
);
}),
iconTheme: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return const IconThemeData(color: AppColors.primary);
}
return const IconThemeData(color: AppColors.textSecondary);
}),
),
// Navigation Rail (desktop sidebar)
navigationRailTheme: NavigationRailThemeData(
backgroundColor: AppColors.surface,
indicatorColor: AppColors.primary.withOpacity(0.2),
selectedIconTheme: const IconThemeData(color: AppColors.primary),
unselectedIconTheme: const IconThemeData(color: AppColors.textSecondary),
selectedLabelTextStyle: GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w600,
color: AppColors.primary,
),
unselectedLabelTextStyle: GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w500,
color: AppColors.textSecondary,
),
),
// Cards
cardTheme: CardThemeData(
color: AppColors.surface,
surfaceTintColor: Colors.transparent,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusMd),
side: const BorderSide(color: AppColors.border, width: 1),
),
margin: EdgeInsets.zero,
),
// Inputs
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: AppColors.surfaceVariant,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusSm),
borderSide: const BorderSide(color: AppColors.border),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusSm),
borderSide: const BorderSide(color: AppColors.border),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusSm),
borderSide: const BorderSide(color: AppColors.primary, width: 2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusSm),
borderSide: const BorderSide(color: AppColors.error),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: spacing16,
vertical: spacing12,
),
hintStyle: GoogleFonts.inter(
color: AppColors.textDisabled,
fontSize: 14,
),
labelStyle: GoogleFonts.inter(
color: AppColors.textSecondary,
fontSize: 14,
),
),
// Buttons
filledButtonTheme: FilledButtonThemeData(
style: FilledButton.styleFrom(
backgroundColor: AppColors.primary,
foregroundColor: Colors.black,
padding: const EdgeInsets.symmetric(
horizontal: spacing24,
vertical: spacing12,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusSm),
),
textStyle: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: AppColors.primary,
side: const BorderSide(color: AppColors.primary),
padding: const EdgeInsets.symmetric(
horizontal: spacing24,
vertical: spacing12,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusSm),
),
textStyle: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: AppColors.primary,
textStyle: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
// Chips
chipTheme: ChipThemeData(
backgroundColor: AppColors.surfaceVariant,
selectedColor: AppColors.primary.withOpacity(0.2),
labelStyle: GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w500,
color: AppColors.textPrimary,
),
side: const BorderSide(color: AppColors.border),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusFull),
),
padding: const EdgeInsets.symmetric(horizontal: spacing8),
),
// Dialogs
dialogTheme: DialogThemeData(
backgroundColor: AppColors.surface,
surfaceTintColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusLg),
),
titleTextStyle: GoogleFonts.inter(
fontSize: 18,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
contentTextStyle: GoogleFonts.inter(
fontSize: 14,
color: AppColors.textSecondary,
),
),
// Snackbars
snackBarTheme: SnackBarThemeData(
backgroundColor: AppColors.surfaceVariant,
contentTextStyle: GoogleFonts.inter(
fontSize: 14,
color: AppColors.textPrimary,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusSm),
),
behavior: SnackBarBehavior.floating,
),
// Tabs
tabBarTheme: TabBarThemeData(
labelColor: AppColors.primary,
unselectedLabelColor: AppColors.textSecondary,
labelStyle: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w600,
),
unselectedLabelStyle: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w500,
),
indicatorColor: AppColors.primary,
indicatorSize: TabBarIndicatorSize.tab,
dividerColor: AppColors.border,
),
// Dividers
dividerTheme: const DividerThemeData(
color: AppColors.border,
thickness: 1,
space: 1,
),
// List tiles
listTileTheme: ListTileThemeData(
tileColor: Colors.transparent,
contentPadding: const EdgeInsets.symmetric(
horizontal: spacing16,
vertical: spacing8,
),
titleTextStyle: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.textPrimary,
),
subtitleTextStyle: GoogleFonts.inter(
fontSize: 12,
color: AppColors.textSecondary,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusSm),
),
),
// Progress indicators
progressIndicatorTheme: const ProgressIndicatorThemeData(
color: AppColors.primary,
linearTrackColor: AppColors.surfaceVariant,
circularTrackColor: AppColors.surfaceVariant,
),
// Switch
switchTheme: SwitchThemeData(
thumbColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return AppColors.primary;
}
return AppColors.textSecondary;
}),
trackColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return AppColors.primary.withOpacity(0.3);
}
return AppColors.surfaceVariant;
}),
),
// Text theme
textTheme: TextTheme(
displayLarge: GoogleFonts.inter(
fontSize: 57,
fontWeight: FontWeight.w400,
color: AppColors.textPrimary,
),
displayMedium: GoogleFonts.inter(
fontSize: 45,
fontWeight: FontWeight.w400,
color: AppColors.textPrimary,
),
displaySmall: GoogleFonts.inter(
fontSize: 36,
fontWeight: FontWeight.w400,
color: AppColors.textPrimary,
),
headlineLarge: GoogleFonts.inter(
fontSize: 32,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
headlineMedium: GoogleFonts.inter(
fontSize: 28,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
headlineSmall: GoogleFonts.inter(
fontSize: 24,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
titleLarge: GoogleFonts.inter(
fontSize: 22,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
titleMedium: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
titleSmall: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
bodyLarge: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w400,
color: AppColors.textPrimary,
),
bodyMedium: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w400,
color: AppColors.textPrimary,
),
bodySmall: GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w400,
color: AppColors.textSecondary,
),
labelLarge: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
labelMedium: GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w500,
color: AppColors.textPrimary,
),
labelSmall: GoogleFonts.inter(
fontSize: 11,
fontWeight: FontWeight.w500,
color: AppColors.textSecondary,
),
),
);
}
// ============ LIGHT THEME ============
static ThemeData get lightTheme {
final baseTheme = ThemeData.light(useMaterial3: true);
return baseTheme.copyWith(
colorScheme: AppColors.lightColorScheme,
scaffoldBackgroundColor: AppColors.backgroundLight,
// AppBar
appBarTheme: AppBarTheme(
backgroundColor: AppColors.surfaceLight,
surfaceTintColor: Colors.transparent,
elevation: 0,
centerTitle: false,
titleTextStyle: GoogleFonts.inter(
fontSize: 20,
fontWeight: FontWeight.w600,
color: AppColors.textPrimaryLight,
),
iconTheme: const IconThemeData(color: AppColors.textPrimaryLight),
),
// Navigation Bar (bottom)
navigationBarTheme: NavigationBarThemeData(
backgroundColor: AppColors.surfaceLight,
surfaceTintColor: Colors.transparent,
indicatorColor: AppColors.primary.withOpacity(0.15),
labelTextStyle: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w600,
color: AppColors.primary,
);
}
return GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w500,
color: AppColors.textSecondaryLight,
);
}),
iconTheme: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return const IconThemeData(color: AppColors.primary);
}
return const IconThemeData(color: AppColors.textSecondaryLight);
}),
),
// Navigation Rail (desktop sidebar)
navigationRailTheme: NavigationRailThemeData(
backgroundColor: AppColors.surfaceLight,
indicatorColor: AppColors.primary.withOpacity(0.15),
selectedIconTheme: const IconThemeData(color: AppColors.primary),
unselectedIconTheme: const IconThemeData(color: AppColors.textSecondaryLight),
selectedLabelTextStyle: GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w600,
color: AppColors.primary,
),
unselectedLabelTextStyle: GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w500,
color: AppColors.textSecondaryLight,
),
),
// Cards
cardTheme: CardThemeData(
color: AppColors.surfaceLight,
surfaceTintColor: Colors.transparent,
elevation: 2,
shadowColor: Colors.black.withOpacity(0.08),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusMd),
),
margin: EdgeInsets.zero,
),
// Inputs
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: AppColors.surfaceVariantLight,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusSm),
borderSide: const BorderSide(color: AppColors.borderLight),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusSm),
borderSide: const BorderSide(color: AppColors.borderLight),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusSm),
borderSide: const BorderSide(color: AppColors.primary, width: 2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusSm),
borderSide: const BorderSide(color: AppColors.error),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: spacing16,
vertical: spacing12,
),
hintStyle: GoogleFonts.inter(
color: AppColors.textDisabledLight,
fontSize: 14,
),
labelStyle: GoogleFonts.inter(
color: AppColors.textSecondaryLight,
fontSize: 14,
),
),
// Buttons
filledButtonTheme: FilledButtonThemeData(
style: FilledButton.styleFrom(
backgroundColor: AppColors.primary,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(
horizontal: spacing24,
vertical: spacing12,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusSm),
),
textStyle: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
// Dialogs
dialogTheme: DialogThemeData(
backgroundColor: AppColors.surfaceLight,
surfaceTintColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusLg),
),
titleTextStyle: GoogleFonts.inter(
fontSize: 18,
fontWeight: FontWeight.w600,
color: AppColors.textPrimaryLight,
),
contentTextStyle: GoogleFonts.inter(
fontSize: 14,
color: AppColors.textSecondaryLight,
),
),
// Dividers
dividerTheme: const DividerThemeData(
color: AppColors.borderLight,
thickness: 1,
space: 1,
),
// List tiles
listTileTheme: ListTileThemeData(
tileColor: Colors.transparent,
contentPadding: const EdgeInsets.symmetric(
horizontal: spacing16,
vertical: spacing8,
),
titleTextStyle: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.textPrimaryLight,
),
subtitleTextStyle: GoogleFonts.inter(
fontSize: 12,
color: AppColors.textSecondaryLight,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusSm),
),
),
// Progress indicators
progressIndicatorTheme: const ProgressIndicatorThemeData(
color: AppColors.primary,
linearTrackColor: AppColors.surfaceVariantLight,
circularTrackColor: AppColors.surfaceVariantLight,
),
// Text theme
textTheme: TextTheme(
displayLarge: GoogleFonts.inter(
fontSize: 57,
fontWeight: FontWeight.w400,
color: AppColors.textPrimaryLight,
),
headlineLarge: GoogleFonts.inter(
fontSize: 32,
fontWeight: FontWeight.w600,
color: AppColors.textPrimaryLight,
),
titleLarge: GoogleFonts.inter(
fontSize: 22,
fontWeight: FontWeight.w600,
color: AppColors.textPrimaryLight,
),
titleMedium: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.textPrimaryLight,
),
bodyLarge: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w400,
color: AppColors.textPrimaryLight,
),
bodyMedium: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w400,
color: AppColors.textPrimaryLight,
),
bodySmall: GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w400,
color: AppColors.textSecondaryLight,
),
labelLarge: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppColors.textPrimaryLight,
),
),
);
}
}