163 lines
5.8 KiB
Dart
163 lines
5.8 KiB
Dart
// 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<WidgetState> states) {
|
|
if (states.contains(WidgetState.pressed) ||
|
|
states.contains(WidgetState.hovered) ||
|
|
states.contains(WidgetState.focused)) {
|
|
return AppColors.bluePressed;
|
|
}
|
|
return AppColors.blue;
|
|
}
|
|
}
|