#260 optional dynamic accent color

This commit is contained in:
Thibault Deckers 2022-05-29 23:08:33 +09:00
parent cbc958289c
commit 21f3df8003
22 changed files with 297 additions and 183 deletions

View file

@ -32,6 +32,7 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
"canPinShortcut" to ShortcutManagerCompat.isRequestPinShortcutSupported(context), "canPinShortcut" to ShortcutManagerCompat.isRequestPinShortcutSupported(context),
"canPrint" to (sdkInt >= Build.VERSION_CODES.KITKAT), "canPrint" to (sdkInt >= Build.VERSION_CODES.KITKAT),
"canRenderFlagEmojis" to (sdkInt >= Build.VERSION_CODES.LOLLIPOP), "canRenderFlagEmojis" to (sdkInt >= Build.VERSION_CODES.LOLLIPOP),
"isDynamicColorAvailable" to (sdkInt >= Build.VERSION_CODES.S),
"showPinShortcutFeedback" to (sdkInt >= Build.VERSION_CODES.O), "showPinShortcutFeedback" to (sdkInt >= Build.VERSION_CODES.O),
"supportEdgeToEdgeUIMode" to (sdkInt >= Build.VERSION_CODES.Q), "supportEdgeToEdgeUIMode" to (sdkInt >= Build.VERSION_CODES.Q),
) )

View file

@ -723,6 +723,7 @@
"settingsSectionDisplay": "Display", "settingsSectionDisplay": "Display",
"settingsThemeBrightness": "Theme", "settingsThemeBrightness": "Theme",
"settingsThemeColorHighlights": "Color highlights", "settingsThemeColorHighlights": "Color highlights",
"settingsThemeEnableDynamicColor": "Dynamic color",
"settingsDisplayRefreshRateModeTile": "Display refresh rate", "settingsDisplayRefreshRateModeTile": "Display refresh rate",
"settingsDisplayRefreshRateModeTitle": "Refresh Rate", "settingsDisplayRefreshRateModeTitle": "Refresh Rate",

View file

