diff --git a/.config/dictionaries/project.dic b/.config/dictionaries/project.dic index df6ca5a776..f33072e0db 100644 --- a/.config/dictionaries/project.dic +++ b/.config/dictionaries/project.dic @@ -293,3 +293,4 @@ xctestrun xcworkspace xvfb yoroi +appbar \ No newline at end of file diff --git a/catalyst_voices/lib/app/view/app_content.dart b/catalyst_voices/lib/app/view/app_content.dart index e4ebf8a169..fc8160e07f 100644 --- a/catalyst_voices/lib/app/view/app_content.dart +++ b/catalyst_voices/lib/app/view/app_content.dart @@ -6,7 +6,7 @@ import 'package:flutter_localized_locales/flutter_localized_locales.dart'; const _restorationScopeId = 'rootVoices'; -final class AppContent extends StatelessWidget { +final class AppContent extends StatefulWidget { final RouterConfig routerConfig; const AppContent({ @@ -14,11 +14,22 @@ final class AppContent extends StatelessWidget { required this.routerConfig, }); - List> get _localizationsDelegates { - return const [ - ...VoicesLocalizations.localizationsDelegates, - LocaleNamesLocalizationsDelegate(), - ]; + @override + State createState() => AppContentState(); + + /// Returns the state associated with the [AppContent]. + static AppContentState of(BuildContext context) { + return context.findAncestorStateOfType()!; + } +} + +class AppContentState extends State { + ThemeMode _themeMode = ThemeMode.light; + + void updateThemeMode(ThemeMode themeMode) { + setState(() { + _themeMode = themeMode; + }); } @override @@ -28,9 +39,8 @@ final class AppContent extends StatelessWidget { localizationsDelegates: _localizationsDelegates, supportedLocales: VoicesLocalizations.supportedLocales, localeListResolutionCallback: basicLocaleListResolution, - routerConfig: routerConfig, - // Light mode is "go to" for now. - themeMode: ThemeMode.light, + routerConfig: widget.routerConfig, + themeMode: _themeMode, theme: ThemeBuilder.buildTheme( brand: Brand.catalyst, brightness: Brightness.light, @@ -46,4 +56,11 @@ final class AppContent extends StatelessWidget { }, ); } + + List> get _localizationsDelegates { + return const [ + ...VoicesLocalizations.localizationsDelegates, + LocaleNamesLocalizationsDelegate(), + ]; + } } diff --git a/catalyst_voices/lib/pages/spaces/appbar/spaces_theme_mode_switch.dart b/catalyst_voices/lib/pages/spaces/appbar/spaces_theme_mode_switch.dart new file mode 100644 index 0000000000..7f6212d74b --- /dev/null +++ b/catalyst_voices/lib/pages/spaces/appbar/spaces_theme_mode_switch.dart @@ -0,0 +1,15 @@ +import 'package:catalyst_voices/app/app.dart'; +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:flutter/material.dart'; + +/// A switch that updates the app theme mode. +class SpacesThemeModeSwitch extends StatelessWidget { + const SpacesThemeModeSwitch({super.key}); + + @override + Widget build(BuildContext context) { + return VoicesThemeModeSwitch( + onChanged: AppContent.of(context).updateThemeMode, + ); + } +} diff --git a/catalyst_voices/lib/pages/spaces/spaces_shell_page.dart b/catalyst_voices/lib/pages/spaces/spaces_shell_page.dart index ebea205e41..964e8599ce 100644 --- a/catalyst_voices/lib/pages/spaces/spaces_shell_page.dart +++ b/catalyst_voices/lib/pages/spaces/spaces_shell_page.dart @@ -1,5 +1,6 @@ import 'package:catalyst_voices/common/ext/ext.dart'; import 'package:catalyst_voices/pages/registration/registration_dialog.dart'; +import 'package:catalyst_voices/pages/spaces/appbar/spaces_theme_mode_switch.dart'; import 'package:catalyst_voices/pages/spaces/drawer/spaces_drawer.dart'; import 'package:catalyst_voices/widgets/widgets.dart'; import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; @@ -62,6 +63,7 @@ class _SpacesShellPageState extends State { leading: isVisitor ? null : const DrawerToggleButton(), automaticallyImplyLeading: false, actions: [ + const SpacesThemeModeSwitch(), SessionActionHeader( onGetStartedTap: _showAccountSetup, ), diff --git a/catalyst_voices/lib/widgets/toggles/voices_theme_mode_switch.dart b/catalyst_voices/lib/widgets/toggles/voices_theme_mode_switch.dart new file mode 100644 index 0000000000..d93aabca0c --- /dev/null +++ b/catalyst_voices/lib/widgets/toggles/voices_theme_mode_switch.dart @@ -0,0 +1,32 @@ +import 'package:catalyst_voices/widgets/toggles/voices_switch.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:flutter/material.dart'; + +/// A switch that toggles between light & dark theme mode. +class VoicesThemeModeSwitch extends StatelessWidget { + final ValueChanged onChanged; + + const VoicesThemeModeSwitch({ + super.key, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text('${context.l10n.themeLight} / ${context.l10n.themeDark}'), + const SizedBox(width: 8), + VoicesSwitch( + value: Theme.of(context).brightness == Brightness.dark, + onChanged: (value) { + onChanged( + value ? ThemeMode.dark : ThemeMode.light, + ); + }, + ), + ], + ); + } +} diff --git a/catalyst_voices/lib/widgets/widgets.dart b/catalyst_voices/lib/widgets/widgets.dart index c205dc1519..c4b0e0f188 100644 --- a/catalyst_voices/lib/widgets/widgets.dart +++ b/catalyst_voices/lib/widgets/widgets.dart @@ -67,5 +67,6 @@ export 'toggles/voices_checkbox.dart'; export 'toggles/voices_checkbox_group.dart'; export 'toggles/voices_radio.dart'; export 'toggles/voices_switch.dart'; +export 'toggles/voices_theme_mode_switch.dart'; export 'tooltips/voices_plain_tooltip.dart'; export 'tooltips/voices_rich_tooltip.dart'; diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart index d28c64e861..5108ae5c6a 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart @@ -1132,6 +1132,18 @@ abstract class VoicesLocalizations { /// **'Total'** String get total; + /// Refers to a light theme mode. + /// + /// In en, this message translates to: + /// **'Light'** + String get themeLight; + + /// Refers to a dark theme mode. + /// + /// In en, this message translates to: + /// **'Dark'** + String get themeDark; + /// A title on keychain deleted dialog /// /// In en, this message translates to: diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart index 239a032f9f..062fa2e35b 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart @@ -601,6 +601,12 @@ class VoicesLocalizationsEn extends VoicesLocalizations { @override String get total => 'Total'; + @override + String get themeLight => 'Light'; + + @override + String get themeDark => 'Dark'; + @override String get keychainDeletedDialogTitle => 'Catalyst keychain removed'; diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart index c06f81ac9a..766140cf94 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart @@ -601,6 +601,12 @@ class VoicesLocalizationsEs extends VoicesLocalizations { @override String get total => 'Total'; + @override + String get themeLight => 'Light'; + + @override + String get themeDark => 'Dark'; + @override String get keychainDeletedDialogTitle => 'Catalyst keychain removed'; diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb b/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb index 5c9c8b3c5e..45b9afb873 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb @@ -708,6 +708,14 @@ "yes": "Yes", "no": "No", "total": "Total", + "themeLight": "Light", + "@themeLight": { + "description": "Refers to a light theme mode." + }, + "themeDark": "Dark", + "@themeDark": { + "description": "Refers to a dark theme mode." + }, "keychainDeletedDialogTitle": "Catalyst keychain removed", "@keychainDeletedDialogTitle": { "description": "A title on keychain deleted dialog" diff --git a/catalyst_voices/uikit_example/lib/main.dart b/catalyst_voices/uikit_example/lib/main.dart index 65ef198be7..d55508d6ad 100644 --- a/catalyst_voices/uikit_example/lib/main.dart +++ b/catalyst_voices/uikit_example/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:catalyst_voices/widgets/toggles/voices_theme_mode_switch.dart'; import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; import 'package:catalyst_voices_localization/generated/catalyst_voices_localizations.dart'; @@ -92,22 +93,11 @@ class _ThemeModeSwitcherWrapper extends StatelessWidget { color: Theme.of(context).colorScheme.surface, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - const Text('Light / Dark'), - Padding( - padding: const EdgeInsets.only(left: 8), - child: Switch( - value: Theme.of(context).brightness == Brightness.dark, - onChanged: (value) { - onChanged( - value ? ThemeMode.dark : ThemeMode.light, - ); - }, - ), - ), - ], + child: Align( + alignment: Alignment.centerRight, + child: VoicesThemeModeSwitch( + onChanged: onChanged, + ), ), ), ),