From c7cffcfbc127bc63c20338ad81e02ce97b700269 Mon Sep 17 00:00:00 2001 From: Prashant Sawant <75819349+ps9310@users.noreply.github.com> Date: Fri, 1 Dec 2023 13:03:37 -0500 Subject: [PATCH] Refactor theme declaration and introduce theme service (#8) In theme_color_switch.dart, restructured the declaration of themes into a global constant 'appThemes', freeing it from a class field in ZetaThemeColorSwitch. This increases application efficiency and adheres to the DRY (Don't Repeat Yourself) principle. A new file 'theme_service.dart' was also added, serving as an abstract class for loading and saving theme data. Various typos in 'colors.dart' and 'zeta.dart' were fixed to ensure correct execution. Theme data loading and saving operations in 'main.dart' were also updated to use this new service. --- CHANGELOG.md | 4 ++ example/lib/main.dart | 36 +++++++++++++- example/lib/pages/theme_color_switch.dart | 46 +++++++++--------- example/lib/theme_service.dart | 47 +++++++++++++++++++ .../Flutter/GeneratedPluginRegistrant.swift | 12 ----- example/pubspec.yaml | 3 +- lib/src/theme/color_swatch.dart | 1 + lib/src/theme/colors.dart | 8 ++-- lib/src/theme/theme_service.dart | 26 +++++++--- lib/src/zeta.dart | 25 +++++----- lib/zeta_flutter.dart | 1 + pubspec.yaml | 2 +- 12 files changed, 149 insertions(+), 62 deletions(-) create mode 100644 example/lib/theme_service.dart delete mode 100644 example/macos/Flutter/GeneratedPluginRegistrant.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d554425..9a39bb59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [0.1.1+1] - 2023-12-01 + +- feature: Refactor theme declaration and introduce theme service + ## [0.1.0+1] - 2023-11-28 - chore: Tidy, reorganise and prepare repo diff --git a/example/lib/main.dart b/example/lib/main.dart index 19c69098..86099838 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,15 +1,47 @@ import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:zeta_example/theme_service.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; import 'home.dart'; -void main() => runApp(const ZetaExample()); +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + final preferences = await SharedPreferences.getInstance(); + final themeService = SharedPrefsThemeService(preferences); + final themPreferences = await themeService.loadTheme(); + + runApp( + ZetaExample( + themeService: themeService, + initialThemeData: themPreferences.$1 ?? ZetaThemeData(), + initialThemeMode: themPreferences.$2 ?? ThemeMode.system, + initialContrast: themPreferences.$3 ?? ZetaContrast.aa, + ), + ); +} class ZetaExample extends StatelessWidget { - const ZetaExample({super.key}); + const ZetaExample({ + super.key, + required this.themeService, + required this.initialContrast, + required this.initialThemeMode, + required this.initialThemeData, + }); + + final ZetaThemeService themeService; + final ZetaContrast initialContrast; + final ThemeMode initialThemeMode; + final ZetaThemeData initialThemeData; @override Widget build(BuildContext context) { return ZetaProvider( + themeService: themeService, + initialContrast: initialContrast, + initialThemeData: initialThemeData, + initialThemeMode: initialThemeMode, builder: (context, themeData, themeMode) { final dark = themeData.colorsDark.toScheme(); final light = themeData.colorsLight.toScheme(); diff --git a/example/lib/pages/theme_color_switch.dart b/example/lib/pages/theme_color_switch.dart index a2ca12fc..98ebbeec 100644 --- a/example/lib/pages/theme_color_switch.dart +++ b/example/lib/pages/theme_color_switch.dart @@ -1,29 +1,29 @@ import 'package:flutter/material.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; +late final appThemes = { + "default": ZetaThemeData(), + "teal": ZetaThemeData( + identifier: 'teal', + primary: ZetaColorBase.teal, + ), + "yellow": ZetaThemeData( + identifier: 'yellow', + primary: ZetaColorBase.yellow, + ), + "red": ZetaThemeData( + identifier: 'red', + primary: ZetaColorBase.red, + ), + "purple": ZetaThemeData( + identifier: 'purple', + primary: ZetaColorBase.purple, + ), +}; + class ZetaThemeColorSwitch extends StatelessWidget { ZetaThemeColorSwitch({super.key}); - late final _themes = { - "default": ZetaThemeData(), - "teal": ZetaThemeData( - identifier: 'teal', - primary: ZetaColorBase.teal, - ), - "yellow": ZetaThemeData( - identifier: 'yellow', - primary: ZetaColorBase.yellow, - ), - "red": ZetaThemeData( - identifier: 'red', - primary: ZetaColorBase.red, - ), - "purple": ZetaThemeData( - identifier: 'purple', - primary: ZetaColorBase.purple, - ), - }; - @override Widget build(BuildContext context) { var zeta = Zeta.of(context); @@ -44,8 +44,8 @@ class ZetaThemeColorSwitch extends StatelessWidget { alignment: Alignment.center, icon: SizedBox(width: 8), dropdownColor: zeta.colors.borderDisabled, - items: _themes.entries.map((e) { - var zetaColors = primary(_themes[e.key]!); + items: appThemes.entries.map((e) { + var zetaColors = primary(appThemes[e.key]!); var color = zetaColors.primary; return DropdownMenuItem( value: e.value.identifier, @@ -58,7 +58,7 @@ class ZetaThemeColorSwitch extends StatelessWidget { ); }).toList(), onChanged: (value) { - final theme = _themes[value]; + final theme = appThemes[value]; if (theme != null) { ZetaProvider.of(context).updateThemeData(theme); } diff --git a/example/lib/theme_service.dart b/example/lib/theme_service.dart new file mode 100644 index 00000000..b25c0a45 --- /dev/null +++ b/example/lib/theme_service.dart @@ -0,0 +1,47 @@ +import 'package:flutter/src/material/app.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:zeta_example/pages/theme_color_switch.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +class SharedPrefsThemeService extends ZetaThemeService { + SharedPrefsThemeService(this.preferences); + + final SharedPreferences preferences; + + @override + Future<(ZetaThemeData?, ThemeMode?, ZetaContrast?)> loadTheme() async { + final theme = preferences.getString('theme') ?? 'default'; + final themeData = appThemes[theme]; + + final modeString = preferences.getString('themeMode'); + final themeMode = modeString == 'light' + ? ThemeMode.light + : modeString == 'dark' + ? ThemeMode.dark + : ThemeMode.system; + + final contrastString = preferences.getString('contrast'); + final contrast = contrastString == 'aaa' ? ZetaContrast.aaa : ZetaContrast.aa; + + return (themeData, themeMode, contrast); + } + + @override + Future saveTheme({ + required ZetaThemeData themeData, + required ThemeMode themeMode, + required ZetaContrast contrast, + }) async { + await preferences.setString('theme', themeData.identifier); + + final modeString = themeMode == ThemeMode.light + ? 'light' + : themeMode == ThemeMode.dark + ? 'dark' + : 'system'; + await preferences.setString('themeMode', modeString); + + final contrastString = contrast == ZetaContrast.aaa ? 'aaa' : 'aa'; + await preferences.setString('contrast', contrastString); + } +} diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index e777c67d..00000000 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import path_provider_foundation - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) -} diff --git a/example/pubspec.yaml b/example/pubspec.yaml index f6c712a9..74d5c998 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -4,13 +4,14 @@ version: 0.0.1 publish_to: "none" environment: - sdk: ">=2.19.1 <3.0.0" + sdk: ">=3.0.1 <4.0.0" dependencies: flutter: sdk: flutter go_router: ^11.1.2 google_fonts: ^6.1.0 + shared_preferences: ^2.2.2 zeta_flutter: path: ../ diff --git a/lib/src/theme/color_swatch.dart b/lib/src/theme/color_swatch.dart index 5b8f8acd..405a0440 100644 --- a/lib/src/theme/color_swatch.dart +++ b/lib/src/theme/color_swatch.dart @@ -27,6 +27,7 @@ class ZetaColorSwatch extends ColorSwatch { /// /// It ensures that the 60th and 80th shades from swatch are abide by the AA and AAA accessibility standards on [background], respectively. /// [background] color defaults to [ZetaColorBase.greyWarm] shade10. + /// {@endtemplate} factory ZetaColorSwatch.fromColor( Color primary, { Brightness brightness = Brightness.light, diff --git a/lib/src/theme/colors.dart b/lib/src/theme/colors.dart index c54f270f..9abdc4f3 100644 --- a/lib/src/theme/colors.dart +++ b/lib/src/theme/colors.dart @@ -218,13 +218,13 @@ class ZetaColors { // Text / icons. + /// {@template zeta-color-dark} /// Default text /icon color. /// /// Defaults to `ZetaColors.cool.90`. /// - /// {@template zeta-color-dark} /// Color swatches are inverted if [ZetaColors.brightness] is Dark. - /// {@endTemplate} + /// {@endtemplate} Color get textDefault => cool.shade90; /// Subtle text /icon color. @@ -252,13 +252,13 @@ class ZetaColors { /// {@macro zeta-color-dark} Color get textInverse => cool.shade20; + /// {@template zeta-color-dark} /// Default icon color. /// /// Defaults to `ZetaColors.cool.90`. /// - /// {@template zeta-color-dark} /// Color swatches are inverted if [ZetaColors.brightness] is Dark. - /// {@endTemplate} + /// {@endtemplate} Color get iconDefault => textDefault; /// Subtle icon color. diff --git a/lib/src/theme/theme_service.dart b/lib/src/theme/theme_service.dart index bbc63ee5..84b243ad 100644 --- a/lib/src/theme/theme_service.dart +++ b/lib/src/theme/theme_service.dart @@ -1,17 +1,25 @@ +import 'package:flutter/material.dart'; + +import 'contrast.dart'; import 'theme_data.dart'; /// `ZetaThemeService` is an abstract class. /// It provides the structure for loading and saving themes in Zeta application. abstract class ZetaThemeService { - /// Loads the application's theme. + /// Asynchronously load the theme data. /// - /// `loadTheme` is a method to retrieve the current theme data. + /// This method returns a `Future` that when complete will produce a + /// tuple of `ZetaThemeData`, `ThemeMode`, and `ZetaContrast`. /// - /// Returns a `Future` that completes with the `ZetaThemeData` object - /// that represents the current theme. - - Future loadTheme(); + /// `ZetaThemeData` describes the colors that are used by a theme. + /// + /// `ThemeMode` determines the brightness of the system. + /// + /// `ZetaContrast` defines different contrast styles to use across the application. + /// + /// Returns a Future `(ZetaThemeData?, ThemeMode?, ZetaContrast?)`. + Future<(ZetaThemeData?, ThemeMode?, ZetaContrast?)> loadTheme(); /// Saves the provided theme data as the application's theme. /// @@ -21,5 +29,9 @@ abstract class ZetaThemeService { /// /// Returns a `Future` that completes when the theme data has been successfully saved. - Future saveTheme(ZetaThemeData themeData); + Future saveTheme({ + required ZetaThemeData themeData, + required ThemeMode themeMode, + required ZetaContrast contrast, + }); } diff --git a/lib/src/zeta.dart b/lib/src/zeta.dart index cf8a7c42..e28de7e3 100644 --- a/lib/src/zeta.dart +++ b/lib/src/zeta.dart @@ -248,17 +248,6 @@ class ZetaProviderState extends State with Diagnosticable, Widgets // Apply the initial contrast to the theme data. _themeData = widget.initialThemeData.apply(contrast: _contrast); - - // Load theme data from themeService if available. - unawaited( - widget.themeService?.loadTheme().then((value) { - if (value != null) { - setState(() { - _themeData = value; - }); - } - }), - ); } /// Clean up function to be called when this object is removed from the tree. @@ -332,6 +321,7 @@ class ZetaProviderState extends State with Diagnosticable, Widgets void updateThemeMode(ThemeMode themeMode) { setState(() { _themeMode = themeMode; + _saveThemeChange(); }); } @@ -339,7 +329,7 @@ class ZetaProviderState extends State with Diagnosticable, Widgets void updateThemeData(ZetaThemeData data) { setState(() { _themeData = data.apply(contrast: _contrast); - unawaited(widget.themeService?.saveTheme(data)); + _saveThemeChange(); }); } @@ -348,9 +338,20 @@ class ZetaProviderState extends State with Diagnosticable, Widgets setState(() { _contrast = contrast; _themeData = _themeData.apply(contrast: contrast); + _saveThemeChange(); }); } + void _saveThemeChange() { + unawaited( + widget.themeService?.saveTheme( + themeData: _themeData, + themeMode: _themeMode, + contrast: _contrast, + ), + ); + } + @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); diff --git a/lib/zeta_flutter.dart b/lib/zeta_flutter.dart index eecb1b7a..576d2887 100644 --- a/lib/zeta_flutter.dart +++ b/lib/zeta_flutter.dart @@ -10,6 +10,7 @@ export 'src/theme/color_swatch.dart'; export 'src/theme/constants.dart'; export 'src/theme/contrast.dart'; export 'src/theme/theme_data.dart'; +export 'src/theme/theme_service.dart'; export 'src/tokens.dart'; export 'src/utils/extensions.dart'; export 'src/zeta.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 564d0e43..4a8e8fba 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: zeta_flutter -version: 0.1.0+1 +version: 0.1.1+1 description: Zeta is the new, formal, standardized Zebra Design System based off the successes of ZDS (Zebra Design System). This package is in pre-release, and so many aspects are incomplete. homepage: https://github.com/zebradevs/zeta_flutter repository: https://github.com/zebradevs/zeta_flutter