Files
SpotiFLAC-Mobile/lib/theme/material_expressive.dart
T

133 lines
5.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:material_3_expressive/material_3_expressive.dart';
import 'package:material_ui/material_ui.dart' as material_ui;
import 'package:spotiflac_android/theme/mornye_theme.dart';
/// Motion-heavy expressive controls use their static Material counterpart when
/// the OS requests reduced motion. Mornye owns its own component system.
bool materialExpressiveEnabled(BuildContext context) =>
!context.isMornye && !MediaQuery.disableAnimationsOf(context);
/// Bridges the app's Flutter theme to the separate Material UI widget library.
/// Local artwork colors, dynamic colors, fonts and AMOLED surfaces stay intact.
class MaterialExpressiveScope extends StatelessWidget {
const MaterialExpressiveScope({super.key, required this.child});
final Widget child;
static final _themes = Expando<material_ui.ThemeData>();
static final _expressiveThemes = Expando<M3EThemeData>();
static material_ui.ThemeData _adapt(ThemeData theme) {
final cached = _themes[theme];
if (cached != null) return cached;
final c = theme.colorScheme;
final t = theme.textTheme;
final adapted = material_ui.ThemeData(
brightness: theme.brightness,
platform: theme.platform,
useMaterial3: true,
scaffoldBackgroundColor: theme.scaffoldBackgroundColor,
colorScheme: material_ui.ColorScheme(
brightness: c.brightness,
primary: c.primary,
onPrimary: c.onPrimary,
primaryContainer: c.primaryContainer,
onPrimaryContainer: c.onPrimaryContainer,
primaryFixed: c.primaryFixed,
primaryFixedDim: c.primaryFixedDim,
onPrimaryFixed: c.onPrimaryFixed,
onPrimaryFixedVariant: c.onPrimaryFixedVariant,
secondary: c.secondary,
onSecondary: c.onSecondary,
secondaryContainer: c.secondaryContainer,
onSecondaryContainer: c.onSecondaryContainer,
secondaryFixed: c.secondaryFixed,
secondaryFixedDim: c.secondaryFixedDim,
onSecondaryFixed: c.onSecondaryFixed,
onSecondaryFixedVariant: c.onSecondaryFixedVariant,
tertiary: c.tertiary,
onTertiary: c.onTertiary,
tertiaryContainer: c.tertiaryContainer,
onTertiaryContainer: c.onTertiaryContainer,
tertiaryFixed: c.tertiaryFixed,
tertiaryFixedDim: c.tertiaryFixedDim,
onTertiaryFixed: c.onTertiaryFixed,
onTertiaryFixedVariant: c.onTertiaryFixedVariant,
error: c.error,
onError: c.onError,
errorContainer: c.errorContainer,
onErrorContainer: c.onErrorContainer,
surface: c.surface,
onSurface: c.onSurface,
onSurfaceVariant: c.onSurfaceVariant,
surfaceDim: c.surfaceDim,
surfaceBright: c.surfaceBright,
surfaceContainerLowest: c.surfaceContainerLowest,
surfaceContainerLow: c.surfaceContainerLow,
surfaceContainer: c.surfaceContainer,
surfaceContainerHigh: c.surfaceContainerHigh,
surfaceContainerHighest: c.surfaceContainerHighest,
outline: c.outline,
outlineVariant: c.outlineVariant,
inverseSurface: c.inverseSurface,
onInverseSurface: c.onInverseSurface,
inversePrimary: c.inversePrimary,
shadow: c.shadow,
scrim: c.scrim,
surfaceTint: c.surfaceTint,
),
textTheme: material_ui.TextTheme(
displayLarge: t.displayLarge,
displayMedium: t.displayMedium,
displaySmall: t.displaySmall,
headlineLarge: t.headlineLarge,
headlineMedium: t.headlineMedium,
headlineSmall: t.headlineSmall,
titleLarge: t.titleLarge,
titleMedium: t.titleMedium,
titleSmall: t.titleSmall,
bodyLarge: t.bodyLarge,
bodyMedium: t.bodyMedium,
bodySmall: t.bodySmall,
labelLarge: t.labelLarge,
labelMedium: t.labelMedium,
labelSmall: t.labelSmall,
),
);
_themes[theme] = adapted;
return adapted;
}
@override
Widget build(BuildContext context) {
final theme = _adapt(Theme.of(context));
final expressive = _expressiveThemes[theme] ??=
M3EThemeData.fromMaterial(theme).copyWith(
fontFamily: theme.textTheme.bodyMedium?.fontFamily,
navigationBarTheme: const M3ENavigationBarTheme(heightMedium: 72),
);
final content = M3ETheme(
data: expressive,
// M3E's projection omits fixed color roles. Keep the complete host theme
// nearest the widgets so it also retains AMOLED and platform typography.
child: material_ui.Theme(data: theme, child: child),
);
if (Localizations.of<material_ui.MaterialLocalizations>(
context,
material_ui.MaterialLocalizations,
) !=
null) {
return content;
}
// Also support isolated sheets/previews hosted by a Flutter MaterialApp.
return Localizations.override(
context: context,
delegates: const [material_ui.GlobalMaterialLocalizations.delegate],
child: Directionality(
textDirection: Directionality.of(context),
child: content,
),
);
}
}