From d7d0a44e787de5823c6761f63a0b30ef973a2e6b Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 10 Sep 2024 09:50:17 +0100 Subject: [PATCH] fix: Top app bar icon color refactor: Add themeMode extensions refactor: Create theme service data class --- .../top_app_bar/extended_top_app_bar.dart | 53 +++++++------- lib/src/theme/color_extensions.dart | 15 ++++ lib/src/theme/theme_service.dart | 70 +++++++++++++------ lib/src/utils/zeta.dart | 12 +--- lib/src/utils/zeta_provider.dart | 29 ++++---- 5 files changed, 109 insertions(+), 70 deletions(-) diff --git a/lib/src/components/top_app_bar/extended_top_app_bar.dart b/lib/src/components/top_app_bar/extended_top_app_bar.dart index 67f4e543..9b07fa67 100644 --- a/lib/src/components/top_app_bar/extended_top_app_bar.dart +++ b/lib/src/components/top_app_bar/extended_top_app_bar.dart @@ -49,33 +49,36 @@ class ZetaExtendedAppBarDelegate extends SliverPersistentHeaderDelegate { constraints: BoxConstraints(minHeight: Zeta.of(context).spacing.xl_9, maxHeight: maxExtent), child: ColoredBox( color: Zeta.of(context).colors.surfaceDefault, - child: Stack( - children: [ - Positioned( - top: shrinks - ? (topMax + (-1 * shrinkOffset)).clamp( - topMin - - (searchController != null && searchController!.isEnabled - ? searchBarOffsetTop - : Zeta.of(context).spacing.none), - topMax, - ) - : topMax, - left: shrinks ? ((shrinkOffset / maxExtent) * _maxExtent).clamp(leftMin, leftMax) : leftMin, - right: searchController != null && searchController!.isEnabled - ? searchBarOffsetRight - : Zeta.of(context).spacing.none, - child: title, - ), - if (leading != null) - Positioned(top: Zeta.of(context).spacing.medium, left: Zeta.of(context).spacing.small, child: leading!), - if (actions != null) + child: IconTheme( + data: IconThemeData(color: Zeta.of(context).colors.mainDefault), + child: Stack( + children: [ Positioned( - top: Zeta.of(context).spacing.medium, - right: Zeta.of(context).spacing.small, - child: Row(children: actions!), + top: shrinks + ? (topMax + (-1 * shrinkOffset)).clamp( + topMin - + (searchController != null && searchController!.isEnabled + ? searchBarOffsetTop + : Zeta.of(context).spacing.none), + topMax, + ) + : topMax, + left: shrinks ? ((shrinkOffset / maxExtent) * _maxExtent).clamp(leftMin, leftMax) : leftMin, + right: searchController != null && searchController!.isEnabled + ? searchBarOffsetRight + : Zeta.of(context).spacing.none, + child: title, ), - ], + if (leading != null) + Positioned(top: Zeta.of(context).spacing.medium, left: Zeta.of(context).spacing.small, child: leading!), + if (actions != null) + Positioned( + top: Zeta.of(context).spacing.medium, + right: Zeta.of(context).spacing.small, + child: Row(children: actions!), + ), + ], + ), ), ), ); diff --git a/lib/src/theme/color_extensions.dart b/lib/src/theme/color_extensions.dart index e9986a58..69c1f046 100644 --- a/lib/src/theme/color_extensions.dart +++ b/lib/src/theme/color_extensions.dart @@ -1,4 +1,5 @@ import 'dart:math' as math; +import 'dart:ui'; import 'package:flutter/material.dart'; import '../../zeta_flutter.dart'; @@ -343,3 +344,17 @@ extension ZetaSemanticColorExtension on ZetaSemanticColors { onSurface: mainDefault, ); } + +/// Extensions on [ThemeMode] to provide additional functionality. +extension ZetaThemeModeExtension on ThemeMode { + /// Returns true if the theme mode is dark. + bool get isDark => + this == ThemeMode.system ? PlatformDispatcher.instance.platformBrightness.isDark : this == ThemeMode.dark; + + /// Returns the brightness value based on the theme mode. + Brightness get brightness => isDark ? Brightness.dark : Brightness.light; +} + +extension on Brightness { + bool get isDark => this == Brightness.dark; +} diff --git a/lib/src/theme/theme_service.dart b/lib/src/theme/theme_service.dart index 16b05c2e..a37f606f 100644 --- a/lib/src/theme/theme_service.dart +++ b/lib/src/theme/theme_service.dart @@ -6,6 +6,28 @@ import 'contrast.dart'; const String _kThemeMode = 'themeMode'; const String _kContrast = 'contrast'; const String _kThemeId = 'theme_id'; +const String _kFontFamily = 'fontFamily'; + +/// `ZetaThemeData` is a class that holds the theme data to be stored with the theme service. +class ZetaThemeServiceData { + /// Constructs a [ZetaThemeServiceData]. + /// + /// All fields are optional. If null, defaults will be used. + ZetaThemeServiceData({this.themeId, this.themeMode, this.contrast, this.fontFamily}); + + /// The unique identifier for the custom theme data. + final String? themeId; + + /// The theme mode of the Zeta theme. + final ThemeMode? themeMode; + + /// The contrast of the Zeta theme. + final ZetaContrast? contrast; + + /// The font family of the Zeta theme. + final String? fontFamily; +} + // TODO(colors): Re-add custom font somewhere (not here) // TODO(colors): Add tests /// `ZetaThemeService` is an abstract class. @@ -27,8 +49,8 @@ abstract class ZetaThemeService { /// /// `ZetaContrast` defines different contrast styles to use across the application. /// - /// Returns a Future `(ZetaThemeData?, ThemeMode?, ZetaContrast?)`. - Future<(ThemeMode?, ZetaContrast?, String?)> loadTheme(); + /// Returns a Future [ZetaThemeServiceData]. + Future loadTheme(); /// Saves the provided theme data as the application's theme. /// @@ -38,11 +60,7 @@ abstract class ZetaThemeService { /// /// Returns a `Future` that completes when the theme data has been successfully saved. - Future saveTheme({ - required ThemeMode themeMode, - required ZetaContrast contrast, - required String? themeId, - }); + Future saveTheme({required ZetaThemeServiceData themeData}); } /// A default implementation of `ZetaThemeService` that uses `SharedPreferences` to store the theme data. @@ -51,7 +69,7 @@ class ZetaDefaultThemeService extends ZetaThemeService { const ZetaDefaultThemeService(); @override - Future<(ThemeMode?, ZetaContrast?, String?)> loadTheme() async { + Future loadTheme() async { final preferences = await SharedPreferences.getInstance(); final modeString = preferences.getString(_kThemeMode); @@ -66,21 +84,33 @@ class ZetaDefaultThemeService extends ZetaThemeService { final contrast = contrastString == ZetaContrast.aaa.name ? ZetaContrast.aaa : ZetaContrast.aa; final themeId = preferences.getString(_kThemeId); - - return (themeMode, contrast, themeId); + final fontFamily = preferences.getString(_kFontFamily); + + return ZetaThemeServiceData( + themeMode: themeMode, + contrast: contrast, + themeId: themeId, + fontFamily: fontFamily, + ); } @override - Future saveTheme({ - required ThemeMode themeMode, - required ZetaContrast contrast, - required String? themeId, - }) async { + Future saveTheme({required ZetaThemeServiceData themeData}) async { final preferences = await SharedPreferences.getInstance(); - - await preferences.setString(_kThemeMode, themeMode.name); - await preferences.setString(_kContrast, contrast.name); - - if (themeId != null) await preferences.setString(_kThemeId, themeId); + final List> futures = []; + + if (themeData.fontFamily != null) { + futures.add(preferences.setString(_kFontFamily, themeData.fontFamily!)); + } + if (themeData.themeMode != null) { + futures.add(preferences.setString(_kThemeMode, themeData.themeMode!.name)); + } + if (themeData.contrast != null) { + futures.add(preferences.setString(_kContrast, themeData.contrast!.name)); + } + if (themeData.themeId != null) { + futures.add(preferences.setString(_kThemeId, themeData.themeId!)); + } + await Future.wait(futures); } } diff --git a/lib/src/utils/zeta.dart b/lib/src/utils/zeta.dart index 3d5d8cf4..89bd1616 100644 --- a/lib/src/utils/zeta.dart +++ b/lib/src/utils/zeta.dart @@ -1,6 +1,5 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; import '../../zeta_flutter.dart'; @@ -66,16 +65,7 @@ class Zeta extends InheritedWidget { /// If the theme mode is set to 'system', it will return the brightness that ties with the device's system theme setting. /// If the theme mode is set to 'light', it always returns `Brightness.light`. /// If neither, it returns `Brightness.dark` by default (i.e., when the theme mode is 'dark'). - Brightness get brightness { - if (themeMode == ThemeMode.system) { - return SchedulerBinding - .instance.platformDispatcher.platformBrightness; // Return the current system brightness setting - } else if (themeMode == ThemeMode.light) { - return Brightness.light; // Return the light mode brightness - } else { - return Brightness.dark; // Default: Return the dark mode brightness - } - } + Brightness get brightness => themeMode.brightness; /// Gets the radius values based on the tokens. ZetaRadiiSemantics get radius => semantics.radii; diff --git a/lib/src/utils/zeta_provider.dart b/lib/src/utils/zeta_provider.dart index f3fd1648..21f36295 100644 --- a/lib/src/utils/zeta_provider.dart +++ b/lib/src/utils/zeta_provider.dart @@ -172,15 +172,15 @@ class ZetaProviderState extends State with Diagnosticable, Widgets /// Retrieves the theme values from the shared preferences. Future getThemeValuesFromPreferences() async { - final (themeMode, contrast, themeId) = await widget.themeService.loadTheme(); + final ZetaThemeServiceData themeServiceData = await widget.themeService.loadTheme(); // Set the initial theme mode. - _themeMode = themeMode ?? widget.initialThemeMode ?? ThemeMode.system; + _themeMode = themeServiceData.themeMode ?? widget.initialThemeMode ?? ThemeMode.system; // Set the initial contrast. - _contrast = contrast ?? widget.initialContrast ?? ZetaContrast.aa; + _contrast = themeServiceData.contrast ?? widget.initialContrast ?? ZetaContrast.aa; - final loadedTheme = _customThemes[widget.initialTheme ?? themeId]; + final loadedTheme = _customThemes[themeServiceData.themeId ?? widget.initialTheme]; if (loadedTheme != null) { _customTheme = loadedTheme; @@ -319,9 +319,11 @@ class ZetaProviderState extends State with Diagnosticable, Widgets void _saveThemeChange() { unawaited( widget.themeService.saveTheme( - themeMode: _themeMode, - contrast: _contrast, - themeId: _customTheme?.id, + themeData: ZetaThemeServiceData( + themeMode: _themeMode, + contrast: _contrast, + themeId: _customTheme?.id, + ), ), ); } @@ -356,7 +358,6 @@ class _InternalProvider extends StatefulWidget { /// Represents the late initialization of the ZetaContrast value. final ZetaContrast contrast; - /// Represents the late initialization of the ThemeMode value. final ThemeMode themeMode; final bool rounded; @@ -389,14 +390,14 @@ class _InternalProviderState extends State<_InternalProvider> { themeMode: widget.themeMode, contrast: widget.contrast, rounded: widget.rounded, - customPrimitives: widget.themeMode == ThemeMode.light - ? ZetaPrimitivesLight( - primary: widget.customTheme?.primary, - secondary: widget.customTheme?.secondary, - ) - : ZetaPrimitivesDark( + customPrimitives: widget.themeMode.isDark + ? ZetaPrimitivesDark( primary: widget.customTheme?.primaryDark, secondary: widget.customTheme?.secondaryDark, + ) + : ZetaPrimitivesLight( + primary: widget.customTheme?.primary, + secondary: widget.customTheme?.secondary, ), child: Builder( builder: (context) {