From 3b1cb53b8afb912acdf4aabe19d9e57db2571f73 Mon Sep 17 00:00:00 2001 From: Christien Rioux Date: Sun, 25 May 2025 23:40:52 -0400 Subject: [PATCH] Accessibility update --- CHANGELOG.md | 3 + assets/i18n/en.json | 1 + ios/Podfile.lock | 83 +------ ios/Runner.xcodeproj/project.pbxproj | 18 -- .../xcshareddata/xcschemes/Runner.xcscheme | 2 + .../views/edit_account_page.dart | 47 ++-- .../views/edit_profile_form.dart | 78 +++--- .../views/new_account_page.dart | 2 + .../views/show_recovery_key_page.dart | 7 +- lib/app.dart | 4 +- .../chat_builders/vc_composer_widget.dart | 1 + .../chat_builders/vc_text_message_widget.dart | 13 +- lib/chat/views/chat_component_widget.dart | 25 +- .../chat_single_contact_item_widget.dart | 23 +- .../views/contact_invitation_item_widget.dart | 4 +- lib/contacts/views/availability_widget.dart | 45 ++-- lib/contacts/views/contact_item_widget.dart | 26 +- lib/contacts/views/contacts_browser.dart | 31 +-- lib/contacts/views/contacts_page.dart | 10 +- lib/contacts/views/edit_contact_form.dart | 35 +-- lib/keyboard_shortcuts.dart | 136 +++++++++-- lib/layout/default_app_bar.dart | 16 +- lib/layout/home/drawer_menu/drawer_menu.dart | 31 +-- .../home/drawer_menu/menu_item_widget.dart | 4 +- lib/layout/home/home_account_ready.dart | 147 ++++++------ lib/layout/home/home_screen.dart | 59 ++--- .../views/notifications_preferences.dart | 226 +++++++----------- lib/router/views/router_shell.dart | 10 +- lib/settings/models/preferences.dart | 4 +- lib/settings/settings_page.dart | 76 +++--- lib/theme/models/contrast_generator.dart | 8 + lib/theme/models/scale_theme/scale_theme.dart | 8 + lib/theme/models/theme_preference.dart | 2 +- lib/theme/views/avatar_widget.dart | 74 ------ lib/theme/views/enter_password.dart | 3 +- .../brightness_preferences.dart | 28 +-- .../{ => preferences}/color_preferences.dart | 43 ++-- .../display_scale_preferences.dart | 109 +++++++++ lib/theme/views/preferences/preferences.dart | 4 + .../preferences/wallpaper_preferences.dart | 25 ++ lib/theme/views/responsive.dart | 4 + .../{ => styled_widgets}/styled_alert.dart | 3 +- .../views/styled_widgets/styled_avatar.dart | 77 ++++++ .../styled_button_box.dart} | 19 +- .../views/styled_widgets/styled_checkbox.dart | 63 +++++ .../{ => styled_widgets}/styled_dialog.dart | 2 +- .../views/styled_widgets/styled_dropdown.dart | 59 +++++ .../{ => styled_widgets}/styled_scaffold.dart | 2 +- .../styled_slide_tile.dart} | 50 ++-- .../views/styled_widgets/styled_slider.dart | 79 ++++++ .../views/styled_widgets/styled_widgets.dart | 8 + lib/theme/views/views.dart | 12 +- lib/theme/views/wallpaper_preferences.dart | 37 --- lib/veilid_processor/views/developer.dart | 8 +- .../views/signal_strength_meter.dart | 2 +- 55 files changed, 1089 insertions(+), 807 deletions(-) delete mode 100644 lib/theme/views/avatar_widget.dart rename lib/theme/views/{ => preferences}/brightness_preferences.dart (59%) rename lib/theme/views/{ => preferences}/color_preferences.dart (54%) create mode 100644 lib/theme/views/preferences/display_scale_preferences.dart create mode 100644 lib/theme/views/preferences/preferences.dart create mode 100644 lib/theme/views/preferences/wallpaper_preferences.dart rename lib/theme/views/{ => styled_widgets}/styled_alert.dart (99%) create mode 100644 lib/theme/views/styled_widgets/styled_avatar.dart rename lib/theme/views/{option_box.dart => styled_widgets/styled_button_box.dart} (75%) create mode 100644 lib/theme/views/styled_widgets/styled_checkbox.dart rename lib/theme/views/{ => styled_widgets}/styled_dialog.dart (98%) create mode 100644 lib/theme/views/styled_widgets/styled_dropdown.dart rename lib/theme/views/{ => styled_widgets}/styled_scaffold.dart (97%) rename lib/theme/views/{slider_tile.dart => styled_widgets/styled_slide_tile.dart} (75%) create mode 100644 lib/theme/views/styled_widgets/styled_slider.dart create mode 100644 lib/theme/views/styled_widgets/styled_widgets.dart delete mode 100644 lib/theme/views/wallpaper_preferences.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index a7b1930..8f7eefa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ - Deprecated accounts no longer crash application at startup - Simplify SingleContactMessagesCubit and MessageReconciliation - Update flutter_chat_ui to 2.0.0 +- Accessibility improvements + - Text scaling + - Keyboard shortcuts Ctrl + / Ctrl - to change font size ## v0.4.7 ## - *Community Contributions* diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 53bf461..50b0904 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -276,6 +276,7 @@ "titlebar": "Settings", "color_theme": "Color Theme", "brightness_mode": "Brightness Mode", + "display_scale": "Display Scale", "display_beta_warning": "Display beta warning on startup", "none": "None", "in_app": "In-app", diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2528d2d..add7488 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -6,54 +6,9 @@ PODS: - Flutter (1.0.0) - flutter_native_splash (2.4.3): - Flutter - - GoogleDataTransport (10.1.0): - - nanopb (~> 3.30910.0) - - PromisesObjC (~> 2.4) - - GoogleMLKit/BarcodeScanning (7.0.0): - - GoogleMLKit/MLKitCore - - MLKitBarcodeScanning (~> 6.0.0) - - GoogleMLKit/MLKitCore (7.0.0): - - MLKitCommon (~> 12.0.0) - - GoogleToolboxForMac/Defines (4.2.1) - - GoogleToolboxForMac/Logger (4.2.1): - - GoogleToolboxForMac/Defines (= 4.2.1) - - "GoogleToolboxForMac/NSData+zlib (4.2.1)": - - GoogleToolboxForMac/Defines (= 4.2.1) - - GoogleUtilities/Environment (8.0.2): - - GoogleUtilities/Privacy - - GoogleUtilities/Logger (8.0.2): - - GoogleUtilities/Environment - - GoogleUtilities/Privacy - - GoogleUtilities/Privacy (8.0.2) - - GoogleUtilities/UserDefaults (8.0.2): - - GoogleUtilities/Logger - - GoogleUtilities/Privacy - - GTMSessionFetcher/Core (3.5.0) - - MLImage (1.0.0-beta6) - - MLKitBarcodeScanning (6.0.0): - - MLKitCommon (~> 12.0) - - MLKitVision (~> 8.0) - - MLKitCommon (12.0.0): - - GoogleDataTransport (~> 10.0) - - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1) - - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)" - - GoogleUtilities/Logger (~> 8.0) - - GoogleUtilities/UserDefaults (~> 8.0) - - GTMSessionFetcher/Core (< 4.0, >= 3.3.2) - - MLKitVision (8.0.0): - - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1) - - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)" - - GTMSessionFetcher/Core (< 4.0, >= 3.3.2) - - MLImage (= 1.0.0-beta6) - - MLKitCommon (~> 12.0) - - mobile_scanner (6.0.2): + - mobile_scanner (7.0.0): - Flutter - - GoogleMLKit/BarcodeScanning (~> 7.0.0) - - nanopb (3.30910.0): - - nanopb/decode (= 3.30910.0) - - nanopb/encode (= 3.30910.0) - - nanopb/decode (3.30910.0) - - nanopb/encode (3.30910.0) + - FlutterMacOS - package_info_plus (0.4.5): - Flutter - pasteboard (0.0.1): @@ -63,7 +18,6 @@ PODS: - FlutterMacOS - printing (1.0.0): - Flutter - - PromisesObjC (2.4.0) - share_plus (0.0.1): - Flutter - shared_preferences_foundation (0.0.1): @@ -84,7 +38,7 @@ DEPENDENCIES: - file_saver (from `.symlinks/plugins/file_saver/ios`) - Flutter (from `Flutter`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) - - mobile_scanner (from `.symlinks/plugins/mobile_scanner/ios`) + - mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - pasteboard (from `.symlinks/plugins/pasteboard/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) @@ -96,20 +50,6 @@ DEPENDENCIES: - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - veilid (from `.symlinks/plugins/veilid/ios`) -SPEC REPOS: - trunk: - - GoogleDataTransport - - GoogleMLKit - - GoogleToolboxForMac - - GoogleUtilities - - GTMSessionFetcher - - MLImage - - MLKitBarcodeScanning - - MLKitCommon - - MLKitVision - - nanopb - - PromisesObjC - EXTERNAL SOURCES: camera_avfoundation: :path: ".symlinks/plugins/camera_avfoundation/ios" @@ -120,7 +60,7 @@ EXTERNAL SOURCES: flutter_native_splash: :path: ".symlinks/plugins/flutter_native_splash/ios" mobile_scanner: - :path: ".symlinks/plugins/mobile_scanner/ios" + :path: ".symlinks/plugins/mobile_scanner/darwin" package_info_plus: :path: ".symlinks/plugins/package_info_plus/ios" pasteboard: @@ -143,26 +83,15 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/veilid/ios" SPEC CHECKSUMS: - camera_avfoundation: 04b44aeb14070126c6529e5ab82cc7c9fca107cf + camera_avfoundation: be3be85408cd4126f250386828e9b1dfa40ab436 file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf - GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 - GoogleMLKit: eff9e23ec1d90ea4157a1ee2e32a4f610c5b3318 - GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8 - GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d - GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 - MLImage: 0ad1c5f50edd027672d8b26b0fee78a8b4a0fc56 - MLKitBarcodeScanning: 0a3064da0a7f49ac24ceb3cb46a5bc67496facd2 - MLKitCommon: 07c2c33ae5640e5380beaaa6e4b9c249a205542d - MLKitVision: 45e79d68845a2de77e2dd4d7f07947f0ed157b0e - mobile_scanner: af8f71879eaba2bbcb4d86c6a462c3c0e7f23036 - nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 + mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93 package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 pasteboard: 49088aeb6119d51f976a421db60d8e1ab079b63c path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 printing: 54ff03f28fe9ba3aa93358afb80a8595a071dd07 - PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index e612191..3a96d3e 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -139,7 +139,6 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 02C44F9283ADDE9FAAA73512 /* [CP] Embed Pods Frameworks */, - 61BE8A90522682C17620991D /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -232,23 +231,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 61BE8A90522682C17620991D /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 3e31b44..a44fb7f 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -26,6 +26,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" shouldUseLaunchSchemeArgsEnv = "YES"> { title: translate('edit_account_page.remove_account_confirm'), child: Column(mainAxisSize: MainAxisSize.min, children: [ Text(translate('edit_account_page.remove_account_confirm_message')) - .paddingLTRB(24, 24, 24, 0), - Text(translate('confirmation.are_you_sure')).paddingAll(8), + .paddingLTRB(24.scaled(context), 24.scaled(context), + 24.scaled(context), 0), + Text(translate('confirmation.are_you_sure')) + .paddingAll(8.scaled(context)), Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: () { Navigator.of(context).pop(false); }, child: Row(mainAxisSize: MainAxisSize.min, children: [ - const Icon(Icons.cancel, size: 16).paddingLTRB(0, 0, 4, 0), + Icon(Icons.cancel, size: 16.scaled(context)) + .paddingLTRB(0, 0, 4.scaled(context), 0), Text(translate('button.no')).paddingLTRB(0, 0, 4, 0) ])), ElevatedButton( @@ -89,10 +92,12 @@ class _EditAccountPageState extends WindowSetupState { Navigator.of(context).pop(true); }, child: Row(mainAxisSize: MainAxisSize.min, children: [ - const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0), - Text(translate('button.yes')).paddingLTRB(0, 0, 4, 0) + Icon(Icons.check, size: 16.scaled(context)) + .paddingLTRB(0, 0, 4.scaled(context), 0), + Text(translate('button.yes')) + .paddingLTRB(0, 0, 4.scaled(context), 0) ])) - ]).paddingAll(24) + ]).paddingAll(24.scaled(context)) ])); if (confirmed != null && confirmed) { try { @@ -141,29 +146,36 @@ class _EditAccountPageState extends WindowSetupState { title: translate('edit_account_page.destroy_account_confirm'), child: Column(mainAxisSize: MainAxisSize.min, children: [ Text(translate('edit_account_page.destroy_account_confirm_message')) - .paddingLTRB(24, 24, 24, 0), + .paddingLTRB(24.scaled(context), 24.scaled(context), + 24.scaled(context), 0), Text(translate( 'edit_account_page.destroy_account_confirm_message_details')) - .paddingLTRB(24, 24, 24, 0), - Text(translate('confirmation.are_you_sure')).paddingAll(8), + .paddingLTRB(24.scaled(context), 24.scaled(context), + 24.scaled(context), 0), + Text(translate('confirmation.are_you_sure')) + .paddingAll(24.scaled(context)), Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: () { Navigator.of(context).pop(false); }, child: Row(mainAxisSize: MainAxisSize.min, children: [ - const Icon(Icons.cancel, size: 16).paddingLTRB(0, 0, 4, 0), - Text(translate('button.no')).paddingLTRB(0, 0, 4, 0) + Icon(Icons.cancel, size: 16.scaled(context)) + .paddingLTRB(0, 0, 4.scaled(context), 0), + Text(translate('button.no')) + .paddingLTRB(0, 0, 4.scaled(context), 0) ])), ElevatedButton( onPressed: () { Navigator.of(context).pop(true); }, child: Row(mainAxisSize: MainAxisSize.min, children: [ - const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0), - Text(translate('button.yes')).paddingLTRB(0, 0, 4, 0) + Icon(Icons.check, size: 16.scaled(context)) + .paddingLTRB(0, 0, 4.scaled(context), 0), + Text(translate('button.yes')) + .paddingLTRB(0, 0, 4.scaled(context), 0) ])) - ]).paddingAll(24) + ]).paddingAll(24.scaled(context)) ])); if (confirmed != null && confirmed) { try { @@ -250,10 +262,12 @@ class _EditAccountPageState extends WindowSetupState { return StyledScaffold( appBar: DefaultAppBar( + context: context, title: Text(translate('edit_account_page.titlebar')), leading: Navigator.canPop(context) ? IconButton( icon: const Icon(Icons.arrow_back), + iconSize: 24.scaled(context), onPressed: () { singleFuture((this, _kDoBackArrow), () async { if (_isModified) { @@ -277,6 +291,7 @@ class _EditAccountPageState extends WindowSetupState { const SignalStrengthMeterWidget(), IconButton( icon: const Icon(Icons.settings), + iconSize: 24.scaled(context), tooltip: translate('menu.settings_tooltip'), onPressed: () async { await GoRouterHelper(context).push('/settings'); @@ -285,14 +300,14 @@ class _EditAccountPageState extends WindowSetupState { body: SingleChildScrollView( child: Column(children: [ _editAccountForm(context).paddingLTRB(0, 0, 0, 32), - OptionBox( + StyledButtonBox( instructions: translate('edit_account_page.remove_account_description'), buttonIcon: Icons.person_remove_alt_1, buttonText: translate('edit_account_page.remove_account'), onClick: _onRemoveAccount, ), - OptionBox( + StyledButtonBox( instructions: translate('edit_account_page.destroy_account_description'), buttonIcon: Icons.person_off, diff --git a/lib/account_manager/views/edit_profile_form.dart b/lib/account_manager/views/edit_profile_form.dart index 4977dc7..114c7b8 100644 --- a/lib/account_manager/views/edit_profile_form.dart +++ b/lib/account_manager/views/edit_profile_form.dart @@ -53,16 +53,16 @@ class EditProfileForm extends StatefulWidget { ..add(DiagnosticsProperty('initialValue', initialValue)); } - static const String formFieldName = 'name'; - static const String formFieldPronouns = 'pronouns'; - static const String formFieldAbout = 'about'; - static const String formFieldAvailability = 'availability'; - static const String formFieldFreeMessage = 'free_message'; - static const String formFieldAwayMessage = 'away_message'; - static const String formFieldBusyMessage = 'busy_message'; - static const String formFieldAvatar = 'avatar'; - static const String formFieldAutoAway = 'auto_away'; - static const String formFieldAutoAwayTimeout = 'auto_away_timeout'; + static const formFieldName = 'name'; + static const formFieldPronouns = 'pronouns'; + static const formFieldAbout = 'about'; + static const formFieldAvailability = 'availability'; + static const formFieldFreeMessage = 'free_message'; + static const formFieldAwayMessage = 'away_message'; + static const formFieldBusyMessage = 'busy_message'; + static const formFieldAvatar = 'avatar'; + static const formFieldAutoAway = 'auto_away'; + static const formFieldAutoAwayTimeout = 'auto_away_timeout'; } class _EditProfileFormState extends State { @@ -98,6 +98,7 @@ class _EditProfileFormState extends State { name: EditProfileForm.formFieldAvailability, initialValue: initialValue, decoration: InputDecoration( + contentPadding: const EdgeInsets.all(8).scaled(context), floatingLabelBehavior: FloatingLabelBehavior.always, labelText: translate('account.form_availability'), hintText: translate('account.empty_busy_message')), @@ -110,7 +111,7 @@ class _EditProfileFormState extends State { Text(x == proto.Availability.AVAILABILITY_OFFLINE ? translate('availability.always_show_offline') : AvailabilityWidget.availabilityName(x)) - .paddingLTRB(8, 0, 0, 0), + .paddingLTRB(8.scaled(context), 0, 0, 0), ]))) .toList(), ); @@ -175,17 +176,8 @@ class _EditProfileFormState extends State { BuildContext context, ) { final theme = Theme.of(context); - final scale = theme.extension()!; - final scaleConfig = theme.extension()!; final textTheme = theme.textTheme; - late final Color border; - if (scaleConfig.useVisualIndicators && !scaleConfig.preferBorders) { - border = scale.primaryScale.elementBackground; - } else { - border = scale.primaryScale.border; - } - return FormBuilder( key: _formKey, autovalidateMode: AutovalidateMode.onUserInteraction, @@ -197,14 +189,9 @@ class _EditProfileFormState extends State { children: [ Row(children: [ const Spacer(), - AvatarWidget( + StyledAvatar( name: _currentValueName, - size: 128, - borderColor: border, - foregroundColor: scale.primaryScale.primaryText, - backgroundColor: scale.primaryScale.primary, - scaleConfig: scaleConfig, - textStyle: theme.textTheme.titleLarge!.copyWith(fontSize: 64), + size: 128.scaled(context), ).paddingLTRB(0, 0, 0, 16), const Spacer() ]), @@ -218,6 +205,7 @@ class _EditProfileFormState extends State { }); }, decoration: InputDecoration( + contentPadding: const EdgeInsets.all(8).scaled(context), floatingLabelBehavior: FloatingLabelBehavior.always, labelText: translate('account.form_name'), hintText: translate('account.empty_name')), @@ -233,6 +221,7 @@ class _EditProfileFormState extends State { initialValue: _savedValue.pronouns, maxLength: 64, decoration: InputDecoration( + contentPadding: const EdgeInsets.all(8).scaled(context), floatingLabelBehavior: FloatingLabelBehavior.always, labelText: translate('account.form_pronouns'), hintText: translate('account.empty_pronouns')), @@ -245,6 +234,7 @@ class _EditProfileFormState extends State { maxLines: 8, minLines: 1, decoration: InputDecoration( + contentPadding: const EdgeInsets.all(8).scaled(context), floatingLabelBehavior: FloatingLabelBehavior.always, labelText: translate('account.form_about'), hintText: translate('account.empty_about')), @@ -256,6 +246,7 @@ class _EditProfileFormState extends State { initialValue: _savedValue.freeMessage, maxLength: 128, decoration: InputDecoration( + contentPadding: const EdgeInsets.all(8).scaled(context), floatingLabelBehavior: FloatingLabelBehavior.always, labelText: translate('account.form_free_message'), hintText: translate('account.empty_free_message')), @@ -266,6 +257,7 @@ class _EditProfileFormState extends State { initialValue: _savedValue.awayMessage, maxLength: 128, decoration: InputDecoration( + contentPadding: const EdgeInsets.all(8).scaled(context), floatingLabelBehavior: FloatingLabelBehavior.always, labelText: translate('account.form_away_message'), hintText: translate('account.empty_away_message')), @@ -276,6 +268,7 @@ class _EditProfileFormState extends State { initialValue: _savedValue.busyMessage, maxLength: 128, decoration: InputDecoration( + contentPadding: const EdgeInsets.all(8).scaled(context), floatingLabelBehavior: FloatingLabelBehavior.always, labelText: translate('account.form_busy_message'), hintText: translate('account.empty_busy_message')), @@ -291,12 +284,13 @@ class _EditProfileFormState extends State { _currentValueAutoAway = v ?? false; }); }, - ).paddingLTRB(0, 0, 0, 16), + ).paddingLTRB(0, 0, 0, 16.scaled(context)), FormBuilderTextField( name: EditProfileForm.formFieldAutoAwayTimeout, enabled: _currentValueAutoAway, initialValue: _savedValue.autoAwayTimeout.toString(), decoration: InputDecoration( + contentPadding: const EdgeInsets.all(8).scaled(context), labelText: translate('account.form_auto_away_timeout'), ), validator: FormBuilderValidators.positiveNumber(), @@ -306,7 +300,7 @@ class _EditProfileFormState extends State { const Spacer(), Text(widget.instructions).toCenter().flexible(flex: 6), const Spacer(), - ]).paddingSymmetric(vertical: 16), + ]).paddingSymmetric(vertical: 16.scaled(context)), Row(children: [ const Spacer(), Builder(builder: (context) { @@ -319,17 +313,19 @@ class _EditProfileFormState extends State { false; return ElevatedButton( - onPressed: (networkReady && _isModified) ? _doSubmit : null, - child: Row(mainAxisSize: MainAxisSize.min, children: [ - Icon(networkReady ? Icons.check : Icons.hourglass_empty, - size: 16) - .paddingLTRB(0, 0, 4, 0), - Text(networkReady - ? widget.submitText - : widget.submitDisabledText) - .paddingLTRB(0, 0, 4, 0) - ]), - ); + onPressed: (networkReady && _isModified) ? _doSubmit : null, + child: Padding( + padding: EdgeInsetsGeometry.all(4.scaled(context)), + child: Row(mainAxisSize: MainAxisSize.min, children: [ + Icon(networkReady ? Icons.check : Icons.hourglass_empty, + size: 16.scaled(context)) + .paddingLTRB(0, 0, 4.scaled(context), 0), + Text(networkReady + ? widget.submitText + : widget.submitDisabledText) + .paddingLTRB(0, 0, 4.scaled(context), 0) + ]), + )); }), const Spacer() ]) @@ -363,5 +359,5 @@ class _EditProfileFormState extends State { late AccountSpec _savedValue; late bool _currentValueAutoAway; late String _currentValueName; - bool _isModified = false; + var _isModified = false; } diff --git a/lib/account_manager/views/new_account_page.dart b/lib/account_manager/views/new_account_page.dart index 07034df..5012527 100644 --- a/lib/account_manager/views/new_account_page.dart +++ b/lib/account_manager/views/new_account_page.dart @@ -94,6 +94,7 @@ class _NewAccountPageState extends WindowSetupState { return StyledScaffold( appBar: DefaultAppBar( + context: context, title: Text(translate('new_account_page.titlebar')), leading: GoRouterHelper(context).canPop() ? IconButton( @@ -111,6 +112,7 @@ class _NewAccountPageState extends WindowSetupState { const SignalStrengthMeterWidget(), IconButton( icon: const Icon(Icons.settings), + iconSize: 24.scaled(context), tooltip: translate('menu.settings_tooltip'), onPressed: () async { await GoRouterHelper(context).push('/settings'); diff --git a/lib/account_manager/views/show_recovery_key_page.dart b/lib/account_manager/views/show_recovery_key_page.dart index 7c971e0..5423543 100644 --- a/lib/account_manager/views/show_recovery_key_page.dart +++ b/lib/account_manager/views/show_recovery_key_page.dart @@ -164,6 +164,7 @@ class _ShowRecoveryKeyPageState extends WindowSetupState { return StyledScaffold( appBar: DefaultAppBar( + context: context, title: Text(translate('show_recovery_key_page.titlebar')), actions: [ const SignalStrengthMeterWidget(), @@ -193,7 +194,7 @@ class _ShowRecoveryKeyPageState extends WindowSetupState { textAlign: TextAlign.center, translate('show_recovery_key_page.instructions_options')) .paddingLTRB(12, 0, 12, 24), - OptionBox( + StyledButtonBox( instructions: translate('show_recovery_key_page.instructions_print'), buttonIcon: Icons.print, @@ -209,7 +210,7 @@ class _ShowRecoveryKeyPageState extends WindowSetupState { _codeHandled = true; }); }), - OptionBox( + StyledButtonBox( instructions: translate('show_recovery_key_page.instructions_view'), buttonIcon: Icons.edit_document, @@ -229,7 +230,7 @@ class _ShowRecoveryKeyPageState extends WindowSetupState { _codeHandled = true; }); }), - OptionBox( + StyledButtonBox( instructions: translate('show_recovery_key_page.instructions_share'), buttonIcon: Icons.ios_share, diff --git a/lib/app.dart b/lib/app.dart index 802b0d7..5f4d6dc 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -31,7 +31,7 @@ class VeilidChatApp extends StatelessWidget { super.key, }); - static const String name = 'VeilidChat'; + static const name = 'VeilidChat'; final ThemeData initialThemeData; @@ -125,7 +125,7 @@ class VeilidChatApp extends StatelessWidget { @override Widget build(BuildContext context) => FutureProvider( initialData: null, - create: (context) async => VeilidChatGlobalInit.initialize(), + create: (context) => VeilidChatGlobalInit.initialize(), builder: (context, __) { final globalInit = context.watch(); if (globalInit == null) { diff --git a/lib/chat/views/chat_builders/vc_composer_widget.dart b/lib/chat/views/chat_builders/vc_composer_widget.dart index b3eb1e5..f470e9b 100644 --- a/lib/chat/views/chat_builders/vc_composer_widget.dart +++ b/lib/chat/views/chat_builders/vc_composer_widget.dart @@ -355,6 +355,7 @@ class _VcComposerState extends State { borderRadius: BorderRadius.all(Radius.circular( 8 * config.borderRadiusScale))), hintText: widget.hintText, + hintMaxLines: 1, hintStyle: chatTheme.typography.bodyMedium.copyWith( color: widget.hintColor ?? chatTheme.colors.onSurface diff --git a/lib/chat/views/chat_builders/vc_text_message_widget.dart b/lib/chat/views/chat_builders/vc_text_message_widget.dart index fc1fe80..52235f6 100644 --- a/lib/chat/views/chat_builders/vc_text_message_widget.dart +++ b/lib/chat/views/chat_builders/vc_text_message_widget.dart @@ -12,7 +12,7 @@ class VcTextMessageWidget extends StatelessWidget { const VcTextMessageWidget({ required this.message, required this.index, - this.padding = const EdgeInsets.symmetric(horizontal: 16, vertical: 10), + this.padding, this.borderRadius, this.onlyEmojiFontSize, this.sentBackgroundColor, @@ -72,10 +72,6 @@ class VcTextMessageWidget extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final scaleTheme = theme.extension()!; - final config = scaleTheme.config; - final scheme = scaleTheme.scheme; - final scale = scaleTheme.scheme.scale(ScaleKind.primary); - final textTheme = theme.textTheme; final scaleChatTheme = scaleTheme.chatTheme(); final chatTheme = scaleChatTheme.chatTheme; @@ -243,15 +239,16 @@ class TimeAndStatus extends StatelessWidget { if (showStatus && status != null) if (status == MessageStatus.sending) SizedBox( - width: 6, - height: 6, + width: 6.scaled(context), + height: 6.scaled(context), child: CircularProgressIndicator( color: textStyle?.color, strokeWidth: 2, ), ) else - Icon(getIconForStatus(status!), color: textStyle?.color, size: 12), + Icon(getIconForStatus(status!), + color: textStyle?.color, size: 12.scaled(context)), ], ); } diff --git a/lib/chat/views/chat_component_widget.dart b/lib/chat/views/chat_component_widget.dart index 7d90d89..8106a48 100644 --- a/lib/chat/views/chat_component_widget.dart +++ b/lib/chat/views/chat_component_widget.dart @@ -132,17 +132,9 @@ class _ChatComponentWidgetState extends State { final scale = scaleTheme.scheme.scale(ScaleKind.primary); final textTheme = theme.textTheme; final scaleChatTheme = scaleTheme.chatTheme(); - // final errorChatTheme = chatTheme.copyWith(color:) - // ..inputTextColor = scaleScheme.errorScale.primary - // ..sendButtonIcon = Image.asset( - // 'assets/icon-send.png', - // color: scaleScheme.errorScale.primary, - // package: 'flutter_chat_ui', - // )) - // .commit(); // Get the enclosing chat component cubit that contains our state - // (created by ChatComponentWidget.builder()) + // (created by ChatComponentWidget.singleContact()) final chatComponentCubit = context.watch(); final chatComponentState = chatComponentCubit.state; @@ -273,14 +265,19 @@ class _ChatComponentWidgetState extends State { // Text message builder textMessageBuilder: (context, message, index) => VcTextMessageWidget( - message: message, - index: index, - // showTime: true, - // showStatus: true, - ), + message: message, + index: index, + padding: const EdgeInsets.symmetric( + vertical: 12, horizontal: 16) + .scaled(context) + // showTime: true, + // showStatus: true, + ), // Composer builder composerBuilder: (ctx) => VcComposerWidget( autofocus: true, + padding: const EdgeInsets.all(4).scaled(context), + gap: 8.scaled(context), focusNode: _focusNode, textInputAction: isAnyMobile ? TextInputAction.newline diff --git a/lib/chat_list/views/chat_single_contact_item_widget.dart b/lib/chat_list/views/chat_single_contact_item_widget.dart index 5191fdd..d2594c5 100644 --- a/lib/chat_list/views/chat_single_contact_item_widget.dart +++ b/lib/chat_list/views/chat_single_contact_item_widget.dart @@ -24,11 +24,9 @@ class ChatSingleContactItemWidget extends StatelessWidget { final bool _disabled; @override - // ignore: prefer_expression_function_bodies Widget build( BuildContext context, ) { - final theme = Theme.of(context); final scaleTheme = Theme.of(context).extension()!; final activeChatCubit = context.watch(); @@ -48,23 +46,12 @@ class ChatSingleContactItemWidget extends StatelessWidget { selected: selected, ); - final avatar = AvatarWidget( + final avatar = StyledAvatar( name: name, - size: 32, - borderColor: scaleTheme.config.useVisualIndicators - ? scaleTheme.scheme.primaryScale.primaryText - : scaleTheme.scheme.primaryScale.subtleBorder, - foregroundColor: _disabled - ? scaleTheme.scheme.grayScale.primaryText - : scaleTheme.scheme.primaryScale.primaryText, - backgroundColor: _disabled - ? scaleTheme.scheme.grayScale.primary - : scaleTheme.scheme.primaryScale.primary, - scaleConfig: scaleTheme.config, - textStyle: theme.textTheme.titleLarge!, + size: 32.scaled(context), ); - return SliderTile( + return StyledSlideTile( key: ValueKey(_localConversationRecordKey), disabled: _disabled, selected: selected, @@ -75,14 +62,14 @@ class ChatSingleContactItemWidget extends StatelessWidget { trailing: AvailabilityWidget( availability: availability, color: scaleTileTheme.textColor, - ).fit(fit: BoxFit.scaleDown), + ).fit(fit: BoxFit.fill), onTap: () { singleFuture(activeChatCubit, () async { activeChatCubit.setActiveChat(_localConversationRecordKey); }); }, endActions: [ - SliderTileAction( + SlideTileAction( //icon: Icons.delete, label: translate('button.delete'), actionScale: ScaleKind.tertiary, diff --git a/lib/contact_invitation/views/contact_invitation_item_widget.dart b/lib/contact_invitation/views/contact_invitation_item_widget.dart index 544e5db..b86a833 100644 --- a/lib/contact_invitation/views/contact_invitation_item_widget.dart +++ b/lib/contact_invitation/views/contact_invitation_item_widget.dart @@ -44,7 +44,7 @@ class ContactInvitationItemWidget extends StatelessWidget { title = contactInvitationRecord.message; } - return SliderTile( + return StyledSlideTile( key: ObjectKey(contactInvitationRecord), disabled: tileDisabled, selected: selected, @@ -67,7 +67,7 @@ class ContactInvitationItemWidget extends StatelessWidget { ))); }, endActions: [ - SliderTileAction( + SlideTileAction( // icon: Icons.delete, label: translate('button.delete'), actionScale: ScaleKind.tertiary, diff --git a/lib/contacts/views/availability_widget.dart b/lib/contacts/views/availability_widget.dart index 75ef0a8..55fac39 100644 --- a/lib/contacts/views/availability_widget.dart +++ b/lib/contacts/views/availability_widget.dart @@ -1,37 +1,34 @@ -import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_translate/flutter_translate.dart'; import '../../proto/proto.dart' as proto; -import '../../theme/theme.dart'; class AvailabilityWidget extends StatelessWidget { const AvailabilityWidget( {required this.availability, required this.color, this.vertical = true, - this.size = 32, super.key}); - static Widget availabilityIcon(proto.Availability availability, Color color, - {double size = 24}) { + static Widget availabilityIcon( + proto.Availability availability, + Color color, + ) { late final Widget icon; switch (availability) { case proto.Availability.AVAILABILITY_AWAY: icon = SvgPicture.asset('assets/images/toilet.svg', - width: size, - height: size, colorFilter: ColorFilter.mode(color, BlendMode.srcATop)); case proto.Availability.AVAILABILITY_BUSY: - icon = Icon(Icons.event_busy, size: size); + icon = const Icon(Icons.event_busy, applyTextScaling: true); case proto.Availability.AVAILABILITY_FREE: - icon = Icon(Icons.event_available, size: size); + icon = const Icon(Icons.event_available, applyTextScaling: true); case proto.Availability.AVAILABILITY_OFFLINE: - icon = Icon(Icons.cloud_off, size: size); + icon = const Icon(Icons.cloud_off, applyTextScaling: true); case proto.Availability.AVAILABILITY_UNSPECIFIED: - icon = Icon(Icons.question_mark, size: size); + icon = const Icon(Icons.question_mark, applyTextScaling: true); } return icon; } @@ -59,23 +56,17 @@ class AvailabilityWidget extends StatelessWidget { final textTheme = theme.textTheme; final name = availabilityName(availability); - final icon = availabilityIcon(availability, color, size: size * 2 / 3); + final icon = availabilityIcon(availability, color); return vertical - ? ConstrainedBox( - constraints: BoxConstraints.tightFor(width: size), - child: Column(mainAxisSize: MainAxisSize.min, children: [ - icon, - Text(name, style: textTheme.labelSmall!.copyWith(color: color)) - .fit(fit: BoxFit.scaleDown) - ])) - : ConstrainedBox( - constraints: BoxConstraints.tightFor(height: size), - child: Row(mainAxisSize: MainAxisSize.min, children: [ - icon, - Text(name, style: textTheme.labelLarge!.copyWith(color: color)) - .paddingLTRB(size / 4, 0, 0, 0) - ])); + ? Column(mainAxisSize: MainAxisSize.min, children: [ + icon, + Text(name, style: textTheme.labelSmall!.copyWith(color: color)) + ]) + : Row(mainAxisSize: MainAxisSize.min, children: [ + icon, + Text(' $name', style: textTheme.labelLarge!.copyWith(color: color)) + ]); } //////////////////////////////////////////////////////////////////////////// @@ -83,7 +74,6 @@ class AvailabilityWidget extends StatelessWidget { final proto.Availability availability; final Color color; final bool vertical; - final double size; @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { @@ -92,7 +82,6 @@ class AvailabilityWidget extends StatelessWidget { ..add( DiagnosticsProperty('availability', availability)) ..add(DiagnosticsProperty('vertical', vertical)) - ..add(DoubleProperty('size', size)) ..add(ColorProperty('color', color)); } } diff --git a/lib/contacts/views/contact_item_widget.dart b/lib/contacts/views/contact_item_widget.dart index e206570..9a76be5 100644 --- a/lib/contacts/views/contact_item_widget.dart +++ b/lib/contacts/views/contact_item_widget.dart @@ -26,30 +26,16 @@ class ContactItemWidget extends StatelessWidget { Widget build( BuildContext context, ) { - final theme = Theme.of(context); - final scale = theme.extension()!; - final scaleConfig = theme.extension()!; - final name = _contact.nameOrNickname; final title = _contact.displayName; final subtitle = _contact.profile.status; - final avatar = AvatarWidget( + final avatar = StyledAvatar( name: name, - size: 34, - borderColor: _disabled - ? scale.grayScale.primaryText - : scale.primaryScale.subtleBorder, - foregroundColor: _disabled - ? scale.grayScale.primaryText - : scale.primaryScale.primaryText, - backgroundColor: - _disabled ? scale.grayScale.primary : scale.primaryScale.primary, - scaleConfig: scaleConfig, - textStyle: theme.textTheme.titleLarge!, + size: 34.scaled(context), ); - return SliderTile( + return StyledSlideTile( key: ObjectKey(_contact), disabled: _disabled, selected: _selected, @@ -69,7 +55,7 @@ class ContactItemWidget extends StatelessWidget { }), startActions: [ if (_onDoubleTap != null) - SliderTileAction( + SlideTileAction( //icon: Icons.edit, label: translate('button.chat'), actionScale: ScaleKind.secondary, @@ -81,7 +67,7 @@ class ContactItemWidget extends StatelessWidget { ], endActions: [ if (_onTap != null) - SliderTileAction( + SlideTileAction( //icon: Icons.edit, label: translate('button.edit'), actionScale: ScaleKind.secondary, @@ -91,7 +77,7 @@ class ContactItemWidget extends StatelessWidget { }), ), if (_onDelete != null) - SliderTileAction( + SlideTileAction( //icon: Icons.delete, label: translate('button.delete'), actionScale: ScaleKind.tertiary, diff --git a/lib/contacts/views/contacts_browser.dart b/lib/contacts/views/contacts_browser.dart index 84a2601..0504a7a 100644 --- a/lib/contacts/views/contacts_browser.dart +++ b/lib/contacts/views/contacts_browser.dart @@ -92,7 +92,7 @@ class _ContactsBrowserState extends State final menuParams = StarMenuParameters( shape: MenuShape.linear, - centerOffset: const Offset(0, 64), + centerOffset: Offset(0, 64.scaled(context)), boundaryBackground: BoundaryBackground( color: menuBackgroundColor, decoration: ShapeDecoration( @@ -113,13 +113,14 @@ class _ContactsBrowserState extends State onPressed: onPressed, icon: Icon( iconData, - size: 32, - ).paddingSTEB(0, 8, 0, 8), + size: 32.scaled(context), + ).paddingSTEB(0, 8.scaled(context), 0, 8.scaled(context)), label: Text( text, + textScaler: MediaQuery.of(context).textScaler, maxLines: 2, textAlign: TextAlign.center, - ).paddingSTEB(0, 8, 0, 8)); + ).paddingSTEB(0, 8.scaled(context), 0, 8.scaled(context))); final inviteMenuItems = [ makeMenuButton( @@ -135,14 +136,14 @@ class _ContactsBrowserState extends State onPressed: () async { _invitationMenuController.closeMenu!(); await ScanInvitationDialog.show(context); - }).paddingLTRB(0, 0, 0, 8), + }).paddingLTRB(0, 0, 0, 8.scaled(context)), makeMenuButton( iconData: Icons.contact_page, text: translate('add_contact_sheet.create_invite'), onPressed: () async { _invitationMenuController.closeMenu!(); await CreateInvitationDialog.show(context); - }).paddingLTRB(0, 0, 0, 8), + }).paddingLTRB(0, 0, 0, 8.scaled(context)), ]; return StarMenu( @@ -154,7 +155,7 @@ class _ContactsBrowserState extends State params: menuParams, child: IconButton( onPressed: () {}, - iconSize: 24, + iconSize: 24.scaled(context), icon: Icon(Icons.person_add, color: menuIconColor), tooltip: translate('add_contact_sheet.add_contact')), ); @@ -202,13 +203,13 @@ class _ContactsBrowserState extends State onDoubleTap: _onStartChat, onTap: onContactSelected, onDelete: _onContactDeleted) - .paddingLTRB(0, 4, 0, 0); + .paddingLTRB(0, 4.scaled(context), 0, 0); case ContactsBrowserElementKind.invitation: final invitation = element.invitation!; return ContactInvitationItemWidget( contactInvitationRecord: invitation, disabled: false) - .paddingLTRB(0, 4, 0, 0); + .paddingLTRB(0, 4.scaled(context), 0, 0); } }, filter: (value) { @@ -242,9 +243,11 @@ class _ContactsBrowserState extends State } return filtered; }, - searchFieldHeight: 40, - listViewPadding: const EdgeInsets.fromLTRB(4, 0, 4, 4), - searchFieldPadding: const EdgeInsets.fromLTRB(4, 8, 4, 4), + searchFieldHeight: 40.scaled(context), + listViewPadding: + const EdgeInsets.fromLTRB(4, 0, 4, 4).scaled(context), + searchFieldPadding: + const EdgeInsets.fromLTRB(4, 8, 4, 4).scaled(context), emptyWidget: contactList == null ? waitingPage( text: translate('contact_list.loading_contacts')) @@ -254,8 +257,8 @@ class _ContactsBrowserState extends State searchFieldEnabled: contactList != null, inputDecoration: InputDecoration(labelText: translate('contact_list.search')), - secondaryWidget: - buildInvitationButton(context).paddingLTRB(4, 0, 0, 0)) + secondaryWidget: buildInvitationButton(context) + .paddingLTRB(4.scaled(context), 0, 0, 0)) .expanded() ]); } diff --git a/lib/contacts/views/contacts_page.dart b/lib/contacts/views/contacts_page.dart index 26a6f0d..c984b57 100644 --- a/lib/contacts/views/contacts_page.dart +++ b/lib/contacts/views/contacts_page.dart @@ -40,6 +40,7 @@ class _ContactsPageState extends State { return StyledScaffold( appBar: DefaultAppBar( + context: context, title: Text( !enableSplit && enableRight ? translate('contacts_dialog.edit_contact') @@ -47,6 +48,7 @@ class _ContactsPageState extends State { ), leading: IconButton( icon: const Icon(Icons.arrow_back), + iconSize: 24.scaled(context), onPressed: () { singleFuture((this, _kDoBackArrow), () async { final confirmed = await _onContactSelected(null); @@ -65,21 +67,21 @@ class _ContactsPageState extends State { if (_selectedContact != null) IconButton( icon: const Icon(Icons.chat_bubble), - iconSize: 24, + iconSize: 24.scaled(context), color: appBarTheme.iconColor, tooltip: translate('contacts_dialog.new_chat'), onPressed: () async { await _onChatStarted(_selectedContact!); - }).paddingLTRB(8, 0, 8, 0), + }), if (enableSplit && _selectedContact != null) IconButton( icon: const Icon(Icons.close), - iconSize: 24, + iconSize: 24.scaled(context), color: appBarTheme.iconColor, tooltip: translate('contacts_dialog.close_contact'), onPressed: () async { await _onContactSelected(null); - }).paddingLTRB(8, 0, 8, 0), + }), ]), body: LayoutBuilder(builder: (context, constraint) { final maxWidth = constraint.maxWidth; diff --git a/lib/contacts/views/edit_contact_form.dart b/lib/contacts/views/edit_contact_form.dart index 514f019..7ab6019 100644 --- a/lib/contacts/views/edit_contact_form.dart +++ b/lib/contacts/views/edit_contact_form.dart @@ -92,16 +92,8 @@ class _EditContactFormState extends State { Widget _editContactForm(BuildContext context) { final theme = Theme.of(context); final scale = theme.extension()!; - final scaleConfig = theme.extension()!; final textTheme = theme.textTheme; - late final Color border; - if (scaleConfig.useVisualIndicators && !scaleConfig.preferBorders) { - border = scale.primaryScale.elementBackground; - } else { - border = scale.primaryScale.subtleBorder; - } - return FormBuilder( key: _formKey, autovalidateMode: AutovalidateMode.onUserInteraction, @@ -116,18 +108,12 @@ class _EditContactFormState extends State { children: [ Row(children: [ const Spacer(), - AvatarWidget( - name: _currentValueNickname.isNotEmpty - ? _currentValueNickname - : widget.contact.profile.name, - size: 128, - borderColor: border, - foregroundColor: scale.primaryScale.primaryText, - backgroundColor: scale.primaryScale.primary, - scaleConfig: scaleConfig, - textStyle: theme.textTheme.titleLarge! - .copyWith(fontSize: 64), - ).paddingLTRB(0, 0, 0, 16), + StyledAvatar( + name: _currentValueNickname.isNotEmpty + ? _currentValueNickname + : widget.contact.profile.name, + size: 128) + .paddingLTRB(0, 0, 0, 16), const Spacer() ]), SelectableText(widget.contact.profile.name, @@ -211,10 +197,11 @@ class _EditContactFormState extends State { ElevatedButton( onPressed: _isModified ? _doSubmit : null, child: Row(mainAxisSize: MainAxisSize.min, children: [ - const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0), - Text(widget.submitText).paddingLTRB(0, 0, 4, 0) - ]), - ).paddingSymmetric(vertical: 4).alignAtCenter(), + Icon(Icons.check, size: 24.scaled(context)) + .paddingLTRB(0, 0, 4, 0), + Text(widget.submitText).paddingLTRB(0, 0, 4.scaled(context), 0) + ]).paddingAll(4.scaled(context)), + ).paddingSymmetric(vertical: 4.scaled(context)).alignAtCenter(), ], ), ); diff --git a/lib/keyboard_shortcuts.dart b/lib/keyboard_shortcuts.dart index 6708d72..7b952c8 100644 --- a/lib/keyboard_shortcuts.dart +++ b/lib/keyboard_shortcuts.dart @@ -32,6 +32,14 @@ class DeveloperPageIntent extends Intent { const DeveloperPageIntent(); } +class DisplayScaleUpIntent extends Intent { + const DisplayScaleUpIntent(); +} + +class DisplayScaleDownIntent extends Intent { + const DisplayScaleDownIntent(); +} + class KeyboardShortcuts extends StatelessWidget { const KeyboardShortcuts({required this.child, super.key}); @@ -57,7 +65,7 @@ class KeyboardShortcuts extends StatelessWidget { }); } - void changeBrightness(BuildContext context) { + void _changeBrightness(BuildContext context) { singleFuture(this, () async { final prefs = PreferencesRepository.instance.value; @@ -79,7 +87,7 @@ class KeyboardShortcuts extends StatelessWidget { }); } - void changeColor(BuildContext context) { + void _changeColor(BuildContext context) { singleFuture(this, () async { final prefs = PreferencesRepository.instance.value; final oldColor = prefs.themePreference.colorPreference; @@ -100,6 +108,54 @@ class KeyboardShortcuts extends StatelessWidget { }); } + void _displayScaleUp(BuildContext context) { + singleFuture(this, () async { + final prefs = PreferencesRepository.instance.value; + final oldIndex = displayScaleToIndex(prefs.themePreference.displayScale); + if (oldIndex == maxDisplayScaleIndex) { + return; + } + final newIndex = oldIndex + 1; + final newDisplayScaleName = indexToDisplayScaleName(newIndex); + + log.info('Changing display scale to $newDisplayScaleName'); + + final newPrefs = prefs.copyWith( + themePreference: prefs.themePreference + .copyWith(displayScale: indexToDisplayScale(newIndex))); + await PreferencesRepository.instance.set(newPrefs); + + if (context.mounted) { + ThemeSwitcher.of(context) + .changeTheme(theme: newPrefs.themePreference.themeData()); + } + }); + } + + void _displayScaleDown(BuildContext context) { + singleFuture(this, () async { + final prefs = PreferencesRepository.instance.value; + final oldIndex = displayScaleToIndex(prefs.themePreference.displayScale); + if (oldIndex == 0) { + return; + } + final newIndex = oldIndex - 1; + final newDisplayScaleName = indexToDisplayScaleName(newIndex); + + log.info('Changing display scale to $newDisplayScaleName'); + + final newPrefs = prefs.copyWith( + themePreference: prefs.themePreference + .copyWith(displayScale: indexToDisplayScale(newIndex))); + await PreferencesRepository.instance.set(newPrefs); + + if (context.mounted) { + ThemeSwitcher.of(context) + .changeTheme(theme: newPrefs.themePreference.themeData()); + } + }); + } + void _attachDetach(BuildContext context) { singleFuture(this, () async { if (ProcessorRepository.instance.processorConnectionState.isAttached) { @@ -125,44 +181,88 @@ class KeyboardShortcuts extends StatelessWidget { @override Widget build(BuildContext context) => ThemeSwitcher( builder: (context) => Shortcuts( - shortcuts: const { - SingleActivator( + shortcuts: { + ////////////////////////// Reload Theme + const SingleActivator( LogicalKeyboardKey.keyR, control: true, alt: true, - ): ReloadThemeIntent(), - SingleActivator( + ): const ReloadThemeIntent(), + ////////////////////////// Switch Brightness + const SingleActivator( LogicalKeyboardKey.keyB, control: true, alt: true, - ): ChangeBrightnessIntent(), - SingleActivator( + ): const ChangeBrightnessIntent(), + ////////////////////////// Change Color + const SingleActivator( LogicalKeyboardKey.keyC, control: true, alt: true, - ): ChangeColorIntent(), - SingleActivator( - LogicalKeyboardKey.keyA, - control: true, - alt: true, - ): AttachDetachIntent(), - SingleActivator( + ): const ChangeColorIntent(), + ////////////////////////// Attach/Detach + if (kIsDebugMode) + const SingleActivator( + LogicalKeyboardKey.keyA, + control: true, + alt: true, + ): const AttachDetachIntent(), + ////////////////////////// Show Developer Page + const SingleActivator( LogicalKeyboardKey.keyD, control: true, alt: true, - ): DeveloperPageIntent(), + ): const DeveloperPageIntent(), + ////////////////////////// Display Scale Up + SingleActivator( + LogicalKeyboardKey.equal, + meta: isMac || isiOS, + control: !(isMac || isiOS), + ): const DisplayScaleUpIntent(), + SingleActivator( + LogicalKeyboardKey.equal, + shift: true, + meta: isMac || isiOS, + control: !(isMac || isiOS), + ): const DisplayScaleUpIntent(), + SingleActivator( + LogicalKeyboardKey.add, + shift: true, + meta: isMac || isiOS, + control: !(isMac || isiOS), + ): const DisplayScaleUpIntent(), + SingleActivator( + LogicalKeyboardKey.numpadAdd, + meta: isMac || isiOS, + control: !(isMac || isiOS), + ): const DisplayScaleUpIntent(), + ////////////////////////// Display Scale Down + SingleActivator( + LogicalKeyboardKey.minus, + meta: isMac || isiOS, + control: !(isMac || isiOS), + ): const DisplayScaleDownIntent(), + SingleActivator( + LogicalKeyboardKey.numpadSubtract, + meta: isMac || isiOS, + control: !(isMac || isiOS), + ): const DisplayScaleDownIntent(), }, child: Actions(actions: >{ ReloadThemeIntent: CallbackAction( onInvoke: (intent) => reloadTheme(context)), ChangeBrightnessIntent: CallbackAction( - onInvoke: (intent) => changeBrightness(context)), + onInvoke: (intent) => _changeBrightness(context)), ChangeColorIntent: CallbackAction( - onInvoke: (intent) => changeColor(context)), + onInvoke: (intent) => _changeColor(context)), AttachDetachIntent: CallbackAction( onInvoke: (intent) => _attachDetach(context)), DeveloperPageIntent: CallbackAction( onInvoke: (intent) => _developerPage(context)), + DisplayScaleUpIntent: CallbackAction( + onInvoke: (intent) => _displayScaleUp(context)), + DisplayScaleDownIntent: CallbackAction( + onInvoke: (intent) => _displayScaleDown(context)), }, child: Focus(autofocus: true, child: child)))); ///////////////////////////////////////////////////////// diff --git a/lib/layout/default_app_bar.dart b/lib/layout/default_app_bar.dart index b9c0b41..7742dad 100644 --- a/lib/layout/default_app_bar.dart +++ b/lib/layout/default_app_bar.dart @@ -2,21 +2,27 @@ import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import '../theme/theme.dart'; + class DefaultAppBar extends AppBar { DefaultAppBar( - {super.title, + {required BuildContext context, + super.title, super.flexibleSpace, super.key, Widget? leading, super.actions}) : super( + toolbarHeight: 40.scaled(context), + leadingWidth: 40.scaled(context), leading: leading ?? Container( - margin: const EdgeInsets.all(4), + margin: const EdgeInsets.all(4).scaled(context), decoration: BoxDecoration( color: Colors.black.withAlpha(32), shape: BoxShape.circle), - child: - SvgPicture.asset('assets/images/vlogo.svg', height: 24) - .paddingAll(4))); + child: SvgPicture.asset('assets/images/vlogo.svg', + width: 24.scaled(context), + height: 24.scaled(context)) + .paddingAll(4.scaled(context)))); } diff --git a/lib/layout/home/drawer_menu/drawer_menu.dart b/lib/layout/home/drawer_menu/drawer_menu.dart index 0863b1f..33f2bc2 100644 --- a/lib/layout/home/drawer_menu/drawer_menu.dart +++ b/lib/layout/home/drawer_menu/drawer_menu.dart @@ -95,19 +95,15 @@ class _DrawerMenuState extends State { activeBorder = scale.primary; } - final avatar = AvatarWidget( + final avatar = StyledAvatar( name: name, - size: 34, - borderColor: border, - foregroundColor: loggedIn ? scale.primaryText : scale.subtleText, - backgroundColor: loggedIn ? scale.primary : scale.elementBackground, - scaleConfig: scaleConfig, - textStyle: theme.textTheme.titleLarge!, + size: 34.scaled(context), ); return AnimatedPadding( padding: EdgeInsets.fromLTRB(selected ? 0 : 8, selected ? 0 : 2, - selected ? 0 : 8, selected ? 0 : 2), + selected ? 0 : 8, selected ? 0 : 2) + .scaled(context), duration: const Duration(milliseconds: 50), child: MenuItemWidget( title: name, @@ -144,7 +140,7 @@ class _DrawerMenuState extends State { (scaleConfig.preferBorders || scaleConfig.useVisualIndicators) ? null : activeBorder, - minHeight: 48, + minHeight: 48.scaled(context), )); } @@ -196,7 +192,8 @@ class _DrawerMenuState extends State { color: scaleScheme.errorScale.subtleBorder, borderRadius: 12 * scaleConfig.borderRadiusScale), ); - loggedInAccounts.add(loggedInAccount.paddingLTRB(0, 0, 0, 8)); + loggedInAccounts + .add(loggedInAccount.paddingLTRB(0, 0, 0, 8.scaled(context))); } else { // Account is not logged in final scale = theme.extension()!.grayScale; @@ -246,8 +243,8 @@ class _DrawerMenuState extends State { } return IconButton( icon: icon, + padding: const EdgeInsets.all(12), color: border, - constraints: const BoxConstraints.expand(height: 48, width: 48), style: ButtonStyle( backgroundColor: WidgetStateProperty.resolveWith((states) { if (states.contains(WidgetState.hovered)) { @@ -286,7 +283,10 @@ class _DrawerMenuState extends State { final scale = scaleScheme.scale(_scaleKind); final settingsButton = _getButton( - icon: const Icon(Icons.settings), + icon: const Icon( + Icons.settings, + applyTextScaling: true, + ), tooltip: translate('menu.settings_tooltip'), scale: scale, scaleConfig: scaleConfig, @@ -295,7 +295,10 @@ class _DrawerMenuState extends State { }).paddingLTRB(0, 0, 16, 0); final addButton = _getButton( - icon: const Icon(Icons.add), + icon: const Icon( + Icons.add, + applyTextScaling: true, + ), tooltip: translate('menu.add_account_tooltip'), scale: scale, scaleConfig: scaleConfig, @@ -364,7 +367,7 @@ class _DrawerMenuState extends State { // : null) // .paddingLTRB(0, 0, 16, 0), GestureDetector( - onLongPress: () async { + onLongPress: () { context .findAncestorWidgetOfExactType()! .reloadTheme(context); diff --git a/lib/layout/home/drawer_menu/menu_item_widget.dart b/lib/layout/home/drawer_menu/menu_item_widget.dart index 1255458..80e466b 100644 --- a/lib/layout/home/drawer_menu/menu_item_widget.dart +++ b/lib/layout/home/drawer_menu/menu_item_widget.dart @@ -2,6 +2,8 @@ import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import '../../../theme/views/preferences/preferences.dart'; + class MenuItemWidget extends StatelessWidget { const MenuItemWidget({ required this.title, @@ -81,7 +83,7 @@ class MenuItemWidget extends StatelessWidget { hoverColor: footerButtonIconHoverColor, icon: Icon( footerButtonIcon, - size: 24, + size: 24.scaled(context), ), onPressed: footerCallback), ], diff --git a/lib/layout/home/home_account_ready.dart b/lib/layout/home/home_account_ready.dart index a2966a6..5ae1180 100644 --- a/lib/layout/home/home_account_ready.dart +++ b/lib/layout/home/home_account_ready.dart @@ -28,73 +28,81 @@ class _HomeAccountReadyState extends State { final theme = Theme.of(context); final scale = theme.extension()!; final scaleConfig = theme.extension()!; - return IconButton( - icon: const Icon(Icons.menu), - color: scaleConfig.preferBorders - ? scale.primaryScale.border - : scale.primaryScale.borderText, - constraints: const BoxConstraints.expand(height: 40, width: 40), - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all( - scaleConfig.preferBorders - ? scale.primaryScale.hoverElementBackground - : scale.primaryScale.hoverBorder), - shape: WidgetStateProperty.all( - RoundedRectangleBorder( - side: !scaleConfig.useVisualIndicators - ? BorderSide.none - : BorderSide( - strokeAlign: BorderSide.strokeAlignCenter, - color: scaleConfig.preferBorders - ? scale.primaryScale.border - : scale.primaryScale.borderText, - width: 2), - borderRadius: BorderRadius.all( - Radius.circular(8 * scaleConfig.borderRadiusScale))), - )), - tooltip: translate('menu.accounts_menu_tooltip'), - onPressed: () async { - final ctrl = context.read(); - await ctrl.toggle?.call(); - }); + return AspectRatio( + aspectRatio: 1, + child: IconButton( + icon: const Icon( + Icons.menu, + applyTextScaling: true, + ), + color: scaleConfig.preferBorders + ? scale.primaryScale.border + : scale.primaryScale.borderText, + style: ButtonStyle( + backgroundColor: WidgetStateProperty.all( + scaleConfig.preferBorders + ? scale.primaryScale.hoverElementBackground + : scale.primaryScale.hoverBorder), + shape: WidgetStateProperty.all( + RoundedRectangleBorder( + side: !scaleConfig.useVisualIndicators + ? BorderSide.none + : BorderSide( + strokeAlign: BorderSide.strokeAlignCenter, + color: scaleConfig.preferBorders + ? scale.primaryScale.border + : scale.primaryScale.borderText, + width: 2), + borderRadius: BorderRadius.all(Radius.circular( + 8 * scaleConfig.borderRadiusScale))), + )), + tooltip: translate('menu.accounts_menu_tooltip'), + onPressed: () async { + final ctrl = context.read(); + await ctrl.toggle?.call(); + })); }); Widget buildContactsButton() => Builder(builder: (context) { final theme = Theme.of(context); final scale = theme.extension()!; final scaleConfig = theme.extension()!; - return IconButton( - icon: const Icon(Icons.contacts), - color: scaleConfig.preferBorders - ? scale.primaryScale.border - : scale.primaryScale.borderText, - constraints: const BoxConstraints.expand(height: 40, width: 40), - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all( - scaleConfig.preferBorders - ? scale.primaryScale.hoverElementBackground - : scale.primaryScale.hoverBorder), - shape: WidgetStateProperty.all( - RoundedRectangleBorder( - side: !scaleConfig.useVisualIndicators - ? BorderSide.none - : BorderSide( - strokeAlign: BorderSide.strokeAlignCenter, - color: scaleConfig.preferBorders - ? scale.primaryScale.border - : scale.primaryScale.borderText, - width: 2), - borderRadius: BorderRadius.all( - Radius.circular(8 * scaleConfig.borderRadiusScale))), - )), - tooltip: translate('menu.contacts_tooltip'), - onPressed: () async { - await Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => const ContactsPage(), + return AspectRatio( + aspectRatio: 1, + child: IconButton( + icon: const Icon( + Icons.contacts, + applyTextScaling: true, ), - ); - }); + color: scaleConfig.preferBorders + ? scale.primaryScale.border + : scale.primaryScale.borderText, + style: ButtonStyle( + backgroundColor: WidgetStateProperty.all( + scaleConfig.preferBorders + ? scale.primaryScale.hoverElementBackground + : scale.primaryScale.hoverBorder), + shape: WidgetStateProperty.all( + RoundedRectangleBorder( + side: !scaleConfig.useVisualIndicators + ? BorderSide.none + : BorderSide( + strokeAlign: BorderSide.strokeAlignCenter, + color: scaleConfig.preferBorders + ? scale.primaryScale.border + : scale.primaryScale.borderText, + width: 2), + borderRadius: BorderRadius.all(Radius.circular( + 8 * scaleConfig.borderRadiusScale))), + )), + tooltip: translate('menu.contacts_tooltip'), + onPressed: () async { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => const ContactsPage(), + ), + ); + })); }); Widget buildLeftPane(BuildContext context) => Builder( @@ -112,14 +120,17 @@ class _HomeAccountReadyState extends State { ? scale.primaryScale.subtleBackground : scale.primaryScale.subtleBorder, child: Column(children: [ - Row(children: [ - buildMenuButton().paddingLTRB(0, 0, 8, 0), - ProfileWidget( - profile: profile, - showPronouns: false, - ).expanded(), - buildContactsButton().paddingLTRB(8, 0, 0, 0), - ]).paddingAll(8), + IntrinsicHeight( + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + buildMenuButton().paddingLTRB(0, 0, 8, 0), + ProfileWidget( + profile: profile, + showPronouns: false, + ).expanded(), + buildContactsButton().paddingLTRB(8, 0, 0, 0), + ])).paddingAll(8), const ChatListWidget().expanded() ])); }))); diff --git a/lib/layout/home/home_screen.dart b/lib/layout/home/home_screen.dart index b4c3b58..e774790 100644 --- a/lib/layout/home/home_screen.dart +++ b/lib/layout/home/home_screen.dart @@ -71,8 +71,9 @@ class HomeScreenState extends State context: context, title: translate('splash.beta_title'), child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon(Icons.warning, size: 64), + Icon(Icons.warning, size: 64.scaled(context)), RichText( + textScaler: MediaQuery.of(context).textScaler, textAlign: TextAlign.center, text: TextSpan( children: [ @@ -206,34 +207,36 @@ class HomeScreenState extends State .indexWhere((x) => x.superIdentity.recordKey == activeLocalAccount); final canClose = activeIndex != -1; + final drawer = ZoomDrawer( + controller: _zoomDrawerController, + menuScreen: Builder(builder: (context) { + final zoomDrawer = ZoomDrawer.of(context); + zoomDrawer!.stateNotifier.addListener(() { + if (zoomDrawer.isOpen()) { + FocusManager.instance.primaryFocus?.unfocus(); + } + }); + return const DrawerMenu(); + }), + mainScreen: Provider.value( + value: _zoomDrawerController, + child: Builder(builder: _buildAccountPageView)), + borderRadius: 0, + angle: 0, + openCurve: Curves.fastEaseInToSlowEaseOut, + closeCurve: Curves.fastEaseInToSlowEaseOut, + menuScreenTapClose: canClose, + mainScreenTapClose: canClose, + disableDragGesture: !canClose, + mainScreenScale: .25, + slideWidth: min(360, MediaQuery.of(context).size.width * 0.9), + ); + + final drawerWithAvoider = + isWeb ? drawer : KeyboardAvoider(curve: Curves.ease, child: drawer); + return DefaultTextStyle( - style: theme.textTheme.bodySmall!, - child: KeyboardAvoider( - curve: Curves.ease, - child: ZoomDrawer( - controller: _zoomDrawerController, - menuScreen: Builder(builder: (context) { - final zoomDrawer = ZoomDrawer.of(context); - zoomDrawer!.stateNotifier.addListener(() { - if (zoomDrawer.isOpen()) { - FocusManager.instance.primaryFocus?.unfocus(); - } - }); - return const DrawerMenu(); - }), - mainScreen: Provider.value( - value: _zoomDrawerController, - child: Builder(builder: _buildAccountPageView)), - borderRadius: 0, - angle: 0, - openCurve: Curves.fastEaseInToSlowEaseOut, - closeCurve: Curves.fastEaseInToSlowEaseOut, - menuScreenTapClose: canClose, - mainScreenTapClose: canClose, - disableDragGesture: !canClose, - mainScreenScale: .25, - slideWidth: min(360, MediaQuery.of(context).size.width * 0.9), - ))); + style: theme.textTheme.bodySmall!, child: drawerWithAvoider); } //////////////////////////////////////////////////////////////////////////// diff --git a/lib/notifications/views/notifications_preferences.dart b/lib/notifications/views/notifications_preferences.dart index 82f3555..8918fa9 100644 --- a/lib/notifications/views/notifications_preferences.dart +++ b/lib/notifications/views/notifications_preferences.dart @@ -1,24 +1,13 @@ import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_translate/flutter_translate.dart'; import '../../settings/settings.dart'; import '../../theme/theme.dart'; import '../notifications.dart'; -const String formFieldDisplayBetaWarning = 'displayBetaWarning'; -const String formFieldEnableBadge = 'enableBadge'; -const String formFieldEnableNotifications = 'enableNotifications'; -const String formFieldMessageNotificationContent = 'messageNotificationContent'; -const String formFieldInvitationAcceptMode = 'invitationAcceptMode'; -const String formFieldInvitationAcceptSound = 'invitationAcceptSound'; -const String formFieldMessageReceivedMode = 'messageReceivedMode'; -const String formFieldMessageReceivedSound = 'messageReceivedSound'; -const String formFieldMessageSentSound = 'messageSentSound'; - Widget buildSettingsPageNotificationPreferences( - {required BuildContext context, required void Function() onChanged}) { + {required BuildContext context}) { final theme = Theme.of(context); final scale = theme.extension()!; final scaleConfig = theme.extension()!; @@ -33,7 +22,6 @@ Widget buildSettingsPageNotificationPreferences( final newPrefs = preferencesRepository.value .copyWith(notificationsPreference: newNotificationsPreference); await preferencesRepository.set(newPrefs); - onChanged(); } List> notificationModeItems() { @@ -54,9 +42,10 @@ Widget buildSettingsPageNotificationPreferences( enabled: x.$2, child: Text( x.$3, - style: textTheme.labelSmall, + softWrap: false, + style: textTheme.labelMedium, textAlign: TextAlign.center, - ))); + ).fit(fit: BoxFit.scaleDown))); } return out; } @@ -77,7 +66,8 @@ Widget buildSettingsPageNotificationPreferences( enabled: x.$2, child: Text( x.$3, - style: textTheme.labelSmall, + softWrap: false, + style: textTheme.labelMedium, textAlign: TextAlign.center, ))); } @@ -110,7 +100,8 @@ Widget buildSettingsPageNotificationPreferences( enabled: x.$2, child: Text( x.$3, - style: textTheme.labelSmall, + softWrap: false, + style: textTheme.labelMedium, textAlign: TextAlign.center, ))); } @@ -127,66 +118,45 @@ Widget buildSettingsPageNotificationPreferences( ), child: Column(mainAxisSize: MainAxisSize.min, children: [ // Display Beta Warning - FormBuilderCheckbox( - name: formFieldDisplayBetaWarning, - title: Text(translate('settings_page.display_beta_warning'), - style: textTheme.labelMedium), - initialValue: notificationsPreference.displayBetaWarning, + StyledCheckbox( + label: translate('settings_page.display_beta_warning'), + value: notificationsPreference.displayBetaWarning, onChanged: (value) async { - if (value == null) { - return; - } final newNotificationsPreference = notificationsPreference.copyWith(displayBetaWarning: value); await updatePreferences(newNotificationsPreference); }), // Enable Badge - FormBuilderCheckbox( - name: formFieldEnableBadge, - title: Text(translate('settings_page.enable_badge'), - style: textTheme.labelMedium), - initialValue: notificationsPreference.enableBadge, + StyledCheckbox( + label: translate('settings_page.enable_badge'), + value: notificationsPreference.enableBadge, onChanged: (value) async { - if (value == null) { - return; - } final newNotificationsPreference = notificationsPreference.copyWith(enableBadge: value); await updatePreferences(newNotificationsPreference); }), // Enable Notifications - FormBuilderCheckbox( - name: formFieldEnableNotifications, - title: Text(translate('settings_page.enable_notifications'), - style: textTheme.labelMedium), - initialValue: notificationsPreference.enableNotifications, + StyledCheckbox( + label: translate('settings_page.enable_notifications'), + value: notificationsPreference.enableNotifications, onChanged: (value) async { - if (value == null) { - return; - } final newNotificationsPreference = notificationsPreference.copyWith(enableNotifications: value); await updatePreferences(newNotificationsPreference); }), - - FormBuilderDropdown( - name: formFieldMessageNotificationContent, - isDense: false, - decoration: InputDecoration( - labelText: translate('settings_page.message_notification_content')), - enabled: notificationsPreference.enableNotifications, - initialValue: notificationsPreference.messageNotificationContent, - onChanged: (value) async { - if (value == null) { - return; - } - final newNotificationsPreference = notificationsPreference.copyWith( - messageNotificationContent: value); - await updatePreferences(newNotificationsPreference); - }, + StyledDropdown( items: messageNotificationContentItems(), - ).paddingLTRB(0, 4, 0, 4), + value: notificationsPreference.messageNotificationContent, + decoratorLabel: translate('settings_page.message_notification_content'), + onChanged: !notificationsPreference.enableNotifications + ? null + : (value) async { + final newNotificationsPreference = notificationsPreference + .copyWith(messageNotificationContent: value); + await updatePreferences(newNotificationsPreference); + }, + ).paddingLTRB(0, 4.scaled(context), 0, 4.scaled(context)), // Notifications Table( @@ -199,95 +169,85 @@ Widget buildSettingsPageNotificationPreferences( color: scale.primaryScale.border, decorationColor: scale.primaryScale.border, decoration: TextDecoration.underline)) - .paddingAll(8), + .paddingAll(8.scaled(context)), Text(translate('settings_page.delivery'), textAlign: TextAlign.center, style: textTheme.titleMedium!.copyWith( color: scale.primaryScale.border, decorationColor: scale.primaryScale.border, decoration: TextDecoration.underline)) - .paddingAll(8), + .paddingAll(8.scaled(context)), Text(translate('settings_page.sound'), textAlign: TextAlign.center, style: textTheme.titleMedium!.copyWith( color: scale.primaryScale.border, decorationColor: scale.primaryScale.border, decoration: TextDecoration.underline)) - .paddingAll(8), + .paddingAll(8.scaled(context)), ]), TableRow(children: [ // Invitation accepted Text( textAlign: TextAlign.right, translate('settings_page.invitation_accepted')) - .paddingAll(8), - FormBuilderDropdown( - name: formFieldInvitationAcceptMode, - isDense: false, - enabled: notificationsPreference.enableNotifications, - initialValue: notificationsPreference.onInvitationAcceptedMode, - onChanged: (value) async { - if (value == null) { - return; - } - final newNotificationsPreference = notificationsPreference - .copyWith(onInvitationAcceptedMode: value); - await updatePreferences(newNotificationsPreference); - }, + .paddingAll(4.scaled(context)), + StyledDropdown( items: notificationModeItems(), - ).paddingAll(4), - FormBuilderDropdown( - name: formFieldInvitationAcceptSound, - isDense: false, - enabled: notificationsPreference.enableNotifications, - initialValue: notificationsPreference.onInvitationAcceptedSound, - onChanged: (value) async { - if (value == null) { - return; - } - final newNotificationsPreference = notificationsPreference - .copyWith(onInvitationAcceptedSound: value); - await updatePreferences(newNotificationsPreference); - }, + value: notificationsPreference.onInvitationAcceptedMode, + onChanged: !notificationsPreference.enableNotifications + ? null + : (value) async { + final newNotificationsPreference = + notificationsPreference.copyWith( + onInvitationAcceptedMode: value); + await updatePreferences(newNotificationsPreference); + }, + ).paddingAll(4.scaled(context)), + StyledDropdown( items: soundEffectItems(), - ).paddingLTRB(4, 4, 0, 4) + value: notificationsPreference.onInvitationAcceptedSound, + onChanged: !notificationsPreference.enableNotifications + ? null + : (value) async { + final newNotificationsPreference = + notificationsPreference.copyWith( + onInvitationAcceptedSound: value); + await updatePreferences(newNotificationsPreference); + }, + ).paddingLTRB( + 4.scaled(context), 4.scaled(context), 0, 4.scaled(context)) ]), // Message received TableRow(children: [ Text( textAlign: TextAlign.right, translate('settings_page.message_received')) - .paddingAll(8), - FormBuilderDropdown( - name: formFieldMessageReceivedMode, - isDense: false, - enabled: notificationsPreference.enableNotifications, - initialValue: notificationsPreference.onMessageReceivedMode, - onChanged: (value) async { - if (value == null) { - return; - } - final newNotificationsPreference = notificationsPreference - .copyWith(onMessageReceivedMode: value); - await updatePreferences(newNotificationsPreference); - }, + .paddingAll(4.scaled(context)), + StyledDropdown( items: notificationModeItems(), + value: notificationsPreference.onMessageReceivedMode, + onChanged: !notificationsPreference.enableNotifications + ? null + : (value) async { + final newNotificationsPreference = + notificationsPreference.copyWith( + onMessageReceivedMode: value); + await updatePreferences(newNotificationsPreference); + }, ).paddingAll(4), - FormBuilderDropdown( - name: formFieldMessageReceivedSound, - isDense: false, - enabled: notificationsPreference.enableNotifications, - initialValue: notificationsPreference.onMessageReceivedSound, - onChanged: (value) async { - if (value == null) { - return; - } - final newNotificationsPreference = notificationsPreference - .copyWith(onMessageReceivedSound: value); - await updatePreferences(newNotificationsPreference); - }, + StyledDropdown( items: soundEffectItems(), - ).paddingLTRB(4, 4, 0, 4) + value: notificationsPreference.onMessageReceivedSound, + onChanged: !notificationsPreference.enableNotifications + ? null + : (value) async { + final newNotificationsPreference = + notificationsPreference.copyWith( + onMessageReceivedSound: value); + await updatePreferences(newNotificationsPreference); + }, + ).paddingLTRB( + 4.scaled(context), 4.scaled(context), 0, 4.scaled(context)) ]), // Message sent @@ -295,25 +255,23 @@ Widget buildSettingsPageNotificationPreferences( Text( textAlign: TextAlign.right, translate('settings_page.message_sent')) - .paddingAll(8), + .paddingAll(4.scaled(context)), const SizedBox.shrink(), - FormBuilderDropdown( - name: formFieldMessageSentSound, - isDense: false, - enabled: notificationsPreference.enableNotifications, - initialValue: notificationsPreference.onMessageSentSound, - onChanged: (value) async { - if (value == null) { - return; - } - final newNotificationsPreference = notificationsPreference - .copyWith(onMessageSentSound: value); - await updatePreferences(newNotificationsPreference); - }, + StyledDropdown( items: soundEffectItems(), - ).paddingLTRB(4, 4, 0, 4) + value: notificationsPreference.onMessageSentSound, + onChanged: !notificationsPreference.enableNotifications + ? null + : (value) async { + final newNotificationsPreference = + notificationsPreference.copyWith( + onMessageSentSound: value); + await updatePreferences(newNotificationsPreference); + }, + ).paddingLTRB( + 4.scaled(context), 4.scaled(context), 0, 4.scaled(context)) ]), ]) - ]).paddingAll(8), + ]).paddingAll(8.scaled(context)), ); } diff --git a/lib/router/views/router_shell.dart b/lib/router/views/router_shell.dart index 164c452..d22129f 100644 --- a/lib/router/views/router_shell.dart +++ b/lib/router/views/router_shell.dart @@ -1,7 +1,9 @@ +import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import '../../keyboard_shortcuts.dart'; import '../../notifications/notifications.dart'; +import '../../settings/settings.dart'; import '../../theme/theme.dart'; class RouterShell extends StatelessWidget { @@ -10,7 +12,13 @@ class RouterShell extends StatelessWidget { @override Widget build(BuildContext context) => PopControl( dismissible: false, - child: NotificationsWidget(child: KeyboardShortcuts(child: _child))); + child: AsyncBlocBuilder( + builder: (context, state) => MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaler: + TextScaler.linear(state.themePreference.displayScale)), + child: NotificationsWidget( + child: KeyboardShortcuts(child: _child))))); final Widget _child; } diff --git a/lib/settings/models/preferences.dart b/lib/settings/models/preferences.dart index 3ef683e..a7432d6 100644 --- a/lib/settings/models/preferences.dart +++ b/lib/settings/models/preferences.dart @@ -20,7 +20,7 @@ sealed class LockPreference with _$LockPreference { factory LockPreference.fromJson(dynamic json) => _$LockPreferenceFromJson(json as Map); - static const LockPreference defaults = LockPreference(); + static const defaults = LockPreference(); } // Theme supports multiple translations @@ -49,5 +49,5 @@ sealed class Preferences with _$Preferences { factory Preferences.fromJson(dynamic json) => _$PreferencesFromJson(json as Map); - static const Preferences defaults = Preferences(); + static const defaults = Preferences(); } diff --git a/lib/settings/settings_page.dart b/lib/settings/settings_page.dart index 2a05f08..810b20e 100644 --- a/lib/settings/settings_page.dart +++ b/lib/settings/settings_page.dart @@ -1,7 +1,6 @@ import 'package:animated_theme_switcher/animated_theme_switcher.dart'; import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_translate/flutter_translate.dart'; import 'package:go_router/go_router.dart'; @@ -11,62 +10,53 @@ import '../theme/theme.dart'; import '../veilid_processor/veilid_processor.dart'; import 'settings.dart'; -class SettingsPage extends StatefulWidget { +class SettingsPage extends StatelessWidget { const SettingsPage({super.key}); - @override - SettingsPageState createState() => SettingsPageState(); -} - -class SettingsPageState extends State { - final _formKey = GlobalKey(); - static const String formFieldTheme = 'theme'; - static const String formFieldBrightness = 'brightness'; - - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) => AsyncBlocBuilder( builder: (context, state) => ThemeSwitcher.withTheme( - builder: (_, switcher, theme) => StyledScaffold( + builder: (_, switcher, theme) => StyledScaffold( appBar: DefaultAppBar( + context: context, title: Text(translate('settings_page.titlebar')), leading: IconButton( - icon: const Icon(Icons.arrow_back), + iconSize: 24.scaled(context), + icon: Icon(Icons.arrow_back), onPressed: () => GoRouterHelper(context).pop(), ), actions: [ const SignalStrengthMeterWidget() .paddingLTRB(16, 0, 16, 0), ]), - body: ThemeSwitchingArea( - child: FormBuilder( - key: _formKey, - child: ListView( - padding: const EdgeInsets.all(8), - children: [ - buildSettingsPageColorPreferences( - context: context, - switcher: switcher, - onChanged: () => setState(() {})) - .paddingLTRB(0, 8, 0, 0), - buildSettingsPageBrightnessPreferences( - context: context, - switcher: switcher, - onChanged: () => setState(() {})), - buildSettingsPageWallpaperPreferences( - context: context, - switcher: switcher, - onChanged: () => setState(() {})), - buildSettingsPageNotificationPreferences( - context: context, - onChanged: () => setState(() {})), - ].map((x) => x.paddingLTRB(0, 0, 0, 8)).toList(), + body: ListView( + padding: const EdgeInsets.all(8).scaled(context), + children: [ + buildSettingsPageColorPreferences( + context: context, + switcher: switcher, ), - ).paddingSymmetric(horizontal: 8, vertical: 8), - )))); + buildSettingsPageBrightnessPreferences( + context: context, + switcher: switcher, + ), + buildSettingsPageDisplayScalePreferences( + context: context, + switcher: switcher, + ), + buildSettingsPageWallpaperPreferences( + context: context, + switcher: switcher, + ), + buildSettingsPageNotificationPreferences( + context: context, + ), + ] + .map((x) => x.paddingLTRB(0, 0, 0, 8.scaled(context))) + .toList(), + ).paddingSymmetric(vertical: 4.scaled(context)), + ).paddingSymmetric( + horizontal: 8.scaled(context), vertical: 8.scaled(context)), + )); } diff --git a/lib/theme/models/contrast_generator.dart b/lib/theme/models/contrast_generator.dart index 314e28a..05c5f55 100644 --- a/lib/theme/models/contrast_generator.dart +++ b/lib/theme/models/contrast_generator.dart @@ -308,6 +308,13 @@ ThemeData contrastGenerator({ side: elementBorderWidgetStateProperty(), backgroundColor: elementBackgroundWidgetStateProperty())); + final sliderTheme = SliderThemeData.fromPrimaryColors( + primaryColor: scheme.primaryScale.borderText, + primaryColorDark: scheme.primaryScale.border, + primaryColorLight: scheme.primaryScale.border, + valueIndicatorTextStyle: textTheme.labelMedium! + .copyWith(color: scheme.primaryScale.borderText)); + final themeData = baseThemeData.copyWith( // chipTheme: baseThemeData.chipTheme.copyWith( // backgroundColor: scaleScheme.primaryScale.elementBackground, @@ -316,6 +323,7 @@ ThemeData contrastGenerator({ // checkmarkColor: scaleScheme.primaryScale.border, // side: BorderSide(color: scaleScheme.primaryScale.border)), elevatedButtonTheme: elevatedButtonTheme, + sliderTheme: sliderTheme, textSelectionTheme: TextSelectionThemeData( cursorColor: scheme.primaryScale.appText, selectionColor: scheme.primaryScale.appText.withAlpha(0x7F), diff --git a/lib/theme/models/scale_theme/scale_theme.dart b/lib/theme/models/scale_theme/scale_theme.dart index c3217ea..755bd54 100644 --- a/lib/theme/models/scale_theme/scale_theme.dart +++ b/lib/theme/models/scale_theme/scale_theme.dart @@ -132,6 +132,13 @@ class ScaleTheme extends ThemeExtension { iconColor: elementColorWidgetStateProperty(), )); + final sliderTheme = SliderThemeData.fromPrimaryColors( + primaryColor: scheme.primaryScale.hoverBorder, + primaryColorDark: scheme.primaryScale.border, + primaryColorLight: scheme.primaryScale.border, + valueIndicatorTextStyle: textTheme.labelMedium! + .copyWith(color: scheme.primaryScale.borderText)); + final themeData = baseThemeData.copyWith( scrollbarTheme: baseThemeData.scrollbarTheme.copyWith( thumbColor: WidgetStateProperty.resolveWith((states) { @@ -183,6 +190,7 @@ class ScaleTheme extends ThemeExtension { elevatedButtonTheme: elevatedButtonTheme, inputDecorationTheme: ScaleInputDecoratorTheme(scheme, config, textTheme), + sliderTheme: sliderTheme, extensions: >[scheme, config, this]); return themeData; diff --git a/lib/theme/models/theme_preference.dart b/lib/theme/models/theme_preference.dart index aaad52d..44d06d8 100644 --- a/lib/theme/models/theme_preference.dart +++ b/lib/theme/models/theme_preference.dart @@ -61,7 +61,7 @@ sealed class ThemePreferences with _$ThemePreferences { factory ThemePreferences.fromJson(dynamic json) => _$ThemePreferencesFromJson(json as Map); - static const ThemePreferences defaults = ThemePreferences(); + static const defaults = ThemePreferences(); } extension ThemePreferencesExt on ThemePreferences { diff --git a/lib/theme/views/avatar_widget.dart b/lib/theme/views/avatar_widget.dart deleted file mode 100644 index 42bea11..0000000 --- a/lib/theme/views/avatar_widget.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:awesome_extensions/awesome_extensions.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; - -import '../theme.dart'; - -class AvatarWidget extends StatelessWidget { - const AvatarWidget({ - required String name, - required double size, - required Color borderColor, - required Color foregroundColor, - required Color backgroundColor, - required ScaleConfig scaleConfig, - required TextStyle textStyle, - super.key, - ImageProvider? imageProvider, - }) : _name = name, - _size = size, - _borderColor = borderColor, - _foregroundColor = foregroundColor, - _backgroundColor = backgroundColor, - _scaleConfig = scaleConfig, - _textStyle = textStyle, - _imageProvider = imageProvider; - - @override - Widget build(BuildContext context) { - final abbrev = _name.split(' ').map((s) => s.isEmpty ? '' : s[0]).join(); - late final String shortname; - if (abbrev.length >= 3) { - shortname = abbrev[0] + abbrev[1] + abbrev[abbrev.length - 1]; - } else { - shortname = abbrev; - } - - return Container( - height: _size, - width: _size, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: _borderColor, - width: 1 * (_size ~/ 32 + 1), - strokeAlign: BorderSide.strokeAlignOutside)), - child: AvatarImage( - //size: 32, - backgroundImage: _imageProvider, - backgroundColor: - _scaleConfig.useVisualIndicators && !_scaleConfig.preferBorders - ? _foregroundColor - : _backgroundColor, - child: Text( - shortname.isNotEmpty ? shortname : '?', - softWrap: false, - style: _textStyle.copyWith( - color: _scaleConfig.useVisualIndicators && - !_scaleConfig.preferBorders - ? _backgroundColor - : _foregroundColor, - ), - ).fit().paddingAll(_size / 16))); - } - - //////////////////////////////////////////////////////////////////////////// - final String _name; - final double _size; - final Color _borderColor; - final Color _foregroundColor; - final Color _backgroundColor; - final ScaleConfig _scaleConfig; - final TextStyle _textStyle; - final ImageProvider? _imageProvider; -} diff --git a/lib/theme/views/enter_password.dart b/lib/theme/views/enter_password.dart index fc876da..f28b69e 100644 --- a/lib/theme/views/enter_password.dart +++ b/lib/theme/views/enter_password.dart @@ -32,7 +32,7 @@ class _EnterPasswordDialogState extends State { final passwordController = TextEditingController(); final focusNode = FocusNode(); final formKey = GlobalKey(); - bool _passwordVisible = false; + var _passwordVisible = false; @override void initState() { @@ -47,7 +47,6 @@ class _EnterPasswordDialogState extends State { } @override - // ignore: prefer_expression_function_bodies Widget build(BuildContext context) { final theme = Theme.of(context); final scale = theme.extension()!; diff --git a/lib/theme/views/brightness_preferences.dart b/lib/theme/views/preferences/brightness_preferences.dart similarity index 59% rename from lib/theme/views/brightness_preferences.dart rename to lib/theme/views/preferences/brightness_preferences.dart index 7a1bb1d..a149483 100644 --- a/lib/theme/views/brightness_preferences.dart +++ b/lib/theme/views/preferences/brightness_preferences.dart @@ -1,14 +1,12 @@ import 'package:animated_theme_switcher/animated_theme_switcher.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_translate/flutter_translate.dart'; -import '../../settings/settings.dart'; -import '../models/models.dart'; +import '../../../settings/settings.dart'; +import '../../models/models.dart'; +import '../views.dart'; -const String formFieldBrightness = 'brightness'; - -List> _getBrightnessDropdownItems() { +List> _getBrightnessDropdownItems() { const brightnessPrefs = BrightnessPreference.values; final brightnessNames = { BrightnessPreference.system: translate('brightness.system'), @@ -22,25 +20,21 @@ List> _getBrightnessDropdownItems() { } Widget buildSettingsPageBrightnessPreferences( - {required BuildContext context, - required void Function() onChanged, - required ThemeSwitcherState switcher}) { + {required BuildContext context, required ThemeSwitcherState switcher}) { final preferencesRepository = PreferencesRepository.instance; final themePreferences = preferencesRepository.value.themePreference; - return FormBuilderDropdown( - name: formFieldBrightness, - decoration: InputDecoration( - label: Text(translate('settings_page.brightness_mode'))), + + return StyledDropdown( items: _getBrightnessDropdownItems(), - initialValue: themePreferences.brightnessPreference, + value: themePreferences.brightnessPreference, + decoratorLabel: translate('settings_page.brightness_mode'), onChanged: (value) async { - final newThemePrefs = themePreferences.copyWith( - brightnessPreference: value as BrightnessPreference); + final newThemePrefs = + themePreferences.copyWith(brightnessPreference: value); final newPrefs = preferencesRepository.value .copyWith(themePreference: newThemePrefs); await preferencesRepository.set(newPrefs); switcher.changeTheme(theme: newThemePrefs.themeData()); - onChanged(); }); } diff --git a/lib/theme/views/color_preferences.dart b/lib/theme/views/preferences/color_preferences.dart similarity index 54% rename from lib/theme/views/color_preferences.dart rename to lib/theme/views/preferences/color_preferences.dart index a9a8841..2c14a93 100644 --- a/lib/theme/views/color_preferences.dart +++ b/lib/theme/views/preferences/color_preferences.dart @@ -1,16 +1,12 @@ import 'package:animated_theme_switcher/animated_theme_switcher.dart'; -import 'package:async_tools/async_tools.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_translate/flutter_translate.dart'; -import '../../settings/settings.dart'; -import '../models/models.dart'; +import '../../../settings/settings.dart'; +import '../../models/models.dart'; +import '../views.dart'; -const String formFieldTheme = 'theme'; -const String _kSwitchTheme = 'switchTheme'; - -List> _getThemeDropdownItems() { +List> _getThemeDropdownItems() { const colorPrefs = ColorPreference.values; final colorNames = { ColorPreference.scarlet: translate('themes.scarlet'), @@ -34,27 +30,20 @@ List> _getThemeDropdownItems() { } Widget buildSettingsPageColorPreferences( - {required BuildContext context, - required void Function() onChanged, - required ThemeSwitcherState switcher}) { + {required BuildContext context, required ThemeSwitcherState switcher}) { final preferencesRepository = PreferencesRepository.instance; final themePreferences = preferencesRepository.value.themePreference; - return FormBuilderDropdown( - name: formFieldTheme, - decoration: - InputDecoration(label: Text(translate('settings_page.color_theme'))), - items: _getThemeDropdownItems(), - initialValue: themePreferences.colorPreference, - onChanged: (value) { - singleFuture(_kSwitchTheme, () async { - final newThemePrefs = themePreferences.copyWith( - colorPreference: value as ColorPreference); - final newPrefs = preferencesRepository.value - .copyWith(themePreference: newThemePrefs); - await preferencesRepository.set(newPrefs); - switcher.changeTheme(theme: newThemePrefs.themeData()); - onChanged(); - }); + return StyledDropdown( + items: _getThemeDropdownItems(), + value: themePreferences.colorPreference, + decoratorLabel: translate('settings_page.color_theme'), + onChanged: (value) async { + final newThemePrefs = themePreferences.copyWith(colorPreference: value); + final newPrefs = preferencesRepository.value + .copyWith(themePreference: newThemePrefs); + + await preferencesRepository.set(newPrefs); + switcher.changeTheme(theme: newThemePrefs.themeData()); }); } diff --git a/lib/theme/views/preferences/display_scale_preferences.dart b/lib/theme/views/preferences/display_scale_preferences.dart new file mode 100644 index 0000000..84bf97d --- /dev/null +++ b/lib/theme/views/preferences/display_scale_preferences.dart @@ -0,0 +1,109 @@ +import 'dart:math'; + +import 'package:animated_theme_switcher/animated_theme_switcher.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_translate/flutter_translate.dart'; + +import '../../../settings/settings.dart'; +import '../../models/models.dart'; +import '../views.dart'; + +const _scales = [ + 1 / (1 + 1 / 2), + 1 / (1 + 1 / 3), + 1 / (1 + 1 / 4), + 1, + 1 + (1 / 4), + 1 + (1 / 2), + 1 + (1 / 1), +]; +const _scaleNames = [ + '-3', + '-2', + '-1', + '0', + '1', + '2', + '3', +]; + +const _scaleNumMult = [ + 1 / (1 + 1 / 2), + 1 / (1 + 1 / 3), + 1 / (1 + 1 / 4), + 1, + 1 + 1 / 4, + 1 + 1 / 2, + 1 + 1 / 1, +]; + +int displayScaleToIndex(double displayScale) { + final idx = _scales.indexWhere((elem) => elem > displayScale); + final currentScaleIdx = idx == -1 ? _scales.length - 1 : max(0, idx - 1); + return currentScaleIdx; +} + +double indexToDisplayScale(int scaleIdx) { + final displayScale = + _scales[max(min(scaleIdx, _scales.length - 1), 0)].toDouble(); + return displayScale; +} + +String indexToDisplayScaleName(int scaleIdx) => + _scaleNames[max(min(scaleIdx, _scales.length - 1), 0)]; + +final maxDisplayScaleIndex = _scales.length - 1; + +Widget buildSettingsPageDisplayScalePreferences( + {required BuildContext context, required ThemeSwitcherState switcher}) { + final preferencesRepository = PreferencesRepository.instance; + final themePreferences = preferencesRepository.value.themePreference; + + final currentScaleIdx = displayScaleToIndex(themePreferences.displayScale); + final currentScaleName = indexToDisplayScaleName(currentScaleIdx); + + return StyledSlider( + value: currentScaleIdx.toDouble(), + label: currentScaleName, + decoratorLabel: translate('settings_page.display_scale'), + max: _scales.length - 1.toDouble(), + divisions: _scales.length - 1, + leftWidget: const Icon(Icons.text_decrease), + rightWidget: const Icon(Icons.text_increase), + onChanged: (value) async { + final scaleIdx = value.toInt(); + final displayScale = indexToDisplayScale(scaleIdx); + final newThemePrefs = + themePreferences.copyWith(displayScale: displayScale); + final newPrefs = preferencesRepository.value + .copyWith(themePreference: newThemePrefs); + + await preferencesRepository.set(newPrefs); + switcher.changeTheme(theme: newThemePrefs.themeData()); + }); +} + +extension DisplayScaledNum on num { + double scaled(BuildContext context) { + final prefs = context.watch().state.asData?.value ?? + PreferencesRepository.instance.value; + final currentScaleIdx = + displayScaleToIndex(prefs.themePreference.displayScale); + return this * _scaleNumMult[currentScaleIdx]; + } +} + +extension DisplayScaledEdgeInsets on EdgeInsets { + EdgeInsets scaled(BuildContext context) { + final prefs = context.watch().state.asData?.value ?? + PreferencesRepository.instance.value; + final currentScaleIdx = + displayScaleToIndex(prefs.themePreference.displayScale); + return EdgeInsets.fromLTRB( + left * _scaleNumMult[currentScaleIdx], + top * _scaleNumMult[currentScaleIdx], + right * _scaleNumMult[currentScaleIdx], + bottom * _scaleNumMult[currentScaleIdx]); + } +} diff --git a/lib/theme/views/preferences/preferences.dart b/lib/theme/views/preferences/preferences.dart new file mode 100644 index 0000000..ddac4c1 --- /dev/null +++ b/lib/theme/views/preferences/preferences.dart @@ -0,0 +1,4 @@ +export 'brightness_preferences.dart'; +export 'color_preferences.dart'; +export 'display_scale_preferences.dart'; +export 'wallpaper_preferences.dart'; diff --git a/lib/theme/views/preferences/wallpaper_preferences.dart b/lib/theme/views/preferences/wallpaper_preferences.dart new file mode 100644 index 0000000..050e294 --- /dev/null +++ b/lib/theme/views/preferences/wallpaper_preferences.dart @@ -0,0 +1,25 @@ +import 'package:animated_theme_switcher/animated_theme_switcher.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_translate/flutter_translate.dart'; + +import '../../../settings/settings.dart'; +import '../../models/models.dart'; +import '../views.dart'; + +Widget buildSettingsPageWallpaperPreferences( + {required BuildContext context, required ThemeSwitcherState switcher}) { + final preferencesRepository = PreferencesRepository.instance; + final themePreferences = preferencesRepository.value.themePreference; + + return StyledCheckbox( + value: themePreferences.enableWallpaper, + label: translate('settings_page.enable_wallpaper'), + onChanged: (value) async { + final newThemePrefs = themePreferences.copyWith(enableWallpaper: value); + final newPrefs = preferencesRepository.value + .copyWith(themePreference: newThemePrefs); + + await preferencesRepository.set(newPrefs); + switcher.changeTheme(theme: newThemePrefs.themeData()); + }); +} diff --git a/lib/theme/views/responsive.dart b/lib/theme/views/responsive.dart index 0182c9f..4ce42d8 100644 --- a/lib/theme/views/responsive.dart +++ b/lib/theme/views/responsive.dart @@ -3,6 +3,10 @@ import 'package:flutter/material.dart'; final isAndroid = !kIsWeb && defaultTargetPlatform == TargetPlatform.android; final isiOS = !kIsWeb && defaultTargetPlatform == TargetPlatform.iOS; +final isMac = !kIsWeb && defaultTargetPlatform == TargetPlatform.macOS; +final isWindows = !kIsWeb && defaultTargetPlatform == TargetPlatform.windows; +final isLinux = !kIsWeb && defaultTargetPlatform == TargetPlatform.linux; + final isMobile = !kIsWeb && (defaultTargetPlatform == TargetPlatform.iOS || defaultTargetPlatform == TargetPlatform.android); diff --git a/lib/theme/views/styled_alert.dart b/lib/theme/views/styled_widgets/styled_alert.dart similarity index 99% rename from lib/theme/views/styled_alert.dart rename to lib/theme/views/styled_widgets/styled_alert.dart index 1215c84..4dec616 100644 --- a/lib/theme/views/styled_alert.dart +++ b/lib/theme/views/styled_widgets/styled_alert.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_translate/flutter_translate.dart'; import 'package:rflutter_alert/rflutter_alert.dart'; -import '../theme.dart'; +import '../../theme.dart'; AlertStyle _alertStyle(BuildContext context) { final theme = Theme.of(context); @@ -186,6 +186,7 @@ Future showAlertWidgetModal( child: Text( translate('button.ok'), style: _buttonTextStyle(context), + softWrap: true, ), ) ], diff --git a/lib/theme/views/styled_widgets/styled_avatar.dart b/lib/theme/views/styled_widgets/styled_avatar.dart new file mode 100644 index 0000000..dde39f2 --- /dev/null +++ b/lib/theme/views/styled_widgets/styled_avatar.dart @@ -0,0 +1,77 @@ +import 'package:awesome_extensions/awesome_extensions.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +import '../../theme.dart'; + +class StyledAvatar extends StatelessWidget { + const StyledAvatar({ + required String name, + required double size, + bool enabled = true, + super.key, + ImageProvider? imageProvider, + }) : _name = name, + _size = size, + _imageProvider = imageProvider, + _enabled = enabled; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final scaleTheme = Theme.of(context).extension()!; + + final borderColor = scaleTheme.config.useVisualIndicators + ? scaleTheme.scheme.primaryScale.primaryText + : scaleTheme.scheme.primaryScale.subtleBorder; + final foregroundColor = !_enabled + ? scaleTheme.scheme.grayScale.primaryText + : scaleTheme.scheme.primaryScale.calloutText; + final backgroundColor = !_enabled + ? scaleTheme.scheme.grayScale.primary + : scaleTheme.scheme.primaryScale.calloutBackground; + final scaleConfig = scaleTheme.config; + final textStyle = theme.textTheme.titleLarge!.copyWith(fontSize: _size / 2); + + final abbrev = _name.split(' ').map((s) => s.isEmpty ? '' : s[0]).join(); + late final String shortname; + if (abbrev.length >= 3) { + shortname = abbrev[0] + abbrev[1] + abbrev[abbrev.length - 1]; + } else { + shortname = abbrev; + } + + return Container( + height: _size, + width: _size, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: !scaleConfig.useVisualIndicators + ? null + : Border.all( + color: borderColor, + width: 1 * (_size ~/ 16 + 1), + strokeAlign: BorderSide.strokeAlignOutside)), + child: AvatarImage( + backgroundImage: _imageProvider, + backgroundColor: scaleConfig.useVisualIndicators + ? foregroundColor + : backgroundColor, + child: Text( + shortname.isNotEmpty ? shortname : '?', + softWrap: false, + textScaler: MediaQuery.of(context).textScaler, + style: textStyle.copyWith( + color: scaleConfig.useVisualIndicators + ? backgroundColor + : foregroundColor, + ), + ).paddingAll(4.scaled(context)).fit(fit: BoxFit.scaleDown))); + } + + //////////////////////////////////////////////////////////////////////////// + final String _name; + final double _size; + final ImageProvider? _imageProvider; + final bool _enabled; +} diff --git a/lib/theme/views/option_box.dart b/lib/theme/views/styled_widgets/styled_button_box.dart similarity index 75% rename from lib/theme/views/option_box.dart rename to lib/theme/views/styled_widgets/styled_button_box.dart index 06a3293..811e01c 100644 --- a/lib/theme/views/option_box.dart +++ b/lib/theme/views/styled_widgets/styled_button_box.dart @@ -1,10 +1,10 @@ import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/material.dart'; -import '../theme.dart'; +import '../../theme.dart'; -class OptionBox extends StatelessWidget { - const OptionBox( +class StyledButtonBox extends StatelessWidget { + const StyledButtonBox( {required String instructions, required IconData buttonIcon, required String buttonText, @@ -41,12 +41,15 @@ class OptionBox extends StatelessWidget { onPressed: _onClick, child: Row(mainAxisSize: MainAxisSize.min, children: [ Icon(_buttonIcon, - size: 24, color: scale.primaryScale.appText) - .paddingLTRB(0, 8, 8, 8), + size: 24.scaled(context), + color: scale.primaryScale.appText) + .paddingLTRB(0, 8.scaled(context), + 8.scaled(context), 8.scaled(context)), Text(textAlign: TextAlign.center, _buttonText) - ])).paddingLTRB(0, 12, 0, 0).toCenter() - ]).paddingAll(12)) - .paddingLTRB(24, 0, 24, 12); + ])).paddingLTRB(0, 12.scaled(context), 0, 0).toCenter() + ]).paddingAll(12.scaled(context))) + .paddingLTRB( + 24.scaled(context), 0, 24.scaled(context), 12.scaled(context)); } final String _instructions; diff --git a/lib/theme/views/styled_widgets/styled_checkbox.dart b/lib/theme/views/styled_widgets/styled_checkbox.dart new file mode 100644 index 0000000..7eb3649 --- /dev/null +++ b/lib/theme/views/styled_widgets/styled_checkbox.dart @@ -0,0 +1,63 @@ +import 'package:async_tools/async_tools.dart'; +import 'package:awesome_extensions/awesome_extensions_flutter.dart'; +import 'package:flutter/material.dart'; + +import '../views.dart'; + +const _kStyledCheckboxChanged = 'kStyledCheckboxChanged'; + +class StyledCheckbox extends StatelessWidget { + const StyledCheckbox( + {required bool value, + required String label, + String? decoratorLabel, + Future Function(bool)? onChanged, + super.key}) + : _value = value, + _onChanged = onChanged, + _label = label, + _decoratorLabel = decoratorLabel; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final textTheme = theme.textTheme; + + var textStyle = textTheme.labelLarge!; + if (_onChanged == null) { + textStyle = textStyle.copyWith(color: textStyle.color!.withAlpha(127)); + } + + Widget ctrl = Row(children: [ + Transform.scale( + scale: 1.scaled(context), + child: Checkbox( + value: _value, + onChanged: _onChanged == null + ? null + : (value) { + if (value == null) { + return; + } + singleFuture((this, _kStyledCheckboxChanged), () async { + await _onChanged(value); + }); + })), + Text(_label, style: textStyle).paddingAll(4.scaled(context)), + ]); + + if (_decoratorLabel != null) { + ctrl = ctrl + .paddingLTRB(4.scaled(context), 4.scaled(context), 4.scaled(context), + 4.scaled(context)) + .decoratorLabel(context, _decoratorLabel); + } + + return ctrl; + } + + final String _label; + final String? _decoratorLabel; + final Future Function(bool)? _onChanged; + final bool _value; +} diff --git a/lib/theme/views/styled_dialog.dart b/lib/theme/views/styled_widgets/styled_dialog.dart similarity index 98% rename from lib/theme/views/styled_dialog.dart rename to lib/theme/views/styled_widgets/styled_dialog.dart index 75a0f6b..4106f1d 100644 --- a/lib/theme/views/styled_dialog.dart +++ b/lib/theme/views/styled_widgets/styled_dialog.dart @@ -2,7 +2,7 @@ import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import '../theme.dart'; +import '../../theme.dart'; class StyledDialog extends StatelessWidget { const StyledDialog({required this.title, required this.child, super.key}); diff --git a/lib/theme/views/styled_widgets/styled_dropdown.dart b/lib/theme/views/styled_widgets/styled_dropdown.dart new file mode 100644 index 0000000..3af6424 --- /dev/null +++ b/lib/theme/views/styled_widgets/styled_dropdown.dart @@ -0,0 +1,59 @@ +import 'package:async_tools/async_tools.dart'; +import 'package:awesome_extensions/awesome_extensions_flutter.dart'; +import 'package:flutter/material.dart'; + +import '../../models/models.dart'; +import '../views.dart'; + +const _kStyledDropdownChanged = 'kStyledDropdownChanged'; + +class StyledDropdown extends StatelessWidget { + const StyledDropdown( + {required List> items, + required T value, + String? decoratorLabel, + Future Function(T)? onChanged, + super.key}) + : _items = items, + _onChanged = onChanged, + _decoratorLabel = decoratorLabel, + _value = value; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final scheme = theme.extension()!; + + Widget ctrl = DropdownButton( + isExpanded: true, + padding: const EdgeInsets.fromLTRB(4, 0, 4, 0).scaled(context), + focusColor: theme.focusColor, + dropdownColor: scheme.primaryScale.elementBackground, + iconEnabledColor: scheme.primaryScale.appText, + iconDisabledColor: scheme.primaryScale.appText.withAlpha(127), + items: _items, + value: _value, + style: theme.textTheme.labelLarge, + onChanged: _onChanged == null + ? null + : (value) { + if (value == null) { + return; + } + singleFuture((this, _kStyledDropdownChanged), () async { + await _onChanged(value); + }); + }); + if (_decoratorLabel != null) { + ctrl = ctrl + .paddingLTRB(0, 4.scaled(context), 0, 4.scaled(context)) + .decoratorLabel(context, _decoratorLabel); + } + return ctrl; + } + + final List> _items; + final String? _decoratorLabel; + final Future Function(T)? _onChanged; + final T _value; +} diff --git a/lib/theme/views/styled_scaffold.dart b/lib/theme/views/styled_widgets/styled_scaffold.dart similarity index 97% rename from lib/theme/views/styled_scaffold.dart rename to lib/theme/views/styled_widgets/styled_scaffold.dart index 4fc803f..82f27f5 100644 --- a/lib/theme/views/styled_scaffold.dart +++ b/lib/theme/views/styled_widgets/styled_scaffold.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../theme.dart'; +import '../../theme.dart'; class StyledScaffold extends StatelessWidget { const StyledScaffold({required this.appBar, required this.body, super.key}); diff --git a/lib/theme/views/slider_tile.dart b/lib/theme/views/styled_widgets/styled_slide_tile.dart similarity index 75% rename from lib/theme/views/slider_tile.dart rename to lib/theme/views/styled_widgets/styled_slide_tile.dart index 8e5f178..43b3fd8 100644 --- a/lib/theme/views/slider_tile.dart +++ b/lib/theme/views/styled_widgets/styled_slide_tile.dart @@ -2,10 +2,10 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; -import '../theme.dart'; +import '../../theme.dart'; -class SliderTileAction { - const SliderTileAction({ +class SlideTileAction { + const SlideTileAction({ required this.actionScale, required this.onPressed, this.key, @@ -20,8 +20,8 @@ class SliderTileAction { final SlidableActionCallback? onPressed; } -class SliderTile extends StatelessWidget { - const SliderTile( +class StyledSlideTile extends StatelessWidget { + const StyledSlideTile( {required this.disabled, required this.selected, required this.tileScale, @@ -38,8 +38,8 @@ class SliderTile extends StatelessWidget { final bool disabled; final bool selected; final ScaleKind tileScale; - final List endActions; - final List startActions; + final List endActions; + final List startActions; final GestureTapCallback? onTap; final GestureTapCallback? onDoubleTap; final Widget? leading; @@ -54,8 +54,8 @@ class SliderTile extends StatelessWidget { ..add(DiagnosticsProperty('disabled', disabled)) ..add(DiagnosticsProperty('selected', selected)) ..add(DiagnosticsProperty('tileScale', tileScale)) - ..add(IterableProperty('endActions', endActions)) - ..add(IterableProperty('startActions', startActions)) + ..add(IterableProperty('endActions', endActions)) + ..add(IterableProperty('startActions', startActions)) ..add(ObjectFlagProperty.has('onTap', onTap)) ..add(DiagnosticsProperty('leading', leading)) ..add(StringProperty('title', title)) @@ -66,7 +66,6 @@ class SliderTile extends StatelessWidget { } @override - // ignore: prefer_expression_function_bodies Widget build(BuildContext context) { final theme = Theme.of(context); final scaleTheme = theme.extension()!; @@ -91,12 +90,13 @@ class SliderTile extends StatelessWidget { selected: true, scaleKind: a.actionScale); return SlidableAction( - onPressed: disabled ? null : a.onPressed, - backgroundColor: scaleActionTheme.backgroundColor, - foregroundColor: scaleActionTheme.textColor, - icon: subtitle.isEmpty ? a.icon : null, - label: a.label, - padding: const EdgeInsets.all(2)); + onPressed: disabled ? null : a.onPressed, + backgroundColor: scaleActionTheme.backgroundColor, + foregroundColor: scaleActionTheme.textColor, + icon: subtitle.isEmpty ? a.icon : null, + label: a.label, + padding: const EdgeInsets.all(2).scaled(context), + ); }).toList()), startActionPane: startActions.isEmpty ? null @@ -109,17 +109,18 @@ class SliderTile extends StatelessWidget { scaleKind: a.actionScale); return SlidableAction( - onPressed: disabled ? null : a.onPressed, - backgroundColor: scaleActionTheme.backgroundColor, - foregroundColor: scaleActionTheme.textColor, - icon: subtitle.isEmpty ? a.icon : null, - label: a.label, - padding: const EdgeInsets.all(2)); + onPressed: disabled ? null : a.onPressed, + backgroundColor: scaleActionTheme.backgroundColor, + foregroundColor: scaleActionTheme.textColor, + icon: subtitle.isEmpty ? a.icon : null, + label: a.label, + padding: const EdgeInsets.all(2).scaled(context), + ); }).toList()), child: Padding( padding: scaleTheme.config.useVisualIndicators ? EdgeInsets.zero - : const EdgeInsets.fromLTRB(0, 2, 0, 2), + : const EdgeInsets.fromLTRB(0, 2, 0, 2).scaled(context), child: GestureDetector( onDoubleTap: onDoubleTap, child: ListTile( @@ -131,7 +132,8 @@ class SliderTile extends StatelessWidget { softWrap: false, ), subtitle: subtitle.isNotEmpty ? Text(subtitle) : null, - minTileHeight: 52, + contentPadding: const EdgeInsets.fromLTRB(8, 4, 8, 4) + .scaled(context), iconColor: scaleTileTheme.textColor, textColor: scaleTileTheme.textColor, leading: diff --git a/lib/theme/views/styled_widgets/styled_slider.dart b/lib/theme/views/styled_widgets/styled_slider.dart new file mode 100644 index 0000000..a0c7259 --- /dev/null +++ b/lib/theme/views/styled_widgets/styled_slider.dart @@ -0,0 +1,79 @@ +import 'package:async_tools/async_tools.dart'; +import 'package:awesome_extensions/awesome_extensions_flutter.dart'; +import 'package:flutter/material.dart'; + +import '../../models/models.dart'; +import '../views.dart'; + +const _kStyledSliderChanged = 'kStyledSliderChanged'; + +class StyledSlider extends StatelessWidget { + const StyledSlider( + {required double value, + String? label, + String? decoratorLabel, + Future Function(double)? onChanged, + Widget? leftWidget, + Widget? rightWidget, + double min = 0, + double max = 1, + int? divisions, + super.key}) + : _value = value, + _onChanged = onChanged, + _leftWidget = leftWidget, + _rightWidget = rightWidget, + _min = min, + _max = max, + _divisions = divisions, + _label = label, + _decoratorLabel = decoratorLabel; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final scale = theme.extension()!; + + Widget ctrl = Row(children: [ + if (_leftWidget != null) _leftWidget, + Slider( + activeColor: scale.scheme.primaryScale.border, + inactiveColor: scale.scheme.primaryScale.subtleBorder, + secondaryActiveColor: scale.scheme.secondaryScale.border, + value: _value, + min: _min, + max: _max, + divisions: _divisions, + label: _label, + thumbColor: scale.scheme.primaryScale.appText, + overlayColor: + WidgetStateColor.resolveWith((ws) => theme.focusColor), + onChanged: _onChanged == null + ? null + : (value) { + singleFuture((this, _kStyledSliderChanged), () async { + await _onChanged(value); + }); + }) + .expanded(), + if (_rightWidget != null) _rightWidget, + ]); + if (_decoratorLabel != null) { + ctrl = ctrl + .paddingLTRB(4.scaled(context), 4.scaled(context), 4.scaled(context), + 4.scaled(context)) + .decoratorLabel(context, _decoratorLabel); + } + return ctrl; + } + + final String? _label; + final String? _decoratorLabel; + final Future Function(double)? _onChanged; + final double _value; + final Widget? _leftWidget; + final Widget? _rightWidget; + final double _min; + final double _max; + final int? _divisions; +} diff --git a/lib/theme/views/styled_widgets/styled_widgets.dart b/lib/theme/views/styled_widgets/styled_widgets.dart new file mode 100644 index 0000000..ae45d59 --- /dev/null +++ b/lib/theme/views/styled_widgets/styled_widgets.dart @@ -0,0 +1,8 @@ +export 'styled_alert.dart'; +export 'styled_avatar.dart'; +export 'styled_checkbox.dart'; +export 'styled_dialog.dart'; +export 'styled_dropdown.dart'; +export 'styled_scaffold.dart'; +export 'styled_slide_tile.dart'; +export 'styled_slider.dart'; diff --git a/lib/theme/views/views.dart b/lib/theme/views/views.dart index 88f4a4a..1144440 100644 --- a/lib/theme/views/views.dart +++ b/lib/theme/views/views.dart @@ -1,16 +1,10 @@ -export 'avatar_widget.dart'; -export 'brightness_preferences.dart'; -export 'color_preferences.dart'; export 'enter_password.dart'; export 'enter_pin.dart'; -export 'option_box.dart'; export 'pop_control.dart'; +export 'preferences/preferences.dart'; export 'recovery_key_widget.dart'; export 'responsive.dart'; export 'scanner_error_widget.dart'; -export 'slider_tile.dart'; -export 'styled_alert.dart'; -export 'styled_dialog.dart'; -export 'styled_scaffold.dart'; -export 'wallpaper_preferences.dart'; +export 'styled_widgets/styled_button_box.dart'; +export 'styled_widgets/styled_widgets.dart'; export 'widget_helpers.dart'; diff --git a/lib/theme/views/wallpaper_preferences.dart b/lib/theme/views/wallpaper_preferences.dart deleted file mode 100644 index f9ae94c..0000000 --- a/lib/theme/views/wallpaper_preferences.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:animated_theme_switcher/animated_theme_switcher.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_form_builder/flutter_form_builder.dart'; -import 'package:flutter_translate/flutter_translate.dart'; - -import '../../settings/settings.dart'; -import '../models/models.dart'; - -const String formFieldEnableWallpaper = 'enable_wallpaper'; - -Widget buildSettingsPageWallpaperPreferences( - {required BuildContext context, - required void Function() onChanged, - required ThemeSwitcherState switcher}) { - final preferencesRepository = PreferencesRepository.instance; - final themePreferences = preferencesRepository.value.themePreference; - final theme = Theme.of(context); - final textTheme = theme.textTheme; - - return FormBuilderCheckbox( - name: formFieldEnableWallpaper, - title: Text(translate('settings_page.enable_wallpaper'), - style: textTheme.labelMedium), - initialValue: themePreferences.enableWallpaper, - onChanged: (value) async { - if (value != null) { - final newThemePrefs = - themePreferences.copyWith(enableWallpaper: value); - final newPrefs = preferencesRepository.value - .copyWith(themePreference: newThemePrefs); - - await preferencesRepository.set(newPrefs); - switcher.changeTheme(theme: newThemePrefs.themeData()); - onChanged(); - } - }); -} diff --git a/lib/veilid_processor/views/developer.dart b/lib/veilid_processor/views/developer.dart index d749a9c..c03b6bf 100644 --- a/lib/veilid_processor/views/developer.dart +++ b/lib/veilid_processor/views/developer.dart @@ -222,13 +222,16 @@ class _DeveloperPageState extends State { return Scaffold( backgroundColor: scale.primaryScale.border, appBar: DefaultAppBar( + context: context, title: Text(translate('developer.title')), leading: IconButton( + iconSize: 24.scaled(context), icon: Icon(Icons.arrow_back, color: scale.primaryScale.borderText), onPressed: () => GoRouterHelper(context).pop(), ), actions: [ IconButton( + iconSize: 24.scaled(context), icon: const Icon(Icons.copy), color: scale.primaryScale.borderText, disabledColor: scale.primaryScale.borderText.withAlpha(0x3F), @@ -238,6 +241,7 @@ class _DeveloperPageState extends State { await copySelection(context); }), IconButton( + iconSize: 24.scaled(context), icon: const Icon(Icons.copy_all), color: scale.primaryScale.borderText, disabledColor: scale.primaryScale.borderText.withAlpha(0x3F), @@ -245,6 +249,7 @@ class _DeveloperPageState extends State { await copyAll(context); }), IconButton( + iconSize: 24.scaled(context), icon: const Icon(Icons.clear_all), color: scale.primaryScale.borderText, disabledColor: scale.primaryScale.borderText.withAlpha(0x3F), @@ -259,7 +264,7 @@ class _DeveloperPageState extends State { } }), SizedBox.fromSize( - size: const Size(140, 48), + size: Size(140.scaled(context), 48), child: CustomDropdown( items: _logLevelDropdownItems, initialItem: _logLevelDropdownItems @@ -300,6 +305,7 @@ class _DeveloperPageState extends State { Image.asset('assets/images/ellet.png'), TerminalView(globalDebugTerminal, textStyle: kDefaultTerminalStyle, + textScaler: TextScaler.noScaling, controller: _terminalController, keyboardType: TextInputType.none, backgroundOpacity: _showEllet ? 0.75 : 1.0, diff --git a/lib/veilid_processor/views/signal_strength_meter.dart b/lib/veilid_processor/views/signal_strength_meter.dart index 5385bb1..44e3cb6 100644 --- a/lib/veilid_processor/views/signal_strength_meter.dart +++ b/lib/veilid_processor/views/signal_strength_meter.dart @@ -19,7 +19,7 @@ class SignalStrengthMeterWidget extends StatelessWidget { final theme = Theme.of(context); final scale = theme.extension()!; - const iconSize = 16.0; + final iconSize = 16.0.scaled(context); return BlocBuilder>(builder: (context, state) {