mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-26 01:10:39 +00:00
Moved ThemeProvider
functionality to EnumProviders
This commit is contained in:
parent
c8c96b851e
commit
d2d57f5fb9
10 changed files with 271 additions and 324 deletions
|
@ -6,9 +6,9 @@ import 'package:lightmeter/environment.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/providers.dart';
|
import 'package:lightmeter/providers.dart';
|
||||||
import 'package:lightmeter/providers/enum_providers.dart';
|
import 'package:lightmeter/providers/enum_providers.dart';
|
||||||
|
import 'package:lightmeter/res/theme.dart';
|
||||||
import 'package:lightmeter/screens/metering/flow_metering.dart';
|
import 'package:lightmeter/screens/metering/flow_metering.dart';
|
||||||
import 'package:lightmeter/screens/settings/flow_settings.dart';
|
import 'package:lightmeter/screens/settings/flow_settings.dart';
|
||||||
import 'package:lightmeter/utils/inherited_generics.dart';
|
|
||||||
|
|
||||||
class Application extends StatelessWidget {
|
class Application extends StatelessWidget {
|
||||||
final Environment env;
|
final Environment env;
|
||||||
|
@ -19,54 +19,48 @@ class Application extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return LightmeterProviders(
|
return LightmeterProviders(
|
||||||
env: env,
|
env: env,
|
||||||
builder: (context, ready) => ready
|
builder: (context, ready) {
|
||||||
? _AnnotatedRegionWrapper(
|
if (ready) {
|
||||||
child: MaterialApp(
|
final theme = themeFrom(
|
||||||
theme: context.listen<ThemeData>(),
|
EnumProviders.primaryColorOf(context),
|
||||||
locale: Locale(EnumProviders.localeOf(context).intlName),
|
EnumProviders.brightnessOf(context),
|
||||||
localizationsDelegates: const [
|
);
|
||||||
S.delegate,
|
final systemIconsBrightness =
|
||||||
GlobalMaterialLocalizations.delegate,
|
ThemeData.estimateBrightnessForColor(theme.colorScheme.onSurface);
|
||||||
GlobalWidgetsLocalizations.delegate,
|
return AnnotatedRegion(
|
||||||
GlobalCupertinoLocalizations.delegate,
|
value: SystemUiOverlayStyle(
|
||||||
],
|
statusBarColor: Colors.transparent,
|
||||||
supportedLocales: S.delegate.supportedLocales,
|
statusBarBrightness:
|
||||||
builder: (context, child) => MediaQuery(
|
systemIconsBrightness == Brightness.light ? Brightness.dark : Brightness.light,
|
||||||
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
|
statusBarIconBrightness: systemIconsBrightness,
|
||||||
child: child!,
|
systemNavigationBarColor: Colors.transparent,
|
||||||
),
|
systemNavigationBarIconBrightness: systemIconsBrightness,
|
||||||
initialRoute: "metering",
|
),
|
||||||
routes: {
|
child: MaterialApp(
|
||||||
"metering": (context) => const MeteringFlow(),
|
theme: theme,
|
||||||
"settings": (context) => const SettingsFlow(),
|
locale: Locale(EnumProviders.localeOf(context).intlName),
|
||||||
},
|
localizationsDelegates: const [
|
||||||
|
S.delegate,
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
supportedLocales: S.delegate.supportedLocales,
|
||||||
|
builder: (context, child) => MediaQuery(
|
||||||
|
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
|
||||||
|
child: child!,
|
||||||
),
|
),
|
||||||
)
|
initialRoute: "metering",
|
||||||
: const SizedBox(),
|
routes: {
|
||||||
);
|
"metering": (context) => const MeteringFlow(),
|
||||||
}
|
"settings": (context) => const SettingsFlow(),
|
||||||
}
|
},
|
||||||
|
),
|
||||||
class _AnnotatedRegionWrapper extends StatelessWidget {
|
);
|
||||||
final Widget child;
|
} else {
|
||||||
|
return const SizedBox();
|
||||||
const _AnnotatedRegionWrapper({required this.child});
|
}
|
||||||
|
},
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final systemIconsBrightness = ThemeData.estimateBrightnessForColor(
|
|
||||||
context.listen<ThemeData>().colorScheme.onSurface,
|
|
||||||
);
|
|
||||||
return AnnotatedRegion(
|
|
||||||
value: SystemUiOverlayStyle(
|
|
||||||
statusBarColor: Colors.transparent,
|
|
||||||
statusBarBrightness:
|
|
||||||
systemIconsBrightness == Brightness.light ? Brightness.dark : Brightness.light,
|
|
||||||
statusBarIconBrightness: systemIconsBrightness,
|
|
||||||
systemNavigationBarColor: Colors.transparent,
|
|
||||||
systemNavigationBarIconBrightness: systemIconsBrightness,
|
|
||||||
),
|
|
||||||
child: child,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import 'package:lightmeter/providers/enum_providers.dart';
|
||||||
import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
||||||
import 'package:lightmeter/providers/metering_screen_layout_provider.dart';
|
import 'package:lightmeter/providers/metering_screen_layout_provider.dart';
|
||||||
import 'package:lightmeter/providers/service_providers.dart';
|
import 'package:lightmeter/providers/service_providers.dart';
|
||||||
import 'package:lightmeter/providers/theme_provider.dart';
|
|
||||||
import 'package:platform/platform.dart';
|
import 'package:platform/platform.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
@ -40,10 +39,8 @@ class LightmeterProviders extends StatelessWidget {
|
||||||
child: EnumProviders(
|
child: EnumProviders(
|
||||||
child: MeteringScreenLayoutProvider(
|
child: MeteringScreenLayoutProvider(
|
||||||
child: EquipmentProfileProvider(
|
child: EquipmentProfileProvider(
|
||||||
child: ThemeProvider(
|
child: Builder(
|
||||||
child: Builder(
|
builder: (context) => builder(context, true),
|
||||||
builder: (context) => builder(context, true),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/scheduler.dart';
|
||||||
|
import 'package:lightmeter/data/models/dynamic_colors_state.dart';
|
||||||
import 'package:lightmeter/data/models/ev_source_type.dart';
|
import 'package:lightmeter/data/models/ev_source_type.dart';
|
||||||
import 'package:lightmeter/data/models/supported_locale.dart';
|
import 'package:lightmeter/data/models/supported_locale.dart';
|
||||||
import 'package:lightmeter/data/models/theme_type.dart';
|
import 'package:lightmeter/data/models/theme_type.dart';
|
||||||
|
@ -8,8 +11,11 @@ import 'package:lightmeter/providers/service_providers.dart';
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
enum _ListenableAspect {
|
enum _ListenableAspect {
|
||||||
|
brightness,
|
||||||
|
dynamicColorState,
|
||||||
evSourceType,
|
evSourceType,
|
||||||
locale,
|
locale,
|
||||||
|
primaryColor,
|
||||||
stopType,
|
stopType,
|
||||||
themeType,
|
themeType,
|
||||||
}
|
}
|
||||||
|
@ -23,6 +29,22 @@ class EnumProviders extends StatefulWidget {
|
||||||
return context.findAncestorStateOfType<_EnumProvidersState>()!;
|
return context.findAncestorStateOfType<_EnumProvidersState>()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Brightness brightnessOf(BuildContext context) {
|
||||||
|
return InheritedModel.inheritFrom<_EnumProvidersModel>(
|
||||||
|
context,
|
||||||
|
aspect: _ListenableAspect.brightness,
|
||||||
|
)!
|
||||||
|
.brightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DynamicColorState dynamicColorStateOf(BuildContext context) {
|
||||||
|
return InheritedModel.inheritFrom<_EnumProvidersModel>(
|
||||||
|
context,
|
||||||
|
aspect: _ListenableAspect.dynamicColorState,
|
||||||
|
)!
|
||||||
|
.dynamicColorState;
|
||||||
|
}
|
||||||
|
|
||||||
static EvSourceType evSourceTypeOf(BuildContext context) {
|
static EvSourceType evSourceTypeOf(BuildContext context) {
|
||||||
return InheritedModel.inheritFrom<_EnumProvidersModel>(
|
return InheritedModel.inheritFrom<_EnumProvidersModel>(
|
||||||
context,
|
context,
|
||||||
|
@ -39,6 +61,14 @@ class EnumProviders extends StatefulWidget {
|
||||||
.locale;
|
.locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Color primaryColorOf(BuildContext context) {
|
||||||
|
return InheritedModel.inheritFrom<_EnumProvidersModel>(
|
||||||
|
context,
|
||||||
|
aspect: _ListenableAspect.primaryColor,
|
||||||
|
)!
|
||||||
|
.primaryColor;
|
||||||
|
}
|
||||||
|
|
||||||
static StopType stopTypeOf(BuildContext context) {
|
static StopType stopTypeOf(BuildContext context) {
|
||||||
return InheritedModel.inheritFrom<_EnumProvidersModel>(
|
return InheritedModel.inheritFrom<_EnumProvidersModel>(
|
||||||
context,
|
context,
|
||||||
|
@ -59,11 +89,13 @@ class EnumProviders extends StatefulWidget {
|
||||||
State<EnumProviders> createState() => _EnumProvidersState();
|
State<EnumProviders> createState() => _EnumProvidersState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _EnumProvidersState extends State<EnumProviders> {
|
class _EnumProvidersState extends State<EnumProviders> with WidgetsBindingObserver {
|
||||||
UserPreferencesService get userPreferencesService =>
|
UserPreferencesService get userPreferencesService =>
|
||||||
ServiceProviders.userPreferencesServiceOf(context);
|
ServiceProviders.userPreferencesServiceOf(context);
|
||||||
|
|
||||||
|
late bool dynamicColor = userPreferencesService.dynamicColor;
|
||||||
late EvSourceType evSourceType;
|
late EvSourceType evSourceType;
|
||||||
|
late Color primaryColor = userPreferencesService.primaryColor;
|
||||||
late StopType stopType = userPreferencesService.stopType;
|
late StopType stopType = userPreferencesService.stopType;
|
||||||
late SupportedLocale locale = userPreferencesService.locale;
|
late SupportedLocale locale = userPreferencesService.locale;
|
||||||
late ThemeType themeType = userPreferencesService.themeType;
|
late ThemeType themeType = userPreferencesService.themeType;
|
||||||
|
@ -76,19 +108,61 @@ class _EnumProvidersState extends State<EnumProviders> {
|
||||||
!ServiceProviders.environmentOf(context).hasLightSensor
|
!ServiceProviders.environmentOf(context).hasLightSensor
|
||||||
? EvSourceType.camera
|
? EvSourceType.camera
|
||||||
: evSourceType;
|
: evSourceType;
|
||||||
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangePlatformBrightness() {
|
||||||
|
super.didChangePlatformBrightness();
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return _EnumProvidersModel(
|
return DynamicColorBuilder(
|
||||||
evSourceType: evSourceType,
|
builder: (lightDynamic, darkDynamic) {
|
||||||
locale: locale,
|
late final DynamicColorState state;
|
||||||
stopType: stopType,
|
late final Color? dynamicPrimaryColor;
|
||||||
themeType: themeType,
|
if (lightDynamic != null && darkDynamic != null) {
|
||||||
child: widget.child,
|
if (dynamicColor) {
|
||||||
|
dynamicPrimaryColor =
|
||||||
|
(_themeBrightness == Brightness.light ? lightDynamic : darkDynamic).primary;
|
||||||
|
state = DynamicColorState.enabled;
|
||||||
|
} else {
|
||||||
|
dynamicPrimaryColor = null;
|
||||||
|
state = DynamicColorState.disabled;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dynamicPrimaryColor = null;
|
||||||
|
state = DynamicColorState.unavailable;
|
||||||
|
}
|
||||||
|
return _EnumProvidersModel(
|
||||||
|
brightness: _themeBrightness,
|
||||||
|
dynamicColorState: state,
|
||||||
|
evSourceType: evSourceType,
|
||||||
|
locale: locale,
|
||||||
|
primaryColor: dynamicPrimaryColor ?? primaryColor,
|
||||||
|
stopType: stopType,
|
||||||
|
themeType: themeType,
|
||||||
|
child: widget.child,
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void enableDynamicColor(bool enable) {
|
||||||
|
setState(() {
|
||||||
|
dynamicColor = enable;
|
||||||
|
});
|
||||||
|
ServiceProviders.userPreferencesServiceOf(context).dynamicColor = enable;
|
||||||
|
}
|
||||||
|
|
||||||
void toggleEvSourceType() {
|
void toggleEvSourceType() {
|
||||||
if (!ServiceProviders.environmentOf(context).hasLightSensor) {
|
if (!ServiceProviders.environmentOf(context).hasLightSensor) {
|
||||||
return;
|
return;
|
||||||
|
@ -113,6 +187,13 @@ class _EnumProvidersState extends State<EnumProviders> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setPrimaryColor(Color primaryColor) {
|
||||||
|
setState(() {
|
||||||
|
this.primaryColor = primaryColor;
|
||||||
|
});
|
||||||
|
ServiceProviders.userPreferencesServiceOf(context).primaryColor = primaryColor;
|
||||||
|
}
|
||||||
|
|
||||||
void setStopType(StopType stopType) {
|
void setStopType(StopType stopType) {
|
||||||
setState(() {
|
setState(() {
|
||||||
this.stopType = stopType;
|
this.stopType = stopType;
|
||||||
|
@ -126,17 +207,34 @@ class _EnumProvidersState extends State<EnumProviders> {
|
||||||
});
|
});
|
||||||
ServiceProviders.userPreferencesServiceOf(context).themeType = themeType;
|
ServiceProviders.userPreferencesServiceOf(context).themeType = themeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Brightness get _themeBrightness {
|
||||||
|
switch (themeType) {
|
||||||
|
case ThemeType.light:
|
||||||
|
return Brightness.light;
|
||||||
|
case ThemeType.dark:
|
||||||
|
return Brightness.dark;
|
||||||
|
case ThemeType.systemDefault:
|
||||||
|
return SchedulerBinding.instance.platformDispatcher.platformBrightness;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _EnumProvidersModel extends InheritedModel<_ListenableAspect> {
|
class _EnumProvidersModel extends InheritedModel<_ListenableAspect> {
|
||||||
|
final Brightness brightness;
|
||||||
|
final DynamicColorState dynamicColorState;
|
||||||
final EvSourceType evSourceType;
|
final EvSourceType evSourceType;
|
||||||
final SupportedLocale locale;
|
final SupportedLocale locale;
|
||||||
|
final Color primaryColor;
|
||||||
final StopType stopType;
|
final StopType stopType;
|
||||||
final ThemeType themeType;
|
final ThemeType themeType;
|
||||||
|
|
||||||
const _EnumProvidersModel({
|
const _EnumProvidersModel({
|
||||||
|
required this.brightness,
|
||||||
|
required this.dynamicColorState,
|
||||||
required this.evSourceType,
|
required this.evSourceType,
|
||||||
required this.locale,
|
required this.locale,
|
||||||
|
required this.primaryColor,
|
||||||
required this.stopType,
|
required this.stopType,
|
||||||
required this.themeType,
|
required this.themeType,
|
||||||
required super.child,
|
required super.child,
|
||||||
|
@ -144,8 +242,11 @@ class _EnumProvidersModel extends InheritedModel<_ListenableAspect> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool updateShouldNotify(_EnumProvidersModel oldWidget) {
|
bool updateShouldNotify(_EnumProvidersModel oldWidget) {
|
||||||
return evSourceType != oldWidget.evSourceType ||
|
return brightness != oldWidget.brightness ||
|
||||||
|
dynamicColorState != oldWidget.dynamicColorState ||
|
||||||
|
evSourceType != oldWidget.evSourceType ||
|
||||||
locale != oldWidget.locale ||
|
locale != oldWidget.locale ||
|
||||||
|
primaryColor != oldWidget.primaryColor ||
|
||||||
stopType != oldWidget.stopType ||
|
stopType != oldWidget.stopType ||
|
||||||
themeType != oldWidget.themeType;
|
themeType != oldWidget.themeType;
|
||||||
}
|
}
|
||||||
|
@ -155,9 +256,15 @@ class _EnumProvidersModel extends InheritedModel<_ListenableAspect> {
|
||||||
_EnumProvidersModel oldWidget,
|
_EnumProvidersModel oldWidget,
|
||||||
Set<_ListenableAspect> dependencies,
|
Set<_ListenableAspect> dependencies,
|
||||||
) {
|
) {
|
||||||
return (evSourceType != oldWidget.evSourceType &&
|
return (brightness != oldWidget.brightness &&
|
||||||
|
dependencies.contains(_ListenableAspect.brightness)) ||
|
||||||
|
(dynamicColorState != oldWidget.dynamicColorState &&
|
||||||
|
dependencies.contains(_ListenableAspect.dynamicColorState)) ||
|
||||||
|
(evSourceType != oldWidget.evSourceType &&
|
||||||
dependencies.contains(_ListenableAspect.evSourceType)) ||
|
dependencies.contains(_ListenableAspect.evSourceType)) ||
|
||||||
(locale != oldWidget.locale && dependencies.contains(_ListenableAspect.locale)) ||
|
(locale != oldWidget.locale && dependencies.contains(_ListenableAspect.locale)) ||
|
||||||
|
(primaryColor != oldWidget.primaryColor &&
|
||||||
|
dependencies.contains(_ListenableAspect.primaryColor)) ||
|
||||||
(stopType != oldWidget.stopType && dependencies.contains(_ListenableAspect.stopType)) ||
|
(stopType != oldWidget.stopType && dependencies.contains(_ListenableAspect.stopType)) ||
|
||||||
(themeType != oldWidget.themeType && dependencies.contains(_ListenableAspect.themeType));
|
(themeType != oldWidget.themeType && dependencies.contains(_ListenableAspect.themeType));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,246 +0,0 @@
|
||||||
import 'package:dynamic_color/dynamic_color.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/scheduler.dart';
|
|
||||||
import 'package:lightmeter/data/models/dynamic_colors_state.dart';
|
|
||||||
import 'package:lightmeter/data/models/theme_type.dart';
|
|
||||||
import 'package:lightmeter/data/shared_prefs_service.dart';
|
|
||||||
import 'package:lightmeter/providers/enum_providers.dart';
|
|
||||||
import 'package:lightmeter/providers/service_providers.dart';
|
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
|
||||||
import 'package:lightmeter/utils/inherited_generics.dart';
|
|
||||||
import 'package:material_color_utilities/material_color_utilities.dart';
|
|
||||||
|
|
||||||
class ThemeProvider extends StatefulWidget {
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
const ThemeProvider({
|
|
||||||
required this.child,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
static ThemeProviderState of(BuildContext context) {
|
|
||||||
return context.findAncestorStateOfType<ThemeProviderState>()!;
|
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
|
||||||
];
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ThemeProvider> createState() => ThemeProviderState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class ThemeProviderState extends State<ThemeProvider> with WidgetsBindingObserver {
|
|
||||||
UserPreferencesService get _prefs => ServiceProviders.userPreferencesServiceOf(context);
|
|
||||||
|
|
||||||
late final _dynamicColorNotifier = ValueNotifier<bool>(_prefs.dynamicColor);
|
|
||||||
late final _primaryColorNotifier = ValueNotifier<Color>(_prefs.primaryColor);
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
WidgetsBinding.instance.addObserver(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangePlatformBrightness() {
|
|
||||||
super.didChangePlatformBrightness();
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
|
||||||
_dynamicColorNotifier.dispose();
|
|
||||||
_primaryColorNotifier.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return ValueListenableBuilder(
|
|
||||||
valueListenable: _dynamicColorNotifier,
|
|
||||||
builder: (_, useDynamicColor, __) => _DynamicColorProvider(
|
|
||||||
useDynamicColor: useDynamicColor,
|
|
||||||
themeBrightness: _themeBrightness,
|
|
||||||
builder: (_, dynamicPrimaryColor) => ValueListenableBuilder(
|
|
||||||
valueListenable: _primaryColorNotifier,
|
|
||||||
builder: (_, primaryColor, __) => _ThemeDataProvider(
|
|
||||||
primaryColor: dynamicPrimaryColor ?? primaryColor,
|
|
||||||
brightness: _themeBrightness,
|
|
||||||
child: widget.child,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Brightness get _themeBrightness {
|
|
||||||
switch (EnumProviders.themeTypeOf(context)) {
|
|
||||||
case ThemeType.light:
|
|
||||||
return Brightness.light;
|
|
||||||
case ThemeType.dark:
|
|
||||||
return Brightness.dark;
|
|
||||||
case ThemeType.systemDefault:
|
|
||||||
return SchedulerBinding.instance.platformDispatcher.platformBrightness;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPrimaryColor(Color color) {
|
|
||||||
_primaryColorNotifier.value = color;
|
|
||||||
_prefs.primaryColor = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
void enableDynamicColor(bool enable) {
|
|
||||||
_dynamicColorNotifier.value = enable;
|
|
||||||
_prefs.dynamicColor = enable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DynamicColorProvider extends StatelessWidget {
|
|
||||||
final bool useDynamicColor;
|
|
||||||
final Brightness themeBrightness;
|
|
||||||
final Widget Function(BuildContext context, Color? primaryColor) builder;
|
|
||||||
|
|
||||||
const _DynamicColorProvider({
|
|
||||||
required this.useDynamicColor,
|
|
||||||
required this.themeBrightness,
|
|
||||||
required this.builder,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return DynamicColorBuilder(
|
|
||||||
builder: (lightDynamic, darkDynamic) {
|
|
||||||
late final DynamicColorState state;
|
|
||||||
late final Color? dynamicPrimaryColor;
|
|
||||||
if (lightDynamic != null && darkDynamic != null) {
|
|
||||||
if (useDynamicColor) {
|
|
||||||
dynamicPrimaryColor =
|
|
||||||
(themeBrightness == Brightness.light ? lightDynamic : darkDynamic).primary;
|
|
||||||
state = DynamicColorState.enabled;
|
|
||||||
} else {
|
|
||||||
dynamicPrimaryColor = null;
|
|
||||||
state = DynamicColorState.disabled;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dynamicPrimaryColor = null;
|
|
||||||
state = DynamicColorState.unavailable;
|
|
||||||
}
|
|
||||||
return InheritedWidgetBase<DynamicColorState>(
|
|
||||||
data: state,
|
|
||||||
child: builder(context, dynamicPrimaryColor),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ThemeDataProvider extends StatelessWidget {
|
|
||||||
final Color primaryColor;
|
|
||||||
final Brightness brightness;
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
const _ThemeDataProvider({
|
|
||||||
required this.primaryColor,
|
|
||||||
required this.brightness,
|
|
||||||
required this.child,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return InheritedWidgetBase<ThemeData>(
|
|
||||||
data: _themeFromColorScheme(_colorSchemeFromColor()),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ThemeData _themeFromColorScheme(ColorScheme scheme) {
|
|
||||||
return ThemeData(
|
|
||||||
useMaterial3: true,
|
|
||||||
brightness: scheme.brightness,
|
|
||||||
primaryColor: primaryColor,
|
|
||||||
colorScheme: scheme,
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
dialogBackgroundColor: scheme.surface,
|
|
||||||
dialogTheme: DialogTheme(
|
|
||||||
backgroundColor: scheme.surface,
|
|
||||||
surfaceTintColor: scheme.surfaceTint,
|
|
||||||
elevation: 6,
|
|
||||||
),
|
|
||||||
dividerColor: scheme.outlineVariant,
|
|
||||||
dividerTheme: DividerThemeData(
|
|
||||||
color: scheme.outlineVariant,
|
|
||||||
space: 0,
|
|
||||||
),
|
|
||||||
listTileTheme: ListTileThemeData(
|
|
||||||
style: ListTileStyle.list,
|
|
||||||
iconColor: scheme.onSurface,
|
|
||||||
textColor: scheme.onSurface,
|
|
||||||
),
|
|
||||||
scaffoldBackgroundColor: scheme.surface,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ColorScheme _colorSchemeFromColor() {
|
|
||||||
final scheme = brightness == Brightness.light
|
|
||||||
? Scheme.light(primaryColor.value)
|
|
||||||
: Scheme.dark(primaryColor.value);
|
|
||||||
|
|
||||||
return ColorScheme(
|
|
||||||
brightness: brightness,
|
|
||||||
background: Color(scheme.background),
|
|
||||||
error: Color(scheme.error),
|
|
||||||
errorContainer: Color(scheme.errorContainer),
|
|
||||||
onBackground: Color(scheme.onBackground),
|
|
||||||
onError: Color(scheme.onError),
|
|
||||||
onErrorContainer: Color(scheme.onErrorContainer),
|
|
||||||
primary: Color(scheme.primary),
|
|
||||||
onPrimary: Color(scheme.onPrimary),
|
|
||||||
primaryContainer: Color(scheme.primaryContainer),
|
|
||||||
onPrimaryContainer: Color(scheme.onPrimaryContainer),
|
|
||||||
secondary: Color(scheme.secondary),
|
|
||||||
onSecondary: Color(scheme.onSecondary),
|
|
||||||
surface: Color.alphaBlend(
|
|
||||||
Color(scheme.primary).withOpacity(0.05),
|
|
||||||
Color(scheme.background),
|
|
||||||
),
|
|
||||||
onSurface: Color(scheme.onSurface),
|
|
||||||
surfaceVariant: Color.alphaBlend(
|
|
||||||
Color(scheme.primary).withOpacity(0.5),
|
|
||||||
Color(scheme.background),
|
|
||||||
),
|
|
||||||
onSurfaceVariant: Color(scheme.onSurfaceVariant),
|
|
||||||
outline: Color(scheme.outline),
|
|
||||||
outlineVariant: Color(scheme.outlineVariant),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
97
lib/res/theme.dart
Normal file
97
lib/res/theme.dart
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
|
import 'package:material_color_utilities/material_color_utilities.dart';
|
||||||
|
|
||||||
|
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),
|
||||||
|
];
|
||||||
|
|
||||||
|
ThemeData themeFrom(Color primaryColor, Brightness brightness) {
|
||||||
|
final scheme = _colorSchemeFromColor(primaryColor, brightness);
|
||||||
|
return ThemeData(
|
||||||
|
useMaterial3: true,
|
||||||
|
brightness: scheme.brightness,
|
||||||
|
primaryColor: primaryColor,
|
||||||
|
colorScheme: scheme,
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
dialogBackgroundColor: scheme.surface,
|
||||||
|
dialogTheme: DialogTheme(
|
||||||
|
backgroundColor: scheme.surface,
|
||||||
|
surfaceTintColor: scheme.surfaceTint,
|
||||||
|
elevation: 6,
|
||||||
|
),
|
||||||
|
dividerColor: scheme.outlineVariant,
|
||||||
|
dividerTheme: DividerThemeData(
|
||||||
|
color: scheme.outlineVariant,
|
||||||
|
space: 0,
|
||||||
|
),
|
||||||
|
listTileTheme: ListTileThemeData(
|
||||||
|
style: ListTileStyle.list,
|
||||||
|
iconColor: scheme.onSurface,
|
||||||
|
textColor: scheme.onSurface,
|
||||||
|
),
|
||||||
|
scaffoldBackgroundColor: scheme.surface,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorScheme _colorSchemeFromColor(Color primaryColor, Brightness brightness) {
|
||||||
|
final scheme = brightness == Brightness.light
|
||||||
|
? Scheme.light(primaryColor.value)
|
||||||
|
: Scheme.dark(primaryColor.value);
|
||||||
|
|
||||||
|
return ColorScheme(
|
||||||
|
brightness: brightness,
|
||||||
|
background: Color(scheme.background),
|
||||||
|
error: Color(scheme.error),
|
||||||
|
errorContainer: Color(scheme.errorContainer),
|
||||||
|
onBackground: Color(scheme.onBackground),
|
||||||
|
onError: Color(scheme.onError),
|
||||||
|
onErrorContainer: Color(scheme.onErrorContainer),
|
||||||
|
primary: Color(scheme.primary),
|
||||||
|
onPrimary: Color(scheme.onPrimary),
|
||||||
|
primaryContainer: Color(scheme.primaryContainer),
|
||||||
|
onPrimaryContainer: Color(scheme.onPrimaryContainer),
|
||||||
|
secondary: Color(scheme.secondary),
|
||||||
|
onSecondary: Color(scheme.onSecondary),
|
||||||
|
surface: Color.alphaBlend(
|
||||||
|
Color(scheme.primary).withOpacity(0.05),
|
||||||
|
Color(scheme.background),
|
||||||
|
),
|
||||||
|
onSurface: Color(scheme.onSurface),
|
||||||
|
surfaceVariant: Color.alphaBlend(
|
||||||
|
Color(scheme.primary).withOpacity(0.5),
|
||||||
|
Color(scheme.background),
|
||||||
|
),
|
||||||
|
onSurfaceVariant: Color(scheme.onSurfaceVariant),
|
||||||
|
outline: Color(scheme.outline),
|
||||||
|
outlineVariant: Color(scheme.outlineVariant),
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,9 +1,8 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/data/models/dynamic_colors_state.dart';
|
import 'package:lightmeter/data/models/dynamic_colors_state.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/providers/theme_provider.dart';
|
import 'package:lightmeter/providers/enum_providers.dart';
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
import 'package:lightmeter/utils/inherited_generics.dart';
|
|
||||||
|
|
||||||
class DynamicColorListTile extends StatelessWidget {
|
class DynamicColorListTile extends StatelessWidget {
|
||||||
const DynamicColorListTile({super.key});
|
const DynamicColorListTile({super.key});
|
||||||
|
@ -13,8 +12,8 @@ class DynamicColorListTile extends StatelessWidget {
|
||||||
return SwitchListTile(
|
return SwitchListTile(
|
||||||
secondary: const Icon(Icons.colorize),
|
secondary: const Icon(Icons.colorize),
|
||||||
title: Text(S.of(context).dynamicColor),
|
title: Text(S.of(context).dynamicColor),
|
||||||
value: context.listen<DynamicColorState>() == DynamicColorState.enabled,
|
value: EnumProviders.dynamicColorStateOf(context) == DynamicColorState.enabled,
|
||||||
onChanged: ThemeProvider.of(context).enableDynamicColor,
|
onChanged: EnumProviders.of(context).enableDynamicColor,
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
|
contentPadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/providers/theme_provider.dart';
|
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
|
import 'package:lightmeter/res/theme.dart';
|
||||||
import 'package:lightmeter/screens/shared/filled_circle/widget_circle_filled.dart';
|
import 'package:lightmeter/screens/shared/filled_circle/widget_circle_filled.dart';
|
||||||
|
|
||||||
class PrimaryColorDialogPicker extends StatefulWidget {
|
class PrimaryColorDialogPicker extends StatefulWidget {
|
||||||
|
@ -38,9 +38,9 @@ class _PrimaryColorDialogPickerState extends State<PrimaryColorDialogPicker> {
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: List.generate(
|
children: List.generate(
|
||||||
ThemeProvider.primaryColorsList.length,
|
primaryColorsList.length,
|
||||||
(index) {
|
(index) {
|
||||||
final color = ThemeProvider.primaryColorsList[index];
|
final color = primaryColorsList[index];
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.only(left: index == 0 ? 0 : Dimens.paddingS),
|
padding: EdgeInsets.only(left: index == 0 ? 0 : Dimens.paddingS),
|
||||||
child: _SelectableColorItem(
|
child: _SelectableColorItem(
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/data/models/dynamic_colors_state.dart';
|
import 'package:lightmeter/data/models/dynamic_colors_state.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/providers/theme_provider.dart';
|
import 'package:lightmeter/providers/enum_providers.dart';
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/theme/components/primary_color/components/primary_color_picker_dialog/widget_dialog_picker_primary_color.dart';
|
import 'package:lightmeter/screens/settings/components/theme/components/primary_color/components/primary_color_picker_dialog/widget_dialog_picker_primary_color.dart';
|
||||||
import 'package:lightmeter/utils/inherited_generics.dart';
|
|
||||||
|
|
||||||
class PrimaryColorListTile extends StatelessWidget {
|
class PrimaryColorListTile extends StatelessWidget {
|
||||||
const PrimaryColorListTile({super.key});
|
const PrimaryColorListTile({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (context.listen<DynamicColorState>() == DynamicColorState.enabled) {
|
if (EnumProviders.dynamicColorStateOf(context) == DynamicColorState.enabled) {
|
||||||
return Opacity(
|
return Opacity(
|
||||||
opacity: Dimens.disabledOpacity,
|
opacity: Dimens.disabledOpacity,
|
||||||
child: IgnorePointer(
|
child: IgnorePointer(
|
||||||
|
@ -31,7 +30,7 @@ class PrimaryColorListTile extends StatelessWidget {
|
||||||
builder: (_) => const PrimaryColorDialogPicker(),
|
builder: (_) => const PrimaryColorDialogPicker(),
|
||||||
).then((value) {
|
).then((value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
ThemeProvider.of(context).setPrimaryColor(value);
|
EnumProviders.of(context).setPrimaryColor(value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/data/models/dynamic_colors_state.dart';
|
import 'package:lightmeter/data/models/dynamic_colors_state.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
|
import 'package:lightmeter/providers/enum_providers.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart';
|
import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/theme/components/dynamic_color/widget_list_tile_dynamic_color.dart';
|
import 'package:lightmeter/screens/settings/components/theme/components/dynamic_color/widget_list_tile_dynamic_color.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/theme/components/primary_color/widget_list_tile_primary_color.dart';
|
import 'package:lightmeter/screens/settings/components/theme/components/primary_color/widget_list_tile_primary_color.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/theme/components/theme_type/widget_list_tile_theme_type.dart';
|
import 'package:lightmeter/screens/settings/components/theme/components/theme_type/widget_list_tile_theme_type.dart';
|
||||||
import 'package:lightmeter/utils/inherited_generics.dart';
|
|
||||||
|
|
||||||
class ThemeSettingsSection extends StatelessWidget {
|
class ThemeSettingsSection extends StatelessWidget {
|
||||||
const ThemeSettingsSection({super.key});
|
const ThemeSettingsSection({super.key});
|
||||||
|
@ -17,7 +17,7 @@ class ThemeSettingsSection extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
const ThemeTypeListTile(),
|
const ThemeTypeListTile(),
|
||||||
const PrimaryColorListTile(),
|
const PrimaryColorListTile(),
|
||||||
if (context.get<DynamicColorState>() != DynamicColorState.unavailable)
|
if (EnumProviders.dynamicColorStateOf(context) != DynamicColorState.unavailable)
|
||||||
const DynamicColorListTile(),
|
const DynamicColorListTile(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,7 +6,7 @@ import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
||||||
import 'package:lightmeter/data/models/supported_locale.dart';
|
import 'package:lightmeter/data/models/supported_locale.dart';
|
||||||
import 'package:lightmeter/data/models/theme_type.dart';
|
import 'package:lightmeter/data/models/theme_type.dart';
|
||||||
import 'package:lightmeter/data/shared_prefs_service.dart';
|
import 'package:lightmeter/data/shared_prefs_service.dart';
|
||||||
import 'package:lightmeter/providers/theme_provider.dart';
|
import 'package:lightmeter/res/theme.dart';
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
import 'package:mocktail/mocktail.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
@ -348,13 +348,13 @@ void main() {
|
||||||
group('primaryColor', () {
|
group('primaryColor', () {
|
||||||
test('get default', () {
|
test('get default', () {
|
||||||
when(() => sharedPreferences.getInt(UserPreferencesService.primaryColorKey)).thenReturn(null);
|
when(() => sharedPreferences.getInt(UserPreferencesService.primaryColorKey)).thenReturn(null);
|
||||||
expect(service.primaryColor, ThemeProvider.primaryColorsList[5]);
|
expect(service.primaryColor, primaryColorsList[5]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('get', () {
|
test('get', () {
|
||||||
when(() => sharedPreferences.getInt(UserPreferencesService.primaryColorKey))
|
when(() => sharedPreferences.getInt(UserPreferencesService.primaryColorKey))
|
||||||
.thenReturn(0xff9c27b0);
|
.thenReturn(0xff9c27b0);
|
||||||
expect(service.primaryColor, ThemeProvider.primaryColorsList[2]);
|
expect(service.primaryColor, primaryColorsList[2]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('set', () {
|
test('set', () {
|
||||||
|
|
Loading…
Reference in a new issue