272 lines
10 KiB
Dart
272 lines
10 KiB
Dart
import 'package:aves/widgets/aves_app.dart';
|
|
import 'package:aves_utils/aves_utils.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
class Themes {
|
|
static const _titleTextStyle = TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.normal,
|
|
fontFeatures: [FontFeature.enable('smcp')],
|
|
);
|
|
|
|
static String asButtonLabel(String s) => s.toUpperCase();
|
|
|
|
static TextStyle searchFieldStyle(BuildContext context) => Theme.of(context).textTheme.bodyLarge!;
|
|
|
|
static Color overlayBackgroundColor({
|
|
required Brightness brightness,
|
|
required bool blurred,
|
|
}) {
|
|
switch (brightness) {
|
|
case Brightness.dark:
|
|
return blurred ? Colors.black26 : Colors.black45;
|
|
case Brightness.light:
|
|
return blurred ? Colors.white54 : const Color(0xCCFFFFFF);
|
|
}
|
|
}
|
|
|
|
static bool _isDarkTheme(ColorScheme colors) => colors.brightness == Brightness.dark && colors.surface != Colors.black;
|
|
|
|
static Color firstLayerColor(BuildContext context) => _schemeFirstLayer(Theme.of(context).colorScheme);
|
|
|
|
static Color _schemeFirstLayer(ColorScheme colors) => _isDarkTheme(colors) ? colors.surfaceContainer : colors.surface;
|
|
|
|
static Color _schemeCardLayer(ColorScheme colors) => _isDarkTheme(colors) ? _schemeSecondLayer(colors) : colors.surfaceContainerLow;
|
|
|
|
static Color secondLayerColor(BuildContext context) => _schemeSecondLayer(Theme.of(context).colorScheme);
|
|
|
|
static Color _schemeSecondLayer(ColorScheme colors) => _isDarkTheme(colors) ? colors.surfaceContainerHigh : colors.surfaceContainer;
|
|
|
|
static Color thirdLayerColor(BuildContext context) => _schemeThirdLayer(Theme.of(context).colorScheme);
|
|
|
|
static Color _schemeThirdLayer(ColorScheme colors) => _isDarkTheme(colors) ? colors.surfaceContainerHighest : colors.surfaceContainerHigh;
|
|
|
|
static Color _unselectedWidgetColor(ColorScheme colors) => colors.onSurface.withOpacity(0.6);
|
|
|
|
static Color backgroundTextColor(BuildContext context) {
|
|
final colors = Theme.of(context).colorScheme;
|
|
return Color.alphaBlend(colors.surfaceTint, colors.onSurface).withOpacity(.5);
|
|
}
|
|
|
|
static final _typography = Typography.material2021(platform: TargetPlatform.android);
|
|
|
|
static ThemeData _baseTheme(ColorScheme colors, bool deviceInitialized) {
|
|
return ThemeData(
|
|
// COLOR
|
|
brightness: colors.brightness,
|
|
canvasColor: _schemeSecondLayer(colors),
|
|
cardColor: _schemeCardLayer(colors),
|
|
colorScheme: colors,
|
|
dividerColor: colors.outlineVariant,
|
|
indicatorColor: colors.primary,
|
|
scaffoldBackgroundColor: _schemeFirstLayer(colors),
|
|
// TYPOGRAPHY & ICONOGRAPHY
|
|
typography: _typography,
|
|
// COMPONENT THEMES
|
|
checkboxTheme: _checkboxTheme(colors),
|
|
drawerTheme: _drawerTheme(colors),
|
|
floatingActionButtonTheme: _floatingActionButtonTheme(colors),
|
|
navigationRailTheme: NavigationRailThemeData(
|
|
backgroundColor: _schemeFirstLayer(colors),
|
|
selectedIconTheme: IconThemeData(color: colors.primary),
|
|
unselectedIconTheme: IconThemeData(color: _unselectedWidgetColor(colors)),
|
|
selectedLabelTextStyle: TextStyle(color: colors.primary),
|
|
unselectedLabelTextStyle: TextStyle(color: _unselectedWidgetColor(colors)),
|
|
),
|
|
radioTheme: _radioTheme(colors),
|
|
sliderTheme: _sliderTheme(colors),
|
|
tooltipTheme: _tooltipTheme,
|
|
);
|
|
}
|
|
|
|
static CheckboxThemeData _checkboxTheme(ColorScheme colors) => CheckboxThemeData(
|
|
side: BorderSide(width: 2.0, color: _unselectedWidgetColor(colors)),
|
|
);
|
|
|
|
static DrawerThemeData _drawerTheme(ColorScheme colors) => DrawerThemeData(
|
|
backgroundColor: _schemeSecondLayer(colors),
|
|
);
|
|
|
|
static const _listTileTheme = ListTileThemeData(
|
|
contentPadding: EdgeInsets.symmetric(horizontal: 16),
|
|
);
|
|
|
|
static PopupMenuThemeData _popupMenuTheme(ColorScheme colors, TextTheme textTheme) {
|
|
return PopupMenuThemeData(
|
|
color: _schemeSecondLayer(colors),
|
|
labelTextStyle: WidgetStateProperty.resolveWith((states) {
|
|
// adapted from M3 defaults
|
|
final TextStyle style = textTheme.labelLarge!;
|
|
if (states.contains(WidgetState.disabled)) {
|
|
return style.apply(color: colors.onSurface.withOpacity(0.38));
|
|
}
|
|
return style.apply(color: colors.onSurface);
|
|
}),
|
|
iconColor: colors.onSurface,
|
|
);
|
|
}
|
|
|
|
static FloatingActionButtonThemeData _floatingActionButtonTheme(ColorScheme colors) {
|
|
return FloatingActionButtonThemeData(
|
|
foregroundColor: colors.onPrimary,
|
|
backgroundColor: colors.primary,
|
|
);
|
|
}
|
|
|
|
// adapted from M3 defaults
|
|
static RadioThemeData _radioTheme(ColorScheme colors) => RadioThemeData(
|
|
fillColor: WidgetStateProperty.resolveWith<Color>((states) {
|
|
if (states.contains(WidgetState.selected)) {
|
|
if (states.contains(WidgetState.disabled)) {
|
|
return colors.onSurface.withOpacity(0.38);
|
|
}
|
|
return colors.primary;
|
|
}
|
|
if (states.contains(WidgetState.disabled)) {
|
|
return colors.onSurface.withOpacity(0.38);
|
|
}
|
|
if (states.contains(WidgetState.pressed)) {
|
|
return colors.onSurface;
|
|
}
|
|
if (states.contains(WidgetState.hovered)) {
|
|
return colors.onSurface;
|
|
}
|
|
if (states.contains(WidgetState.focused)) {
|
|
return colors.onSurface;
|
|
}
|
|
return _unselectedWidgetColor(colors);
|
|
}),
|
|
);
|
|
|
|
static SliderThemeData _sliderTheme(ColorScheme colors) => SliderThemeData(
|
|
inactiveTrackColor: colors.primary.withOpacity(0.24),
|
|
);
|
|
|
|
static SnackBarThemeData _snackBarTheme(ColorScheme colors) => SnackBarThemeData(
|
|
actionTextColor: colors.primary,
|
|
behavior: SnackBarBehavior.floating,
|
|
);
|
|
|
|
static const _tooltipTheme = TooltipThemeData(
|
|
verticalOffset: 32,
|
|
);
|
|
|
|
// light
|
|
|
|
static final _lightThemeTypo = _typography.black;
|
|
static final _lightTitleColor = _lightThemeTypo.titleMedium!.color!;
|
|
static final _lightLabelColor = _lightThemeTypo.labelMedium!.color!;
|
|
static const _lightActionIconColor = Color(0xAA000000);
|
|
static const _lightOnSurface = Colors.black;
|
|
|
|
static ThemeData lightTheme(Color accentColor, bool deviceInitialized) {
|
|
final onAccent = ColorUtils.textColorOn(accentColor);
|
|
final colors = ColorScheme.fromSeed(
|
|
seedColor: accentColor,
|
|
brightness: Brightness.light,
|
|
primary: accentColor,
|
|
onPrimary: onAccent,
|
|
secondary: accentColor,
|
|
onSecondary: onAccent,
|
|
onSurface: _lightOnSurface,
|
|
);
|
|
final textTheme = _lightThemeTypo;
|
|
return _baseTheme(colors, deviceInitialized).copyWith(
|
|
// TYPOGRAPHY & ICONOGRAPHY
|
|
textTheme: textTheme,
|
|
// COMPONENT THEMES
|
|
appBarTheme: AppBarTheme(
|
|
backgroundColor: _schemeFirstLayer(colors),
|
|
// `foregroundColor` is used by icons
|
|
foregroundColor: _lightActionIconColor,
|
|
// `titleTextStyle.color` is used by text
|
|
titleTextStyle: _titleTextStyle.copyWith(color: _lightTitleColor),
|
|
systemOverlayStyle: deviceInitialized ? AvesApp.systemUIStyleForBrightness(colors.brightness, _schemeFirstLayer(colors)) : null,
|
|
),
|
|
dialogTheme: DialogTheme(
|
|
backgroundColor: _schemeSecondLayer(colors),
|
|
titleTextStyle: _titleTextStyle.copyWith(color: _lightTitleColor),
|
|
),
|
|
listTileTheme: _listTileTheme.copyWith(
|
|
iconColor: _lightActionIconColor,
|
|
),
|
|
popupMenuTheme: _popupMenuTheme(colors, textTheme),
|
|
snackBarTheme: _snackBarTheme(colors),
|
|
textButtonTheme: TextButtonThemeData(
|
|
style: TextButton.styleFrom(
|
|
foregroundColor: _lightLabelColor,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// dark
|
|
|
|
static final _darkThemeTypo = _typography.white;
|
|
static final _darkTitleColor = _darkThemeTypo.titleMedium!.color!;
|
|
static final _darkBodyColor = _darkThemeTypo.bodyMedium!.color!;
|
|
static final _darkLabelColor = _darkThemeTypo.labelMedium!.color!;
|
|
static const _darkOnSurface = Colors.white;
|
|
|
|
static ColorScheme _darkColorScheme(Color accentColor) {
|
|
final onAccent = ColorUtils.textColorOn(accentColor);
|
|
final colors = ColorScheme.fromSeed(
|
|
seedColor: accentColor,
|
|
brightness: Brightness.dark,
|
|
primary: accentColor,
|
|
onPrimary: onAccent,
|
|
secondary: accentColor,
|
|
onSecondary: onAccent,
|
|
onSurface: _darkOnSurface,
|
|
);
|
|
return colors;
|
|
}
|
|
|
|
static ThemeData _baseDarkTheme(ColorScheme colors, bool deviceInitialized) {
|
|
final textTheme = _darkThemeTypo;
|
|
return _baseTheme(colors, deviceInitialized).copyWith(
|
|
// TYPOGRAPHY & ICONOGRAPHY
|
|
textTheme: textTheme,
|
|
// COMPONENT THEMES
|
|
appBarTheme: AppBarTheme(
|
|
backgroundColor: _schemeFirstLayer(colors),
|
|
// `foregroundColor` is used by icons
|
|
foregroundColor: _darkTitleColor,
|
|
// `titleTextStyle.color` is used by text
|
|
titleTextStyle: _titleTextStyle.copyWith(color: _darkTitleColor),
|
|
systemOverlayStyle: deviceInitialized ? AvesApp.systemUIStyleForBrightness(colors.brightness, _schemeFirstLayer(colors)) : null,
|
|
),
|
|
dialogTheme: DialogTheme(
|
|
backgroundColor: _schemeSecondLayer(colors),
|
|
titleTextStyle: _titleTextStyle.copyWith(color: _darkTitleColor),
|
|
),
|
|
listTileTheme: _listTileTheme,
|
|
popupMenuTheme: _popupMenuTheme(colors, textTheme),
|
|
snackBarTheme: _snackBarTheme(colors).copyWith(
|
|
backgroundColor: _schemeSecondLayer(colors),
|
|
contentTextStyle: TextStyle(
|
|
color: _darkBodyColor,
|
|
),
|
|
),
|
|
textButtonTheme: TextButtonThemeData(
|
|
style: TextButton.styleFrom(
|
|
foregroundColor: _darkLabelColor,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
static ThemeData darkTheme(Color accentColor, bool deviceInitialized) {
|
|
final colors = _darkColorScheme(accentColor);
|
|
return _baseDarkTheme(colors, deviceInitialized);
|
|
}
|
|
|
|
// black
|
|
|
|
static ThemeData blackTheme(Color accentColor, bool deviceInitialized) {
|
|
final colors = _darkColorScheme(accentColor).copyWith(
|
|
surface: Colors.black,
|
|
);
|
|
return _baseDarkTheme(colors, deviceInitialized);
|
|
}
|
|
}
|