import 'package:flex_color_scheme/flex_color_scheme.dart'; import 'package:flutter/material.dart'; @immutable class MapflowThemeTokens extends ThemeExtension { const MapflowThemeTokens({ required this.mapPanel, required this.mapPanelBorder, required this.mapShadow, required this.danger, required this.onDark, required this.darkSurface, required this.darkSurfaceAlt, required this.voiceAccent, required this.voiceAccentSoft, required this.onVoiceControl, required this.snowflakeLine, required this.snowflakeNode, required this.snowflakeCenter, required this.snowflakeLabel, required this.imageScrim, required this.panelRadius, }); final Color mapPanel; final Color mapPanelBorder; final Color mapShadow; final Color danger; final Color onDark; final Color darkSurface; final Color darkSurfaceAlt; final Color voiceAccent; final Color voiceAccentSoft; final Color onVoiceControl; final Color snowflakeLine; final Color snowflakeNode; final Color snowflakeCenter; final Color snowflakeLabel; final Color imageScrim; final double panelRadius; @override MapflowThemeTokens copyWith({ Color? mapPanel, Color? mapPanelBorder, Color? mapShadow, Color? danger, Color? onDark, Color? darkSurface, Color? darkSurfaceAlt, Color? voiceAccent, Color? voiceAccentSoft, Color? onVoiceControl, Color? snowflakeLine, Color? snowflakeNode, Color? snowflakeCenter, Color? snowflakeLabel, Color? imageScrim, double? panelRadius, }) { return MapflowThemeTokens( mapPanel: mapPanel ?? this.mapPanel, mapPanelBorder: mapPanelBorder ?? this.mapPanelBorder, mapShadow: mapShadow ?? this.mapShadow, danger: danger ?? this.danger, onDark: onDark ?? this.onDark, darkSurface: darkSurface ?? this.darkSurface, darkSurfaceAlt: darkSurfaceAlt ?? this.darkSurfaceAlt, voiceAccent: voiceAccent ?? this.voiceAccent, voiceAccentSoft: voiceAccentSoft ?? this.voiceAccentSoft, onVoiceControl: onVoiceControl ?? this.onVoiceControl, snowflakeLine: snowflakeLine ?? this.snowflakeLine, snowflakeNode: snowflakeNode ?? this.snowflakeNode, snowflakeCenter: snowflakeCenter ?? this.snowflakeCenter, snowflakeLabel: snowflakeLabel ?? this.snowflakeLabel, imageScrim: imageScrim ?? this.imageScrim, panelRadius: panelRadius ?? this.panelRadius, ); } @override MapflowThemeTokens lerp( covariant ThemeExtension? other, double t, ) { if (other is! MapflowThemeTokens) { return this; } return MapflowThemeTokens( mapPanel: Color.lerp(mapPanel, other.mapPanel, t)!, mapPanelBorder: Color.lerp(mapPanelBorder, other.mapPanelBorder, t)!, mapShadow: Color.lerp(mapShadow, other.mapShadow, t)!, danger: Color.lerp(danger, other.danger, t)!, onDark: Color.lerp(onDark, other.onDark, t)!, darkSurface: Color.lerp(darkSurface, other.darkSurface, t)!, darkSurfaceAlt: Color.lerp(darkSurfaceAlt, other.darkSurfaceAlt, t)!, voiceAccent: Color.lerp(voiceAccent, other.voiceAccent, t)!, voiceAccentSoft: Color.lerp(voiceAccentSoft, other.voiceAccentSoft, t)!, onVoiceControl: Color.lerp(onVoiceControl, other.onVoiceControl, t)!, snowflakeLine: Color.lerp(snowflakeLine, other.snowflakeLine, t)!, snowflakeNode: Color.lerp(snowflakeNode, other.snowflakeNode, t)!, snowflakeCenter: Color.lerp(snowflakeCenter, other.snowflakeCenter, t)!, snowflakeLabel: Color.lerp(snowflakeLabel, other.snowflakeLabel, t)!, imageScrim: Color.lerp(imageScrim, other.imageScrim, t)!, panelRadius: lerpDouble(panelRadius, other.panelRadius, t), ); } } class MapflowTheme { const MapflowTheme._(); static const tokens = MapflowThemeTokens( mapPanel: Color(0xFFFFFBF5), mapPanelBorder: Color(0xFFE0D8CA), mapShadow: Color(0x33000000), danger: Color(0xFFE11D48), onDark: Colors.white, darkSurface: Color(0xFF05030B), darkSurfaceAlt: Color(0xFF15111D), voiceAccent: Color(0xFFFF2D75), voiceAccentSoft: Color(0xFFFF7A90), onVoiceControl: Color(0xFF090613), snowflakeLine: Color(0xFFE6DDD2), snowflakeNode: Color(0xFFDED3C7), snowflakeCenter: Color(0xFF241B18), snowflakeLabel: Color(0xFF746A60), imageScrim: Color(0xCC000000), panelRadius: 8, ); static ThemeData light() { return FlexThemeData.light( useMaterial3: true, colors: const FlexSchemeColor( primary: Color(0xFF0F766E), primaryContainer: Color(0xFFBFE7DF), secondary: Color(0xFFE11D48), secondaryContainer: Color(0xFFFFD9E1), tertiary: Color(0xFF7C3AED), tertiaryContainer: Color(0xFFE9DDFF), appBarColor: Color(0xFFF7F3EA), error: Color(0xFFB3261E), ), scaffoldBackground: const Color(0xFFF7F3EA), surface: tokens.mapPanel, subThemesData: const FlexSubThemesData( defaultRadius: 8, inputDecoratorRadius: 8, cardRadius: 8, chipRadius: 8, filledButtonRadius: 8, ), fontFamily: 'SF Pro Display', extensions: const [tokens], ).copyWith( appBarTheme: const AppBarTheme( backgroundColor: Color(0xFFF7F3EA), foregroundColor: Color(0xFF17211D), surfaceTintColor: Colors.transparent, ), cardTheme: CardThemeData( elevation: 0, color: tokens.mapPanel, surfaceTintColor: Colors.transparent, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(tokens.panelRadius), side: BorderSide(color: tokens.mapPanelBorder), ), ), chipTheme: ChipThemeData( shape: StadiumBorder(side: BorderSide(color: tokens.mapPanelBorder)), ), ); } } double lerpDouble(double a, double b, double t) => a + (b - a) * t; extension MapflowThemeContext on BuildContext { MapflowThemeTokens get mapflowTokens { return Theme.of(this).extension()!; } }