import 'package:flutter/material.dart';
import '../config/app_config.dart';
class MeinWeimarPalette extends ThemeExtension<MeinWeimarPalette> {
const MeinWeimarPalette({
required this.brand,
required this.secondary,
required this.accent,
required this.ink,
required this.surface,
required this.muted,
required this.brandSurface,
required this.success,
required this.warning,
});
final Color brand;
final Color secondary;
final Color accent;
final Color ink;
final Color surface;
final Color muted;
final Color brandSurface;
final Color success;
final Color warning;
@override
MeinWeimarPalette copyWith({
Color? brand,
Color? secondary,
Color? accent,
Color? ink,
Color? surface,
Color? muted,
Color? brandSurface,
Color? success,
Color? warning,
}) => MeinWeimarPalette(
brand: brand ?? this.brand,
secondary: secondary ?? this.secondary,
accent: accent ?? this.accent,
ink: ink ?? this.ink,
surface: surface ?? this.surface,
muted: muted ?? this.muted,
brandSurface: brandSurface ?? this.brandSurface,
success: success ?? this.success,
warning: warning ?? this.warning,
);
@override
MeinWeimarPalette lerp(ThemeExtension<MeinWeimarPalette>? other, double t) {
if (other is! MeinWeimarPalette) return this;
return MeinWeimarPalette(
brand: Color.lerp(brand, other.brand, t)!,
secondary: Color.lerp(secondary, other.secondary, t)!,
accent: Color.lerp(accent, other.accent, t)!,
ink: Color.lerp(ink, other.ink, t)!,
surface: Color.lerp(surface, other.surface, t)!,
muted: Color.lerp(muted, other.muted, t)!,
brandSurface: Color.lerp(brandSurface, other.brandSurface, t)!,
success: Color.lerp(success, other.success, t)!,
warning: Color.lerp(warning, other.warning, t)!,
);
}
}
class AppTheme {
static const brand = Color(0xFFFF1E6E);
static const brandSurface = Color(0xFFFFE5EE);
static const ink = Color(0xFF1F2735);
static const surface = Color(0xFFFFFFFF);
static const muted = Color(0xFF64748B);
static const success = Color(0xFF159570);
static const warning = Color(0xFFB7791F);
/// Shared soft elevation used by white cards on the white app surface.
/// Keeping this in the theme prevents individual screens from inventing
/// different shadow strengths.
static const cardShadow = <BoxShadow>[
BoxShadow(color: Color(0x40000000), blurRadius: 18, offset: Offset(0, 6)),
];
static MeinWeimarPalette palette(BuildContext context) {
return Theme.of(context).extension<MeinWeimarPalette>() ??
const MeinWeimarPalette(
brand: brand,
secondary: Color(0xFF20242E),
accent: brand,
ink: ink,
surface: surface,
muted: muted,
brandSurface: brandSurface,
success: success,
warning: warning,
);
}
static String cssColor(Color color) {
final rgb = color.toARGB32() & 0x00FFFFFF;
return '#${rgb.toRadixString(16).padLeft(6, '0').toUpperCase()}';
}
static ThemeData light({AppColorConfig? colors, Color? primary}) {
final config = colors ?? AppColorConfig.fallback();
final primaryColor = primary ?? Color(config.primary);
final palette = MeinWeimarPalette(
brand: primaryColor,
secondary: Color(config.secondary),
accent: Color(config.accent),
ink: Color(config.ink),
surface: Color(config.surface),
muted: Color(config.muted),
brandSurface: Color(config.brandSurface),
success: Color(config.success),
warning: Color(config.warning),
);
final scheme = ColorScheme.fromSeed(
seedColor: primaryColor,
brightness: Brightness.light,
surface: palette.surface,
);
return ThemeData(
colorScheme: scheme.copyWith(
primary: primaryColor,
onPrimary: Colors.white,
primaryContainer: palette.brandSurface,
onPrimaryContainer: palette.ink,
secondary: palette.secondary,
onSecondary: Colors.white,
secondaryContainer: palette.brandSurface,
onSecondaryContainer: palette.ink,
tertiary: palette.accent,
onTertiary: Colors.white,
tertiaryContainer: palette.brandSurface,
onTertiaryContainer: palette.ink,
surface: palette.surface,
onSurface: palette.ink,
surfaceContainerLowest: Colors.white,
surfaceContainerLow: palette.surface,
surfaceContainer: palette.surface,
surfaceContainerHigh: palette.surface,
surfaceContainerHighest: palette.brandSurface,
outline: palette.muted,
outlineVariant: palette.muted.withValues(alpha: .35),
error: palette.warning,
onError: Colors.white,
errorContainer: palette.brandSurface,
onErrorContainer: palette.ink,
inverseSurface: palette.ink,
onInverseSurface: Colors.white,
inversePrimary: palette.accent,
),
scaffoldBackgroundColor: palette.surface,
useMaterial3: true,
fontFamily: 'Inter',
appBarTheme: AppBarTheme(
backgroundColor: Colors.white,
foregroundColor: palette.ink,
elevation: 0,
centerTitle: false,
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: BorderSide.none,
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 14,
),
),
cardTheme: CardThemeData(
color: Colors.white,
elevation: 6,
shadowColor: const Color(0x40000000),
margin: EdgeInsets.zero,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)),
),
extensions: <ThemeExtension<dynamic>>[palette],
);
}
}
