// Author: fruit.com.cn import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'app_colors.dart'; import 'app_dimens.dart'; /// Obsidian Flare themes. Dark is the product default; light mirrors the same /// orange primary for system-light devices. abstract final class AppTheme { static const _lightBackground = Color(0xFFF7F7F8); static const _darkBackground = AppColors.ink; /// Light theme for system light mode. static ThemeData light() => _build( brightness: Brightness.light, surface: _lightBackground, accent: AppColors.blue, ); /// Dark theme for system dark mode. static ThemeData dark() => _build( brightness: Brightness.dark, surface: _darkBackground, accent: AppColors.blue, ); static ThemeData _build({ required Brightness brightness, required Color surface, required Color accent, }) { final dark = brightness == Brightness.dark; final scheme = dark ? const ColorScheme.dark( primary: AppColors.blue, onPrimary: AppColors.onPrimary, secondary: AppColors.secondary, onSecondary: AppColors.onPrimary, primaryContainer: AppColors.bluePressed, onPrimaryContainer: AppColors.onPrimary, secondaryContainer: AppColors.raised, onSecondaryContainer: AppColors.onPrimary, surface: _darkBackground, onSurface: AppColors.onSurface, outline: AppColors.outlineDark, outlineVariant: AppColors.outlineVariantDark, surfaceTint: AppColors.blue, inversePrimary: AppColors.blueSoft, ) : ColorScheme.light( primary: AppColors.blue, onPrimary: AppColors.onPrimary, secondary: AppColors.secondary, onSecondary: AppColors.onPrimary, primaryContainer: AppColors.blue.withValues(alpha: 0.14), onPrimaryContainer: AppColors.ink, secondaryContainer: AppColors.blueSoft.withValues(alpha: 0.14), onSecondaryContainer: AppColors.ink, surface: _lightBackground, onSurface: AppColors.ink, outline: AppColors.outlineLight, outlineVariant: AppColors.outlineVariantLight, surfaceTint: AppColors.blue, inversePrimary: AppColors.blueSoft, ); final base = ThemeData( useMaterial3: true, brightness: brightness, colorScheme: scheme, scaffoldBackgroundColor: surface, fontFamily: 'PlusJakartaSans', progressIndicatorTheme: ProgressIndicatorThemeData(color: accent), sliderTheme: SliderThemeData( activeTrackColor: accent, thumbColor: AppColors.onPrimary, ), tabBarTheme: TabBarThemeData(labelColor: accent, indicatorColor: accent), textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom(foregroundColor: accent), ), filledButtonTheme: FilledButtonThemeData( style: ButtonStyle( backgroundColor: WidgetStateProperty.resolveWith(_buttonColor), foregroundColor: const WidgetStatePropertyAll(AppColors.onPrimary), minimumSize: const WidgetStatePropertyAll( Size(0, AppDimens.buttonHeight), ), ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: ButtonStyle( foregroundColor: WidgetStatePropertyAll( dark ? AppColors.blueSoft : AppColors.bluePressed, ), minimumSize: const WidgetStatePropertyAll( Size(0, AppDimens.buttonHeight), ), side: WidgetStatePropertyAll( BorderSide( color: dark ? AppColors.outlineDark : AppColors.outlineLight, ), ), ), ), appBarTheme: const AppBarTheme( systemOverlayStyle: SystemUiOverlayStyle.light, titleTextStyle: TextStyle( color: AppColors.onSurface, fontFamily: 'Outfit', fontSize: AppDimens.pageTitle, fontWeight: FontWeight.w800, ), ), switchTheme: SwitchThemeData( thumbColor: WidgetStateProperty.resolveWith( (states) => states.contains(WidgetState.selected) ? AppColors.onPrimary : null, ), trackColor: WidgetStateProperty.resolveWith( (states) => states.contains(WidgetState.selected) ? accent : null, ), ), snackBarTheme: SnackBarThemeData( backgroundColor: AppColors.bluePressed, contentTextStyle: const TextStyle(color: AppColors.onPrimary), ), ); final textTheme = base.textTheme .apply( fontFamily: 'PlusJakartaSans', bodyColor: dark ? AppColors.cream : AppColors.ink, displayColor: dark ? AppColors.onSurface : AppColors.ink, ) .copyWith( displayLarge: _display(base.textTheme.displayLarge, dark), displayMedium: _display(base.textTheme.displayMedium, dark), displaySmall: _display(base.textTheme.displaySmall, dark), headlineLarge: _display(base.textTheme.headlineLarge, dark), headlineMedium: _display(base.textTheme.headlineMedium, dark), headlineSmall: _display(base.textTheme.headlineSmall, dark), titleLarge: _display(base.textTheme.titleLarge, dark), ); return base.copyWith(textTheme: textTheme); } static TextStyle? _display(TextStyle? style, bool dark) => style?.copyWith( fontFamily: 'Outfit', color: dark ? AppColors.onSurface : AppColors.ink, ); static Color _buttonColor(Set states) { if (states.contains(WidgetState.pressed) || states.contains(WidgetState.hovered) || states.contains(WidgetState.focused)) { return AppColors.bluePressed; } return AppColors.blue; } }