@ -6,7 +6,7 @@ final Device device = Device._private();
class Device { class Device {
late final String _userAgent; late final String _userAgent;
late final bool _canGrantDirectoryAccess, _canPinShortcut, _canPrint, _canRenderFlagEmojis; late final bool _canGrantDirectoryAccess, _canPinShortcut, _canPrint, _canRenderFlagEmojis;
late final bool _showPinShortcutFeedback, _supportEdgeToEdgeUIMode; late final bool _isDynamicColorAvailable, _showPinShortcutFeedback, _supportEdgeToEdgeUIMode;
String get userAgent => _userAgent; String get userAgent => _userAgent;
@ -18,6 +18,8 @@ class Device {
bool get canRenderFlagEmojis => _canRenderFlagEmojis; bool get canRenderFlagEmojis => _canRenderFlagEmojis;
bool get isDynamicColorAvailable => _isDynamicColorAvailable;
bool get showPinShortcutFeedback => _showPinShortcutFeedback; bool get showPinShortcutFeedback => _showPinShortcutFeedback;
bool get supportEdgeToEdgeUIMode => _supportEdgeToEdgeUIMode; bool get supportEdgeToEdgeUIMode => _supportEdgeToEdgeUIMode;
@ -33,6 +35,7 @@ class Device {
_canPinShortcut = capabilities['canPinShortcut'] ?? false; _canPinShortcut = capabilities['canPinShortcut'] ?? false;
_canPrint = capabilities['canPrint'] ?? false; _canPrint = capabilities['canPrint'] ?? false;
_canRenderFlagEmojis = capabilities['canRenderFlagEmojis'] ?? false; _canRenderFlagEmojis = capabilities['canRenderFlagEmojis'] ?? false;
_isDynamicColorAvailable = capabilities['isDynamicColorAvailable'] ?? false;
_showPinShortcutFeedback = capabilities['showPinShortcutFeedback'] ?? false; _showPinShortcutFeedback = capabilities['showPinShortcutFeedback'] ?? false;
_supportEdgeToEdgeUIMode = capabilities['supportEdgeToEdgeUIMode'] ?? false; _supportEdgeToEdgeUIMode = capabilities['supportEdgeToEdgeUIMode'] ?? false;
} }

View file

@ -17,11 +17,15 @@ class SettingsDefaults {
static const canUseAnalysisService = true; static const canUseAnalysisService = true;
static const isInstalledAppAccessAllowed = false; static const isInstalledAppAccessAllowed = false;
static const isErrorReportingAllowed = false; static const isErrorReportingAllowed = false;
static const tileLayout = TileLayout.grid;
static const entryRenamingPattern = '<${DateNamingProcessor.key}, yyyyMMdd-HHmmss> <${NameNamingProcessor.key}>';
// display
static const displayRefreshRateMode = DisplayRefreshRateMode.auto; static const displayRefreshRateMode = DisplayRefreshRateMode.auto;
static const themeBrightness = AvesThemeBrightness.system; static const themeBrightness = AvesThemeBrightness.system;
static const themeColorMode = AvesThemeColorMode.polychrome; static const themeColorMode = AvesThemeColorMode.polychrome;
static const tileLayout = TileLayout.grid; static const enableDynamicColor = false;
static const entryRenamingPattern = '<${DateNamingProcessor.key}, yyyyMMdd-HHmmss> <${NameNamingProcessor.key}>'; static const enableBlurEffect = true; // `enableBlurEffect` has a contextual default value
// navigation // navigation
static const mustBackTwiceToExit = true; static const mustBackTwiceToExit = true;
@ -79,7 +83,6 @@ class SettingsDefaults {
static const showOverlayInfo = true; static const showOverlayInfo = true;
static const showOverlayShootingDetails = false; static const showOverlayShootingDetails = false;
static const showOverlayThumbnailPreview = false; static const showOverlayThumbnailPreview = false;
static const enableOverlayBlurEffect = true; // `enableOverlayBlurEffect` has a contextual default value
static const viewerUseCutout = true; static const viewerUseCutout = true;
static const viewerMaxBrightness = false; static const viewerMaxBrightness = false;
static const enableMotionPhotoAutoPlay = false; static const enableMotionPhotoAutoPlay = false;

View file

@ -42,15 +42,19 @@ class Settings extends ChangeNotifier {
static const isInstalledAppAccessAllowedKey = 'is_installed_app_access_allowed'; static const isInstalledAppAccessAllowedKey = 'is_installed_app_access_allowed';
static const isErrorReportingAllowedKey = 'is_crashlytics_enabled'; static const isErrorReportingAllowedKey = 'is_crashlytics_enabled';
static const localeKey = 'locale'; static const localeKey = 'locale';
static const displayRefreshRateModeKey = 'display_refresh_rate_mode';
static const themeBrightnessKey = 'theme_brightness';
static const themeColorModeKey = 'theme_color_mode';
static const catalogTimeZoneKey = 'catalog_time_zone'; static const catalogTimeZoneKey = 'catalog_time_zone';
static const tileExtentPrefixKey = 'tile_extent_'; static const tileExtentPrefixKey = 'tile_extent_';
static const tileLayoutPrefixKey = 'tile_layout_'; static const tileLayoutPrefixKey = 'tile_layout_';
static const entryRenamingPatternKey = 'entry_renaming_pattern'; static const entryRenamingPatternKey = 'entry_renaming_pattern';
static const topEntryIdsKey = 'top_entry_ids'; static const topEntryIdsKey = 'top_entry_ids';
// display
static const displayRefreshRateModeKey = 'display_refresh_rate_mode';
static const themeBrightnessKey = 'theme_brightness';
static const themeColorModeKey = 'theme_color_mode';
static const enableDynamicColorKey = 'dynamic_color';
static const enableBlurEffectKey = 'enable_overlay_blur_effect';
// navigation // navigation
static const mustBackTwiceToExitKey = 'must_back_twice_to_exit'; static const mustBackTwiceToExitKey = 'must_back_twice_to_exit';
static const keepScreenOnKey = 'keep_screen_on'; static const keepScreenOnKey = 'keep_screen_on';
@ -92,7 +96,6 @@ class Settings extends ChangeNotifier {
static const showOverlayInfoKey = 'show_overlay_info'; static const showOverlayInfoKey = 'show_overlay_info';
static const showOverlayShootingDetailsKey = 'show_overlay_shooting_details'; static const showOverlayShootingDetailsKey = 'show_overlay_shooting_details';
static const showOverlayThumbnailPreviewKey = 'show_overlay_thumbnail_preview'; static const showOverlayThumbnailPreviewKey = 'show_overlay_thumbnail_preview';
static const enableOverlayBlurEffectKey = 'enable_overlay_blur_effect';
static const viewerUseCutoutKey = 'viewer_use_cutout'; static const viewerUseCutoutKey = 'viewer_use_cutout';
static const viewerMaxBrightnessKey = 'viewer_max_brightness'; static const viewerMaxBrightnessKey = 'viewer_max_brightness';
static const enableMotionPhotoAutoPlayKey = 'motion_photo_auto_play'; static const enableMotionPhotoAutoPlayKey = 'motion_photo_auto_play';
@ -161,7 +164,7 @@ class Settings extends ChangeNotifier {
Future<void> setContextualDefaults() async { Future<void> setContextualDefaults() async {
// performance // performance
final performanceClass = await deviceService.getPerformanceClass(); final performanceClass = await deviceService.getPerformanceClass();
enableOverlayBlurEffect = performanceClass >= 29; enableBlurEffect = performanceClass >= 29;
// availability // availability
final defaultMapStyle = mobileServices.defaultMapStyle; final defaultMapStyle = mobileServices.defaultMapStyle;
@ -249,18 +252,6 @@ class Settings extends ChangeNotifier {
return _appliedLocale!; return _appliedLocale!;
} }
DisplayRefreshRateMode get displayRefreshRateMode => getEnumOrDefault(displayRefreshRateModeKey, SettingsDefaults.displayRefreshRateMode, DisplayRefreshRateMode.values);
set displayRefreshRateMode(DisplayRefreshRateMode newValue) => setAndNotify(displayRefreshRateModeKey, newValue.toString());
AvesThemeBrightness get themeBrightness => getEnumOrDefault(themeBrightnessKey, SettingsDefaults.themeBrightness, AvesThemeBrightness.values);
set themeBrightness(AvesThemeBrightness newValue) => setAndNotify(themeBrightnessKey, newValue.toString());
AvesThemeColorMode get themeColorMode => getEnumOrDefault(themeColorModeKey, SettingsDefaults.themeColorMode, AvesThemeColorMode.values);
set themeColorMode(AvesThemeColorMode newValue) => setAndNotify(themeColorModeKey, newValue.toString());
String get catalogTimeZone => getString(catalogTimeZoneKey) ?? ''; String get catalogTimeZone => getString(catalogTimeZoneKey) ?? '';
set catalogTimeZone(String newValue) => setAndNotify(catalogTimeZoneKey, newValue); set catalogTimeZone(String newValue) => setAndNotify(catalogTimeZoneKey, newValue);
@ -281,6 +272,28 @@ class Settings extends ChangeNotifier {
set topEntryIds(List<int>? newValue) => setAndNotify(topEntryIdsKey, newValue?.map((id) => id.toString()).whereNotNull().toList()); set topEntryIds(List<int>? newValue) => setAndNotify(topEntryIdsKey, newValue?.map((id) => id.toString()).whereNotNull().toList());
// display
DisplayRefreshRateMode get displayRefreshRateMode => getEnumOrDefault(displayRefreshRateModeKey, SettingsDefaults.displayRefreshRateMode, DisplayRefreshRateMode.values);
set displayRefreshRateMode(DisplayRefreshRateMode newValue) => setAndNotify(displayRefreshRateModeKey, newValue.toString());
AvesThemeBrightness get themeBrightness => getEnumOrDefault(themeBrightnessKey, SettingsDefaults.themeBrightness, AvesThemeBrightness.values);
set themeBrightness(AvesThemeBrightness newValue) => setAndNotify(themeBrightnessKey, newValue.toString());
AvesThemeColorMode get themeColorMode => getEnumOrDefault(themeColorModeKey, SettingsDefaults.themeColorMode, AvesThemeColorMode.values);
set themeColorMode(AvesThemeColorMode newValue) => setAndNotify(themeColorModeKey, newValue.toString());
bool get enableDynamicColor => getBoolOrDefault(enableDynamicColorKey, SettingsDefaults.enableDynamicColor);
set enableDynamicColor(bool newValue) => setAndNotify(enableDynamicColorKey, newValue);
bool get enableBlurEffect => getBoolOrDefault(enableBlurEffectKey, SettingsDefaults.enableBlurEffect);
set enableBlurEffect(bool newValue) => setAndNotify(enableBlurEffectKey, newValue);
// navigation // navigation
bool get mustBackTwiceToExit => getBoolOrDefault(mustBackTwiceToExitKey, SettingsDefaults.mustBackTwiceToExit); bool get mustBackTwiceToExit => getBoolOrDefault(mustBackTwiceToExitKey, SettingsDefaults.mustBackTwiceToExit);
@ -441,10 +454,6 @@ class Settings extends ChangeNotifier {
set showOverlayThumbnailPreview(bool newValue) => setAndNotify(showOverlayThumbnailPreviewKey, newValue); set showOverlayThumbnailPreview(bool newValue) => setAndNotify(showOverlayThumbnailPreviewKey, newValue);
bool get enableOverlayBlurEffect => getBoolOrDefault(enableOverlayBlurEffectKey, SettingsDefaults.enableOverlayBlurEffect);
set enableOverlayBlurEffect(bool newValue) => setAndNotify(enableOverlayBlurEffectKey, newValue);
bool get viewerUseCutout => getBoolOrDefault(viewerUseCutoutKey, SettingsDefaults.viewerUseCutout); bool get viewerUseCutout => getBoolOrDefault(viewerUseCutoutKey, SettingsDefaults.viewerUseCutout);
set viewerUseCutout(bool newValue) => setAndNotify(viewerUseCutoutKey, newValue); set viewerUseCutout(bool newValue) => setAndNotify(viewerUseCutoutKey, newValue);
@ -695,6 +704,8 @@ class Settings extends ChangeNotifier {
break; break;
case isInstalledAppAccessAllowedKey: case isInstalledAppAccessAllowedKey:
case isErrorReportingAllowedKey: case isErrorReportingAllowedKey:
case enableDynamicColorKey:
case enableBlurEffectKey:
case showBottomNavigationBarKey: case showBottomNavigationBarKey:
case mustBackTwiceToExitKey: case mustBackTwiceToExitKey:
case confirmDeleteForeverKey: case confirmDeleteForeverKey:
@ -713,7 +724,6 @@ class Settings extends ChangeNotifier {
case showOverlayInfoKey: case showOverlayInfoKey:
case showOverlayShootingDetailsKey: case showOverlayShootingDetailsKey:
case showOverlayThumbnailPreviewKey: case showOverlayThumbnailPreviewKey:
case enableOverlayBlurEffectKey:
case viewerUseCutoutKey: case viewerUseCutoutKey:
case viewerMaxBrightnessKey: case viewerMaxBrightnessKey:
case enableMotionPhotoAutoPlayKey: case enableMotionPhotoAutoPlayKey:

View file

@ -7,7 +7,7 @@ import 'package:flutter/services.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class Themes { class Themes {
static const _accentColor = Colors.indigoAccent; static const defaultAccent = Colors.indigoAccent;
static const _tooltipTheme = TooltipThemeData( static const _tooltipTheme = TooltipThemeData(
verticalOffset: 32, verticalOffset: 32,
@ -19,8 +19,8 @@ class Themes {
fontFeatures: [FontFeature.enable('smcp')], fontFeatures: [FontFeature.enable('smcp')],
); );
static const _snackBarTheme = SnackBarThemeData( static SnackBarThemeData _snackBarTheme(Color accentColor) => SnackBarThemeData(
actionTextColor: _accentColor, actionTextColor: accentColor,
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
); );
@ -35,10 +35,10 @@ class Themes {
static const _lightSecondLayer = Color(0xFFF5F5F5); // aka `Colors.grey[100]` static const _lightSecondLayer = Color(0xFFF5F5F5); // aka `Colors.grey[100]`
static const _lightThirdLayer = Color(0xFFEEEEEE); // aka `Colors.grey[200]` static const _lightThirdLayer = Color(0xFFEEEEEE); // aka `Colors.grey[200]`
static final lightTheme = ThemeData( static ThemeData lightTheme(Color accentColor) => ThemeData(
colorScheme: ColorScheme.light( colorScheme: ColorScheme.light(
primary: _accentColor, primary: accentColor,
secondary: _accentColor, secondary: accentColor,
onPrimary: _lightBodyColor, onPrimary: _lightBodyColor,
onSecondary: _lightBodyColor, onSecondary: _lightBodyColor,
), ),
@ -49,8 +49,8 @@ class Themes {
// `cardColor` is used by `ExpansionPanel` // `cardColor` is used by `ExpansionPanel`
cardColor: _lightSecondLayer, cardColor: _lightSecondLayer,
dialogBackgroundColor: _lightSecondLayer, dialogBackgroundColor: _lightSecondLayer,
indicatorColor: _accentColor, indicatorColor: accentColor,
toggleableActiveColor: _accentColor, toggleableActiveColor: accentColor,
typography: _typography, typography: _typography,
appBarTheme: AppBarTheme( appBarTheme: AppBarTheme(
backgroundColor: _lightFirstLayer, backgroundColor: _lightFirstLayer,
@ -66,7 +66,7 @@ class Themes {
popupMenuTheme: const PopupMenuThemeData( popupMenuTheme: const PopupMenuThemeData(
color: _lightSecondLayer, color: _lightSecondLayer,
), ),
snackBarTheme: _snackBarTheme, snackBarTheme: _snackBarTheme(accentColor),
tabBarTheme: TabBarTheme( tabBarTheme: TabBarTheme(
labelColor: _lightTitleColor, labelColor: _lightTitleColor,
unselectedLabelColor: Colors.black54, unselectedLabelColor: Colors.black54,
@ -87,10 +87,10 @@ class Themes {
static const _darkSecondLayer = Color(0xFF363636); static const _darkSecondLayer = Color(0xFF363636);
static const _darkThirdLayer = Color(0xFF424242); // aka `Colors.grey[800]` static const _darkThirdLayer = Color(0xFF424242); // aka `Colors.grey[800]`
static final darkTheme = ThemeData( static ThemeData darkTheme(Color accentColor) => ThemeData(
colorScheme: ColorScheme.dark( colorScheme: ColorScheme.dark(
primary: _accentColor, primary: accentColor,
secondary: _accentColor, secondary: accentColor,
// surface color is used by the date/time pickers // surface color is used by the date/time pickers
surface: Colors.grey.shade800, surface: Colors.grey.shade800,
onPrimary: _darkBodyColor, onPrimary: _darkBodyColor,
@ -103,8 +103,8 @@ class Themes {
// `cardColor` is used by `ExpansionPanel` // `cardColor` is used by `ExpansionPanel`
cardColor: _darkSecondLayer, cardColor: _darkSecondLayer,
dialogBackgroundColor: _darkSecondLayer, dialogBackgroundColor: _darkSecondLayer,
indicatorColor: _accentColor, indicatorColor: accentColor,
toggleableActiveColor: _accentColor, toggleableActiveColor: accentColor,
typography: _typography, typography: _typography,
appBarTheme: AppBarTheme( appBarTheme: AppBarTheme(
backgroundColor: _darkFirstLayer, backgroundColor: _darkFirstLayer,
@ -117,7 +117,7 @@ class Themes {
popupMenuTheme: const PopupMenuThemeData( popupMenuTheme: const PopupMenuThemeData(
color: _darkSecondLayer, color: _darkSecondLayer,
), ),
snackBarTheme: _snackBarTheme.copyWith( snackBarTheme: _snackBarTheme(accentColor).copyWith(
backgroundColor: Colors.grey.shade800, backgroundColor: Colors.grey.shade800,
contentTextStyle: TextStyle( contentTextStyle: TextStyle(
color: _darkBodyColor, color: _darkBodyColor,
@ -138,20 +138,23 @@ class Themes {
static const _blackSecondLayer = Color(0xFF212121); // aka `Colors.grey[900]` static const _blackSecondLayer = Color(0xFF212121); // aka `Colors.grey[900]`
static const _blackThirdLayer = Color(0xFF303030); // aka `Colors.grey[850]` static const _blackThirdLayer = Color(0xFF303030); // aka `Colors.grey[850]`
static final blackTheme = darkTheme.copyWith( static ThemeData blackTheme(Color accentColor) {
final baseTheme = darkTheme(accentColor);
return baseTheme.copyWith(
// `canvasColor` is used by `Drawer`, `DropdownButton` and `ExpansionTileCard` // `canvasColor` is used by `Drawer`, `DropdownButton` and `ExpansionTileCard`
canvasColor: _blackSecondLayer, canvasColor: _blackSecondLayer,
scaffoldBackgroundColor: _blackFirstLayer, scaffoldBackgroundColor: _blackFirstLayer,
// `cardColor` is used by `ExpansionPanel` // `cardColor` is used by `ExpansionPanel`
cardColor: _blackSecondLayer, cardColor: _blackSecondLayer,
dialogBackgroundColor: _blackSecondLayer, dialogBackgroundColor: _blackSecondLayer,
appBarTheme: darkTheme.appBarTheme.copyWith( appBarTheme: baseTheme.appBarTheme.copyWith(
backgroundColor: _blackFirstLayer, backgroundColor: _blackFirstLayer,
), ),
popupMenuTheme: darkTheme.popupMenuTheme.copyWith( popupMenuTheme: baseTheme.popupMenuTheme.copyWith(
color: _blackSecondLayer, color: _blackSecondLayer,
), ),
); );
}
static Color overlayBackgroundColor({ static Color overlayBackgroundColor({
required Brightness brightness, required Brightness brightness,

View file

@ -108,6 +108,11 @@ class Constants {
licenseUrl: 'https://github.com/fluttercommunity/plus_plugins/blob/main/packages/device_info_plus/device_info_plus/LICENSE', licenseUrl: 'https://github.com/fluttercommunity/plus_plugins/blob/main/packages/device_info_plus/device_info_plus/LICENSE',
sourceUrl: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/device_info_plus', sourceUrl: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/device_info_plus',
), ),
Dependency(
name: 'Dynamic Color',
license: 'BSD 3-Clause',
sourceUrl: 'https://github.com/material-foundation/material-dynamic-color-flutter',
),
Dependency( Dependency(
name: 'fijkplayer (Aves fork)', name: 'fijkplayer (Aves fork)',
license: 'MIT', license: 'MIT',
@ -337,6 +342,12 @@ class Constants {
license: 'Apache 2.0', license: 'Apache 2.0',
sourceUrl: 'https://github.com/jifalops/dart-latlong', sourceUrl: 'https://github.com/jifalops/dart-latlong',
), ),
Dependency(
name: 'Material Color Utilities',
license: 'Apache 2.0',
licenseUrl: 'https://github.com/material-foundation/material-color-utilities/tree/main/dart/LICENSE',
sourceUrl: 'https://github.com/material-foundation/material-color-utilities/tree/main/dart',
),
Dependency( Dependency(
name: 'Path', name: 'Path',
license: 'BSD 3-Clause', license: 'BSD 3-Clause',

View file

@ -5,6 +5,7 @@ import 'package:aves/app_flavor.dart';
import 'package:aves/app_mode.dart'; import 'package:aves/app_mode.dart';
import 'package:aves/l10n/l10n.dart'; import 'package:aves/l10n/l10n.dart';
import 'package:aves/model/device.dart'; import 'package:aves/model/device.dart';
import 'package:aves/model/settings/defaults.dart';
import 'package:aves/model/settings/enums/accessibility_animations.dart'; import 'package:aves/model/settings/enums/accessibility_animations.dart';
import 'package:aves/model/settings/enums/display_refresh_rate_mode.dart'; import 'package:aves/model/settings/enums/display_refresh_rate_mode.dart';
import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/enums/enums.dart';
@ -30,11 +31,13 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/providers/highlight_info_provider.dart'; import 'package:aves/widgets/common/providers/highlight_info_provider.dart';
import 'package:aves/widgets/home_page.dart'; import 'package:aves/widgets/home_page.dart';
import 'package:aves/widgets/welcome_page.dart'; import 'package:aves/widgets/welcome_page.dart';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:fijkplayer/fijkplayer.dart'; import 'package:fijkplayer/fijkplayer.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:material_color_utilities/material_color_utilities.dart';
import 'package:overlay_support/overlay_support.dart'; import 'package:overlay_support/overlay_support.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
@ -70,6 +73,7 @@ class AvesApp extends StatefulWidget {
class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver { class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
final ValueNotifier<AppMode> appModeNotifier = ValueNotifier(AppMode.main); final ValueNotifier<AppMode> appModeNotifier = ValueNotifier(AppMode.main);
late Future<void> _appSetup; late Future<void> _appSetup;
late Future<CorePalette?> _dynamicColorPaletteLoader;
final _mediaStoreSource = MediaStoreSource(); final _mediaStoreSource = MediaStoreSource();
final Debouncer _mediaStoreChangeDebouncer = Debouncer(delay: Durations.mediaContentChangeDebounceDelay); final Debouncer _mediaStoreChangeDebouncer = Debouncer(delay: Durations.mediaContentChangeDebounceDelay);
final Set<String> changedUris = {}; final Set<String> changedUris = {};
@ -89,6 +93,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
super.initState(); super.initState();
EquatableConfig.stringify = true; EquatableConfig.stringify = true;
_appSetup = _setup(); _appSetup = _setup();
_dynamicColorPaletteLoader = DynamicColorPlugin.getCorePalette();
_mediaStoreChangeChannel.receiveBroadcastStream().listen((event) => _onMediaStoreChange(event as String?)); _mediaStoreChangeChannel.receiveBroadcastStream().listen((event) => _onMediaStoreChange(event as String?));
_newIntentChannel.receiveBroadcastStream().listen((event) => _onNewIntent(event as Map?)); _newIntentChannel.receiveBroadcastStream().listen((event) => _onNewIntent(event as Map?));
_analysisCompletionChannel.receiveBroadcastStream().listen((event) => _onAnalysisCompletion()); _analysisCompletionChannel.receiveBroadcastStream().listen((event) => _onAnalysisCompletion());
@ -120,16 +125,18 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
: Scaffold( : Scaffold(
body: snapshot.hasError ? _buildError(snapshot.error!) : const SizedBox(), body: snapshot.hasError ? _buildError(snapshot.error!) : const SizedBox(),
); );
return Selector<Settings, Tuple3<Locale?, bool, AvesThemeBrightness>>( return Selector<Settings, Tuple4<Locale?, bool, AvesThemeBrightness, bool>>(
selector: (context, s) => Tuple3( selector: (context, s) => Tuple4(
s.locale, s.locale,
s.initialized ? s.accessibilityAnimations.animate : true, s.initialized ? s.accessibilityAnimations.animate : true,
s.initialized ? s.themeBrightness : AvesThemeBrightness.system, s.initialized ? s.themeBrightness : SettingsDefaults.themeBrightness,
s.initialized ? s.enableDynamicColor : SettingsDefaults.enableDynamicColor,
), ),
builder: (context, s, child) { builder: (context, s, child) {
final settingsLocale = s.item1; final settingsLocale = s.item1;
final areAnimationsEnabled = s.item2; final areAnimationsEnabled = s.item2;
final themeBrightness = s.item3; final themeBrightness = s.item3;
final enableDynamicColor = s.item4;
final pageTransitionsTheme = areAnimationsEnabled final pageTransitionsTheme = areAnimationsEnabled
// Flutter has various page transition implementations for Android: // Flutter has various page transition implementations for Android:
@ -144,6 +151,19 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
// strip page transitions used by `MaterialPageRoute` // strip page transitions used by `MaterialPageRoute`
: const DirectPageTransitionsTheme(); : const DirectPageTransitionsTheme();
return FutureBuilder<CorePalette?>(
future: _dynamicColorPaletteLoader,
builder: (context, snapshot) {
const defaultAccent = Themes.defaultAccent;
Color lightAccent = defaultAccent, darkAccent = defaultAccent;
if (enableDynamicColor) {
// `DynamicColorBuilder` from package `dynamic_color` provides light/dark
// palettes with a primary color from tones too dark/light (40/80),
// so we derive the color with adjusted tones (60/70)
final tonalPalette = snapshot.data?.primary;
lightAccent = Color(tonalPalette?.get(60) ?? defaultAccent.value);
darkAccent = Color(tonalPalette?.get(70) ?? defaultAccent.value);
}
return MaterialApp( return MaterialApp(
navigatorKey: AvesApp.navigatorKey, navigatorKey: AvesApp.navigatorKey,
home: home, home: home,
@ -157,8 +177,8 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
), ),
), ),
onGenerateTitle: (context) => context.l10n.appName, onGenerateTitle: (context) => context.l10n.appName,
theme: Themes.lightTheme, theme: Themes.lightTheme(lightAccent),
darkTheme: themeBrightness == AvesThemeBrightness.black ? Themes.blackTheme : Themes.darkTheme, darkTheme: themeBrightness == AvesThemeBrightness.black ? Themes.blackTheme(darkAccent) : Themes.darkTheme(darkAccent),
themeMode: themeBrightness.appThemeMode, themeMode: themeBrightness.appThemeMode,
locale: settingsLocale, locale: settingsLocale,
localizationsDelegates: AppLocalizations.localizationsDelegates, localizationsDelegates: AppLocalizations.localizationsDelegates,
@ -169,6 +189,8 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
}, },
); );
}, },
);
},
), ),
), ),
), ),

View file

@ -206,7 +206,7 @@ class _AvesFloatingBarState extends State<AvesFloatingBar> with RouteAware {
return ValueListenableBuilder<bool>( return ValueListenableBuilder<bool>(
valueListenable: _isBlurAllowedNotifier, valueListenable: _isBlurAllowedNotifier,
builder: (context, isBlurAllowed, child) { builder: (context, isBlurAllowed, child) {
final blurred = isBlurAllowed && context.select<Settings, bool>((s) => s.enableOverlayBlurEffect); final blurred = isBlurAllowed && context.select<Settings, bool>((s) => s.enableBlurEffect);
return Container( return Container(
foregroundDecoration: BoxDecoration( foregroundDecoration: BoxDecoration(
border: Border.all( border: Border.all(

View file

@ -20,7 +20,7 @@ class EmptyContent extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
const color = Colors.blueGrey; final color = Theme.of(context).colorScheme.secondary.withOpacity(.5);
return Padding( return Padding(
padding: safeBottom padding: safeBottom
? EdgeInsets.only( ? EdgeInsets.only(

View file

@ -20,7 +20,7 @@ class MapOverlayButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final blurred = settings.enableOverlayBlurEffect; final blurred = settings.enableBlurEffect;
return Selector<MapThemeData, Animation<double>>( return Selector<MapThemeData, Animation<double>>(
selector: (context, v) => v.scale, selector: (context, v) => v.scale,
builder: (context, scale, child) => ScaleTransition( builder: (context, scale, child) => ScaleTransition(

View file

@ -57,7 +57,7 @@ class _OverlayCoordinateFilterChipState extends State<OverlayCoordinateFilterChi
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final blurred = settings.enableOverlayBlurEffect; final blurred = settings.enableBlurEffect;
final theme = Theme.of(context); final theme = Theme.of(context);
return Theme( return Theme(
data: theme.copyWith( data: theme.copyWith(

View file

@ -129,7 +129,7 @@ class _AppDrawerState extends State<AppDrawer> {
spacing: 16, spacing: 16,
crossAxisAlignment: WrapCrossAlignment.center, crossAxisAlignment: WrapCrossAlignment.center,
children: [ children: [
const AvesLogo(size: 64), const AvesLogo(size: 56),
Text( Text(
context.l10n.appName, context.l10n.appName,
style: const TextStyle( style: const TextStyle(

View file

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:aves/model/device.dart';
import 'package:aves/model/settings/enums/display_refresh_rate_mode.dart'; import 'package:aves/model/settings/enums/display_refresh_rate_mode.dart';
import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/enums/enums.dart';
import 'package:aves/model/settings/enums/theme_brightness.dart'; import 'package:aves/model/settings/enums/theme_brightness.dart';
@ -30,8 +31,9 @@ class DisplaySection extends SettingsSection {
FutureOr<List<SettingsTile>> tiles(BuildContext context) => [ FutureOr<List<SettingsTile>> tiles(BuildContext context) => [
SettingsTileDisplayThemeBrightness(), SettingsTileDisplayThemeBrightness(),
SettingsTileDisplayThemeColorMode(), SettingsTileDisplayThemeColorMode(),
SettingsTileDisplayDisplayRefreshRateMode(), if (device.isDynamicColorAvailable) SettingsTileDisplayEnableDynamicColor(),
SettingsTileDisplayEnableBlurEffect(), SettingsTileDisplayEnableBlurEffect(),
SettingsTileDisplayDisplayRefreshRateMode(),
]; ];
} }
@ -62,6 +64,30 @@ class SettingsTileDisplayThemeColorMode extends SettingsTile {
); );
} }
class SettingsTileDisplayEnableDynamicColor extends SettingsTile {
@override
String title(BuildContext context) => context.l10n.settingsThemeEnableDynamicColor;
@override
Widget build(BuildContext context) => SettingsSwitchListTile(
selector: (context, s) => s.enableDynamicColor,
onChanged: (v) => settings.enableDynamicColor = v,
title: title(context),
);
}
class SettingsTileDisplayEnableBlurEffect extends SettingsTile {
@override
String title(BuildContext context) => context.l10n.settingsViewerEnableOverlayBlurEffect;
@override
Widget build(BuildContext context) => SettingsSwitchListTile(
selector: (context, s) => s.enableBlurEffect,
onChanged: (v) => settings.enableBlurEffect = v,
title: title(context),
);
}
class SettingsTileDisplayDisplayRefreshRateMode extends SettingsTile { class SettingsTileDisplayDisplayRefreshRateMode extends SettingsTile {
@override @override
String title(BuildContext context) => context.l10n.settingsDisplayRefreshRateModeTile; String title(BuildContext context) => context.l10n.settingsDisplayRefreshRateModeTile;
@ -76,15 +102,3 @@ class SettingsTileDisplayDisplayRefreshRateMode extends SettingsTile {
dialogTitle: context.l10n.settingsDisplayRefreshRateModeTitle, dialogTitle: context.l10n.settingsDisplayRefreshRateModeTitle,
); );
} }
class SettingsTileDisplayEnableBlurEffect extends SettingsTile {
@override
String title(BuildContext context) => context.l10n.settingsViewerEnableOverlayBlurEffect;
@override
Widget build(BuildContext context) => SettingsSwitchListTile(
selector: (context, s) => s.enableOverlayBlurEffect,
onChanged: (v) => settings.enableOverlayBlurEffect = v,
title: title(context),
);
}

View file

@ -19,7 +19,7 @@ class OverlayButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final brightness = Theme.of(context).brightness; final brightness = Theme.of(context).brightness;
final blurred = settings.enableOverlayBlurEffect; final blurred = settings.enableBlurEffect;
return ScaleTransition( return ScaleTransition(
scale: scale, scale: scale,
child: borderRadius != null child: borderRadius != null
@ -77,7 +77,7 @@ class OverlayTextButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final blurred = settings.enableOverlayBlurEffect; final blurred = settings.enableBlurEffect;
final theme = Theme.of(context); final theme = Theme.of(context);
return SizeTransition( return SizeTransition(
sizeFactor: scale, sizeFactor: scale,

View file

@ -42,7 +42,7 @@ class ViewerTopOverlay extends StatelessWidget {
final viewStateConductor = context.read<ViewStateConductor>(); final viewStateConductor = context.read<ViewStateConductor>();
final viewStateNotifier = viewStateConductor.getOrCreateController(pageEntry); final viewStateNotifier = viewStateConductor.getOrCreateController(pageEntry);
final blurred = settings.enableOverlayBlurEffect; final blurred = settings.enableBlurEffect;
final viewInsetsPadding = (viewInsets ?? EdgeInsets.zero) + (viewPadding ?? EdgeInsets.zero); final viewInsetsPadding = (viewInsets ?? EdgeInsets.zero) + (viewPadding ?? EdgeInsets.zero);
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,

View file

@ -39,7 +39,7 @@ class _VideoProgressBarState extends State<VideoProgressBar> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final blurred = settings.enableOverlayBlurEffect; final blurred = settings.enableBlurEffect;
final brightness = Theme.of(context).brightness; final brightness = Theme.of(context).brightness;
final textStyle = TextStyle( final textStyle = TextStyle(
shadows: brightness == Brightness.dark ? Constants.embossShadows : null, shadows: brightness == Brightness.dark ? Constants.embossShadows : null,

View file

@ -260,6 +260,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.1" version: "2.1.1"
dynamic_color:
dependency: "direct main"
description:
name: dynamic_color
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
equatable: equatable:
dependency: "direct main" dependency: "direct main"
description: description:
@ -589,7 +596,7 @@ packages:
source: hosted source: hosted
version: "0.12.11" version: "0.12.11"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: "direct main"
description: description:
name: material_color_utilities name: material_color_utilities
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"

View file

@ -37,6 +37,7 @@ dependencies:
country_code: country_code:
decorated_icon: decorated_icon:
device_info_plus: device_info_plus:
dynamic_color:
equatable: equatable:
event_bus: event_bus:
expansion_tile_card: expansion_tile_card:
@ -56,6 +57,7 @@ dependencies:
get_it: get_it:
intl: intl:
latlong2: latlong2:
material_color_utilities:
material_design_icons_flutter: material_design_icons_flutter:
overlay_support: overlay_support:
package_info_plus: package_info_plus:

View file

@ -41,7 +41,7 @@ Future<void> configureAndLaunch() async {
..showOverlayInfo = true ..showOverlayInfo = true
..showOverlayShootingDetails = false ..showOverlayShootingDetails = false
..showOverlayThumbnailPreview = false ..showOverlayThumbnailPreview = false
..enableOverlayBlurEffect = true ..enableBlurEffect = true
..viewerUseCutout = true ..viewerUseCutout = true
// info // info
..infoMapStyle = EntryMapStyle.stamenWatercolor ..infoMapStyle = EntryMapStyle.stamenWatercolor

View file

@ -29,7 +29,7 @@ Future<void> configureAndLaunch() async {
..showOverlayInfo = true ..showOverlayInfo = true
..showOverlayShootingDetails = true ..showOverlayShootingDetails = true
..showOverlayThumbnailPreview = true ..showOverlayThumbnailPreview = true
..enableOverlayBlurEffect = true ..enableBlurEffect = true
..imageBackground = EntryBackground.checkered ..imageBackground = EntryBackground.checkered
// info // info
..infoMapStyle = EntryMapStyle.googleNormal; ..infoMapStyle = EntryMapStyle.googleNormal;

View file

@ -1,6 +1,43 @@
{ {
"de": [
"settingsThemeEnableDynamicColor"
],
"es": [ "es": [
"settingsShowBottomNavigationBar", "settingsShowBottomNavigationBar",
"settingsThumbnailShowTagIcon" "settingsThumbnailShowTagIcon",
"settingsThemeEnableDynamicColor"
],
"fr": [
"settingsThemeEnableDynamicColor"
],
"id": [
"settingsThemeEnableDynamicColor"
],
"it": [
"settingsThemeEnableDynamicColor"
],
"ja": [
"settingsThemeEnableDynamicColor"
],
"ko": [
"settingsThemeEnableDynamicColor"
],
"pt": [
"settingsThemeEnableDynamicColor"
],
"ru": [
"settingsThemeEnableDynamicColor"
],
"zh": [
"settingsThemeEnableDynamicColor"
] ]
} }