2023-01-22 19:30:29 +00:00
|
|
|
import 'package:dynamic_color/dynamic_color.dart';
|
2022-10-24 20:25:38 +00:00
|
|
|
import 'package:flutter/material.dart';
|
2022-12-16 20:07:05 +00:00
|
|
|
import 'package:flutter/scheduler.dart';
|
2023-01-22 19:30:29 +00:00
|
|
|
import 'package:lightmeter/data/models/dynamic_colors_state.dart';
|
2022-12-16 20:07:05 +00:00
|
|
|
import 'package:lightmeter/data/models/theme_type.dart';
|
|
|
|
import 'package:lightmeter/data/shared_prefs_service.dart';
|
2023-01-31 21:24:26 +00:00
|
|
|
import 'package:lightmeter/res/dimens.dart';
|
2023-06-04 11:04:04 +00:00
|
|
|
import 'package:lightmeter/utils/inherited_generics.dart';
|
2022-10-24 20:25:38 +00:00
|
|
|
import 'package:material_color_utilities/material_color_utilities.dart';
|
|
|
|
|
2022-12-16 20:07:05 +00:00
|
|
|
class ThemeProvider extends StatefulWidget {
|
2023-06-04 11:04:04 +00:00
|
|
|
final Widget child;
|
2022-12-16 20:07:05 +00:00
|
|
|
|
|
|
|
const ThemeProvider({
|
2023-06-04 11:04:04 +00:00
|
|
|
required this.child,
|
2022-12-16 20:07:05 +00:00
|
|
|
super.key,
|
|
|
|
});
|
|
|
|
|
|
|
|
static ThemeProviderState of(BuildContext context) {
|
|
|
|
return context.findAncestorStateOfType<ThemeProviderState>()!;
|
|
|
|
}
|
|
|
|
|
2023-01-31 21:24:26 +00:00
|
|
|
static const primaryColorsList = [
|
|
|
|
Color(0xfff44336),
|
|
|
|
Color(0xffe91e63),
|
|
|
|
Color(0xff9c27b0),
|
|
|
|
Color(0xff673ab7),
|
|
|
|
Color(0xff3f51b5),
|
|
|
|
Color(0xff2196f3),
|
|
|
|
Color(0xff03a9f4),
|
|
|
|
Color(0xff00bcd4),
|
|
|
|
Color(0xff009688),
|
|
|
|
Color(0xff4caf50),
|
|
|
|
Color(0xff8bc34a),
|
|
|
|
Color(0xffcddc39),
|
|
|
|
Color(0xffffeb3b),
|
|
|
|
Color(0xffffc107),
|
|
|
|
Color(0xffff9800),
|
|
|
|
Color(0xffff5722),
|
|
|
|
];
|
|
|
|
|
2022-12-16 20:07:05 +00:00
|
|
|
@override
|
|
|
|
State<ThemeProvider> createState() => ThemeProviderState();
|
|
|
|
}
|
|
|
|
|
2023-02-04 21:09:19 +00:00
|
|
|
class ThemeProviderState extends State<ThemeProvider> with WidgetsBindingObserver {
|
2023-06-04 11:04:04 +00:00
|
|
|
UserPreferencesService get _prefs => context.get<UserPreferencesService>();
|
2023-01-26 15:33:33 +00:00
|
|
|
|
2023-01-26 15:03:48 +00:00
|
|
|
late final _themeTypeNotifier = ValueNotifier<ThemeType>(_prefs.themeType);
|
|
|
|
late final _dynamicColorNotifier = ValueNotifier<bool>(_prefs.dynamicColor);
|
2023-01-31 21:24:26 +00:00
|
|
|
late final _primaryColorNotifier = ValueNotifier<Color>(_prefs.primaryColor);
|
2022-12-16 20:07:05 +00:00
|
|
|
|
2023-02-04 21:09:19 +00:00
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
WidgetsBinding.instance.addObserver(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void didChangePlatformBrightness() {
|
|
|
|
super.didChangePlatformBrightness();
|
|
|
|
setState(() {});
|
|
|
|
}
|
|
|
|
|
2022-12-16 20:07:05 +00:00
|
|
|
@override
|
2023-01-22 19:30:29 +00:00
|
|
|
void dispose() {
|
2023-02-04 21:09:19 +00:00
|
|
|
WidgetsBinding.instance.removeObserver(this);
|
2023-01-22 19:30:29 +00:00
|
|
|
_themeTypeNotifier.dispose();
|
2023-01-25 10:08:11 +00:00
|
|
|
_dynamicColorNotifier.dispose();
|
2023-01-22 19:30:29 +00:00
|
|
|
_primaryColorNotifier.dispose();
|
|
|
|
super.dispose();
|
2022-12-16 20:07:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2023-01-22 19:30:29 +00:00
|
|
|
return ValueListenableBuilder(
|
|
|
|
valueListenable: _themeTypeNotifier,
|
2023-06-04 11:04:04 +00:00
|
|
|
builder: (_, themeType, __) => InheritedWidgetBase<ThemeType>(
|
|
|
|
data: themeType,
|
2023-01-22 19:30:29 +00:00
|
|
|
child: ValueListenableBuilder(
|
2023-01-25 10:08:11 +00:00
|
|
|
valueListenable: _dynamicColorNotifier,
|
|
|
|
builder: (_, useDynamicColor, __) => _DynamicColorProvider(
|
|
|
|
useDynamicColor: useDynamicColor,
|
2023-01-22 19:30:29 +00:00
|
|
|
themeBrightness: _themeBrightness,
|
|
|
|
builder: (_, dynamicPrimaryColor) => ValueListenableBuilder(
|
|
|
|
valueListenable: _primaryColorNotifier,
|
|
|
|
builder: (_, primaryColor, __) => _ThemeDataProvider(
|
|
|
|
primaryColor: dynamicPrimaryColor ?? primaryColor,
|
|
|
|
brightness: _themeBrightness,
|
2023-06-04 11:04:04 +00:00
|
|
|
child: widget.child,
|
2023-01-22 19:30:29 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
2022-12-16 20:07:05 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void setThemeType(ThemeType themeType) {
|
2023-01-22 19:30:29 +00:00
|
|
|
_themeTypeNotifier.value = themeType;
|
2023-01-26 15:03:48 +00:00
|
|
|
_prefs.themeType = themeType;
|
2022-12-16 20:07:05 +00:00
|
|
|
}
|
|
|
|
|
2023-01-22 19:30:29 +00:00
|
|
|
Brightness get _themeBrightness {
|
|
|
|
switch (_themeTypeNotifier.value) {
|
2022-12-16 20:07:05 +00:00
|
|
|
case ThemeType.light:
|
|
|
|
return Brightness.light;
|
|
|
|
case ThemeType.dark:
|
|
|
|
return Brightness.dark;
|
|
|
|
case ThemeType.systemDefault:
|
|
|
|
return SchedulerBinding.instance.platformDispatcher.platformBrightness;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-31 21:24:26 +00:00
|
|
|
void setPrimaryColor(Color color) {
|
|
|
|
_primaryColorNotifier.value = color;
|
|
|
|
_prefs.primaryColor = color;
|
|
|
|
}
|
|
|
|
|
2023-01-25 10:08:11 +00:00
|
|
|
void enableDynamicColor(bool enable) {
|
|
|
|
_dynamicColorNotifier.value = enable;
|
2023-01-26 15:03:48 +00:00
|
|
|
_prefs.dynamicColor = enable;
|
2023-01-22 19:30:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-25 10:08:11 +00:00
|
|
|
class _DynamicColorProvider extends StatelessWidget {
|
|
|
|
final bool useDynamicColor;
|
2023-01-22 19:30:29 +00:00
|
|
|
final Brightness themeBrightness;
|
|
|
|
final Widget Function(BuildContext context, Color? primaryColor) builder;
|
|
|
|
|
2023-01-25 10:08:11 +00:00
|
|
|
const _DynamicColorProvider({
|
|
|
|
required this.useDynamicColor,
|
2023-01-22 19:30:29 +00:00
|
|
|
required this.themeBrightness,
|
|
|
|
required this.builder,
|
|
|
|
});
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return DynamicColorBuilder(
|
|
|
|
builder: (lightDynamic, darkDynamic) {
|
2023-01-25 10:08:11 +00:00
|
|
|
late final DynamicColorState state;
|
2023-01-22 19:30:29 +00:00
|
|
|
late final Color? dynamicPrimaryColor;
|
|
|
|
if (lightDynamic != null && darkDynamic != null) {
|
2023-01-25 10:08:11 +00:00
|
|
|
if (useDynamicColor) {
|
2023-01-26 15:03:48 +00:00
|
|
|
dynamicPrimaryColor =
|
|
|
|
(themeBrightness == Brightness.light ? lightDynamic : darkDynamic).primary;
|
2023-01-25 10:08:11 +00:00
|
|
|
state = DynamicColorState.enabled;
|
2023-01-22 19:30:29 +00:00
|
|
|
} else {
|
|
|
|
dynamicPrimaryColor = null;
|
2023-01-25 10:08:11 +00:00
|
|
|
state = DynamicColorState.disabled;
|
2023-01-22 19:30:29 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dynamicPrimaryColor = null;
|
2023-01-25 10:08:11 +00:00
|
|
|
state = DynamicColorState.unavailable;
|
2023-01-22 19:30:29 +00:00
|
|
|
}
|
2023-06-04 11:04:04 +00:00
|
|
|
return InheritedWidgetBase<DynamicColorState>(
|
|
|
|
data: state,
|
2023-01-22 19:30:29 +00:00
|
|
|
child: builder(context, dynamicPrimaryColor),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class _ThemeDataProvider extends StatelessWidget {
|
|
|
|
final Color primaryColor;
|
|
|
|
final Brightness brightness;
|
2023-06-04 11:04:04 +00:00
|
|
|
final Widget child;
|
2023-01-22 19:30:29 +00:00
|
|
|
|
|
|
|
const _ThemeDataProvider({
|
|
|
|
required this.primaryColor,
|
|
|
|
required this.brightness,
|
2023-06-04 11:04:04 +00:00
|
|
|
required this.child,
|
2023-01-22 19:30:29 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2023-06-04 11:04:04 +00:00
|
|
|
return InheritedWidgetBase<ThemeData>(
|
|
|
|
data: _themeFromColorScheme(_colorSchemeFromColor()),
|
|
|
|
child: child,
|
2023-01-22 19:30:29 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
ThemeData _themeFromColorScheme(ColorScheme scheme) {
|
|
|
|
return ThemeData(
|
|
|
|
useMaterial3: true,
|
|
|
|
brightness: scheme.brightness,
|
2023-01-31 21:24:26 +00:00
|
|
|
primaryColor: primaryColor,
|
2023-01-22 19:30:29 +00:00
|
|
|
colorScheme: scheme,
|
2023-01-31 21:24:26 +00:00
|
|
|
appBarTheme: AppBarTheme(
|
|
|
|
elevation: 4,
|
|
|
|
color: scheme.surface,
|
|
|
|
surfaceTintColor: scheme.surfaceTint,
|
|
|
|
),
|
|
|
|
cardTheme: CardTheme(
|
|
|
|
clipBehavior: Clip.antiAlias,
|
|
|
|
color: scheme.surface,
|
|
|
|
elevation: 4,
|
|
|
|
margin: EdgeInsets.zero,
|
|
|
|
shadowColor: Colors.transparent,
|
|
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(Dimens.borderRadiusL)),
|
|
|
|
surfaceTintColor: scheme.surfaceTint,
|
|
|
|
),
|
2023-01-22 19:30:29 +00:00
|
|
|
dialogBackgroundColor: scheme.surface,
|
2023-01-31 21:24:26 +00:00
|
|
|
dialogTheme: DialogTheme(
|
|
|
|
backgroundColor: scheme.surface,
|
|
|
|
surfaceTintColor: scheme.surfaceTint,
|
|
|
|
elevation: 6,
|
|
|
|
),
|
2023-02-05 14:20:36 +00:00
|
|
|
dividerColor: scheme.outlineVariant,
|
|
|
|
dividerTheme: DividerThemeData(
|
|
|
|
color: scheme.outlineVariant,
|
|
|
|
space: 0,
|
|
|
|
),
|
2023-01-31 21:24:26 +00:00
|
|
|
listTileTheme: ListTileThemeData(
|
|
|
|
style: ListTileStyle.list,
|
|
|
|
iconColor: scheme.onSurface,
|
|
|
|
textColor: scheme.onSurface,
|
|
|
|
),
|
2023-01-22 19:30:29 +00:00
|
|
|
scaffoldBackgroundColor: scheme.surface,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
ColorScheme _colorSchemeFromColor() {
|
2023-01-26 15:03:48 +00:00
|
|
|
final scheme = brightness == Brightness.light
|
|
|
|
? Scheme.light(primaryColor.value)
|
|
|
|
: Scheme.dark(primaryColor.value);
|
2023-01-26 15:33:33 +00:00
|
|
|
|
2023-01-22 19:30:29 +00:00
|
|
|
return ColorScheme(
|
2022-12-16 20:07:05 +00:00
|
|
|
brightness: brightness,
|
2023-02-05 13:56:04 +00:00
|
|
|
background: Color(scheme.background),
|
|
|
|
error: Color(scheme.error),
|
|
|
|
errorContainer: Color(scheme.errorContainer),
|
|
|
|
onBackground: Color(scheme.onBackground),
|
|
|
|
onError: Color(scheme.onError),
|
|
|
|
onErrorContainer: Color(scheme.onErrorContainer),
|
2022-12-16 20:07:05 +00:00
|
|
|
primary: Color(scheme.primary),
|
|
|
|
onPrimary: Color(scheme.onPrimary),
|
|
|
|
primaryContainer: Color(scheme.primaryContainer),
|
|
|
|
onPrimaryContainer: Color(scheme.onPrimaryContainer),
|
|
|
|
secondary: Color(scheme.secondary),
|
|
|
|
onSecondary: Color(scheme.onSecondary),
|
2023-01-26 15:33:33 +00:00
|
|
|
surface: Color.alphaBlend(
|
|
|
|
Color(scheme.primary).withOpacity(0.05),
|
|
|
|
Color(scheme.background),
|
|
|
|
),
|
2022-12-16 20:07:05 +00:00
|
|
|
onSurface: Color(scheme.onSurface),
|
2023-01-26 15:33:33 +00:00
|
|
|
surfaceVariant: Color.alphaBlend(
|
|
|
|
Color(scheme.primary).withOpacity(0.5),
|
|
|
|
Color(scheme.background),
|
|
|
|
),
|
2022-12-16 20:07:05 +00:00
|
|
|
onSurfaceVariant: Color(scheme.onSurfaceVariant),
|
2023-02-05 13:56:04 +00:00
|
|
|
outline: Color(scheme.outline),
|
|
|
|
outlineVariant: Color(scheme.outlineVariant),
|
2022-12-16 20:07:05 +00:00
|
|
|
);
|
|
|
|
}
|
2022-10-24 20:25:38 +00:00
|
|
|
}
|