mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-07-23 14:40:58 -04:00
Accessibility update
This commit is contained in:
parent
be8014c97a
commit
3b1cb53b8a
55 changed files with 1089 additions and 807 deletions
|
@ -6,6 +6,9 @@
|
||||||
- Deprecated accounts no longer crash application at startup
|
- Deprecated accounts no longer crash application at startup
|
||||||
- Simplify SingleContactMessagesCubit and MessageReconciliation
|
- Simplify SingleContactMessagesCubit and MessageReconciliation
|
||||||
- Update flutter_chat_ui to 2.0.0
|
- Update flutter_chat_ui to 2.0.0
|
||||||
|
- Accessibility improvements
|
||||||
|
- Text scaling
|
||||||
|
- Keyboard shortcuts Ctrl + / Ctrl - to change font size
|
||||||
|
|
||||||
## v0.4.7 ##
|
## v0.4.7 ##
|
||||||
- *Community Contributions*
|
- *Community Contributions*
|
||||||
|
|
|
@ -276,6 +276,7 @@
|
||||||
"titlebar": "Settings",
|
"titlebar": "Settings",
|
||||||
"color_theme": "Color Theme",
|
"color_theme": "Color Theme",
|
||||||
"brightness_mode": "Brightness Mode",
|
"brightness_mode": "Brightness Mode",
|
||||||
|
"display_scale": "Display Scale",
|
||||||
"display_beta_warning": "Display beta warning on startup",
|
"display_beta_warning": "Display beta warning on startup",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
"in_app": "In-app",
|
"in_app": "In-app",
|
||||||
|
|
|
@ -6,54 +6,9 @@ PODS:
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
- flutter_native_splash (2.4.3):
|
- flutter_native_splash (2.4.3):
|
||||||
- Flutter
|
- Flutter
|
||||||
- GoogleDataTransport (10.1.0):
|
- mobile_scanner (7.0.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):
|
|
||||||
- Flutter
|
- Flutter
|
||||||
- GoogleMLKit/BarcodeScanning (~> 7.0.0)
|
- FlutterMacOS
|
||||||
- nanopb (3.30910.0):
|
|
||||||
- nanopb/decode (= 3.30910.0)
|
|
||||||
- nanopb/encode (= 3.30910.0)
|
|
||||||
- nanopb/decode (3.30910.0)
|
|
||||||
- nanopb/encode (3.30910.0)
|
|
||||||
- package_info_plus (0.4.5):
|
- package_info_plus (0.4.5):
|
||||||
- Flutter
|
- Flutter
|
||||||
- pasteboard (0.0.1):
|
- pasteboard (0.0.1):
|
||||||
|
@ -63,7 +18,6 @@ PODS:
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- printing (1.0.0):
|
- printing (1.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- PromisesObjC (2.4.0)
|
|
||||||
- share_plus (0.0.1):
|
- share_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
|
@ -84,7 +38,7 @@ DEPENDENCIES:
|
||||||
- file_saver (from `.symlinks/plugins/file_saver/ios`)
|
- file_saver (from `.symlinks/plugins/file_saver/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
- 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`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- pasteboard (from `.symlinks/plugins/pasteboard/ios`)
|
- pasteboard (from `.symlinks/plugins/pasteboard/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
- 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`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
- veilid (from `.symlinks/plugins/veilid/ios`)
|
- veilid (from `.symlinks/plugins/veilid/ios`)
|
||||||
|
|
||||||
SPEC REPOS:
|
|
||||||
trunk:
|
|
||||||
- GoogleDataTransport
|
|
||||||
- GoogleMLKit
|
|
||||||
- GoogleToolboxForMac
|
|
||||||
- GoogleUtilities
|
|
||||||
- GTMSessionFetcher
|
|
||||||
- MLImage
|
|
||||||
- MLKitBarcodeScanning
|
|
||||||
- MLKitCommon
|
|
||||||
- MLKitVision
|
|
||||||
- nanopb
|
|
||||||
- PromisesObjC
|
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
camera_avfoundation:
|
camera_avfoundation:
|
||||||
:path: ".symlinks/plugins/camera_avfoundation/ios"
|
:path: ".symlinks/plugins/camera_avfoundation/ios"
|
||||||
|
@ -120,7 +60,7 @@ EXTERNAL SOURCES:
|
||||||
flutter_native_splash:
|
flutter_native_splash:
|
||||||
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
||||||
mobile_scanner:
|
mobile_scanner:
|
||||||
:path: ".symlinks/plugins/mobile_scanner/ios"
|
:path: ".symlinks/plugins/mobile_scanner/darwin"
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
pasteboard:
|
pasteboard:
|
||||||
|
@ -143,26 +83,15 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/veilid/ios"
|
:path: ".symlinks/plugins/veilid/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
camera_avfoundation: 04b44aeb14070126c6529e5ab82cc7c9fca107cf
|
camera_avfoundation: be3be85408cd4126f250386828e9b1dfa40ab436
|
||||||
file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6
|
file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
||||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
|
||||||
GoogleMLKit: eff9e23ec1d90ea4157a1ee2e32a4f610c5b3318
|
|
||||||
GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8
|
|
||||||
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
|
||||||
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
|
|
||||||
MLImage: 0ad1c5f50edd027672d8b26b0fee78a8b4a0fc56
|
|
||||||
MLKitBarcodeScanning: 0a3064da0a7f49ac24ceb3cb46a5bc67496facd2
|
|
||||||
MLKitCommon: 07c2c33ae5640e5380beaaa6e4b9c249a205542d
|
|
||||||
MLKitVision: 45e79d68845a2de77e2dd4d7f07947f0ed157b0e
|
|
||||||
mobile_scanner: af8f71879eaba2bbcb4d86c6a462c3c0e7f23036
|
|
||||||
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
|
||||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||||
pasteboard: 49088aeb6119d51f976a421db60d8e1ab079b63c
|
pasteboard: 49088aeb6119d51f976a421db60d8e1ab079b63c
|
||||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||||
printing: 54ff03f28fe9ba3aa93358afb80a8595a071dd07
|
printing: 54ff03f28fe9ba3aa93358afb80a8595a071dd07
|
||||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
|
||||||
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
|
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
|
||||||
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
|
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
|
||||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||||
|
|
|
@ -139,7 +139,6 @@
|
||||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
02C44F9283ADDE9FAAA73512 /* [CP] Embed Pods Frameworks */,
|
02C44F9283ADDE9FAAA73512 /* [CP] Embed Pods Frameworks */,
|
||||||
61BE8A90522682C17620991D /* [CP] Copy Pods Resources */,
|
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
@ -232,23 +231,6 @@
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
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 */ = {
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
alwaysOutOfDate = 1;
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
|
@ -43,6 +44,7 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
|
|
@ -73,15 +73,18 @@ class _EditAccountPageState extends WindowSetupState<EditAccountPage> {
|
||||||
title: translate('edit_account_page.remove_account_confirm'),
|
title: translate('edit_account_page.remove_account_confirm'),
|
||||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||||
Text(translate('edit_account_page.remove_account_confirm_message'))
|
Text(translate('edit_account_page.remove_account_confirm_message'))
|
||||||
.paddingLTRB(24, 24, 24, 0),
|
.paddingLTRB(24.scaled(context), 24.scaled(context),
|
||||||
Text(translate('confirmation.are_you_sure')).paddingAll(8),
|
24.scaled(context), 0),
|
||||||
|
Text(translate('confirmation.are_you_sure'))
|
||||||
|
.paddingAll(8.scaled(context)),
|
||||||
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
|
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop(false);
|
Navigator.of(context).pop(false);
|
||||||
},
|
},
|
||||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
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)
|
Text(translate('button.no')).paddingLTRB(0, 0, 4, 0)
|
||||||
])),
|
])),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
|
@ -89,10 +92,12 @@ class _EditAccountPageState extends WindowSetupState<EditAccountPage> {
|
||||||
Navigator.of(context).pop(true);
|
Navigator.of(context).pop(true);
|
||||||
},
|
},
|
||||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||||
const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0),
|
Icon(Icons.check, size: 16.scaled(context))
|
||||||
Text(translate('button.yes')).paddingLTRB(0, 0, 4, 0)
|
.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) {
|
if (confirmed != null && confirmed) {
|
||||||
try {
|
try {
|
||||||
|
@ -141,29 +146,36 @@ class _EditAccountPageState extends WindowSetupState<EditAccountPage> {
|
||||||
title: translate('edit_account_page.destroy_account_confirm'),
|
title: translate('edit_account_page.destroy_account_confirm'),
|
||||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||||
Text(translate('edit_account_page.destroy_account_confirm_message'))
|
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(
|
Text(translate(
|
||||||
'edit_account_page.destroy_account_confirm_message_details'))
|
'edit_account_page.destroy_account_confirm_message_details'))
|
||||||
.paddingLTRB(24, 24, 24, 0),
|
.paddingLTRB(24.scaled(context), 24.scaled(context),
|
||||||
Text(translate('confirmation.are_you_sure')).paddingAll(8),
|
24.scaled(context), 0),
|
||||||
|
Text(translate('confirmation.are_you_sure'))
|
||||||
|
.paddingAll(24.scaled(context)),
|
||||||
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
|
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop(false);
|
Navigator.of(context).pop(false);
|
||||||
},
|
},
|
||||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||||
const Icon(Icons.cancel, size: 16).paddingLTRB(0, 0, 4, 0),
|
Icon(Icons.cancel, size: 16.scaled(context))
|
||||||
Text(translate('button.no')).paddingLTRB(0, 0, 4, 0)
|
.paddingLTRB(0, 0, 4.scaled(context), 0),
|
||||||
|
Text(translate('button.no'))
|
||||||
|
.paddingLTRB(0, 0, 4.scaled(context), 0)
|
||||||
])),
|
])),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop(true);
|
Navigator.of(context).pop(true);
|
||||||
},
|
},
|
||||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||||
const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0),
|
Icon(Icons.check, size: 16.scaled(context))
|
||||||
Text(translate('button.yes')).paddingLTRB(0, 0, 4, 0)
|
.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) {
|
if (confirmed != null && confirmed) {
|
||||||
try {
|
try {
|
||||||
|
@ -250,10 +262,12 @@ class _EditAccountPageState extends WindowSetupState<EditAccountPage> {
|
||||||
|
|
||||||
return StyledScaffold(
|
return StyledScaffold(
|
||||||
appBar: DefaultAppBar(
|
appBar: DefaultAppBar(
|
||||||
|
context: context,
|
||||||
title: Text(translate('edit_account_page.titlebar')),
|
title: Text(translate('edit_account_page.titlebar')),
|
||||||
leading: Navigator.canPop(context)
|
leading: Navigator.canPop(context)
|
||||||
? IconButton(
|
? IconButton(
|
||||||
icon: const Icon(Icons.arrow_back),
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
iconSize: 24.scaled(context),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
singleFuture((this, _kDoBackArrow), () async {
|
singleFuture((this, _kDoBackArrow), () async {
|
||||||
if (_isModified) {
|
if (_isModified) {
|
||||||
|
@ -277,6 +291,7 @@ class _EditAccountPageState extends WindowSetupState<EditAccountPage> {
|
||||||
const SignalStrengthMeterWidget(),
|
const SignalStrengthMeterWidget(),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.settings),
|
icon: const Icon(Icons.settings),
|
||||||
|
iconSize: 24.scaled(context),
|
||||||
tooltip: translate('menu.settings_tooltip'),
|
tooltip: translate('menu.settings_tooltip'),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await GoRouterHelper(context).push('/settings');
|
await GoRouterHelper(context).push('/settings');
|
||||||
|
@ -285,14 +300,14 @@ class _EditAccountPageState extends WindowSetupState<EditAccountPage> {
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(children: [
|
child: Column(children: [
|
||||||
_editAccountForm(context).paddingLTRB(0, 0, 0, 32),
|
_editAccountForm(context).paddingLTRB(0, 0, 0, 32),
|
||||||
OptionBox(
|
StyledButtonBox(
|
||||||
instructions:
|
instructions:
|
||||||
translate('edit_account_page.remove_account_description'),
|
translate('edit_account_page.remove_account_description'),
|
||||||
buttonIcon: Icons.person_remove_alt_1,
|
buttonIcon: Icons.person_remove_alt_1,
|
||||||
buttonText: translate('edit_account_page.remove_account'),
|
buttonText: translate('edit_account_page.remove_account'),
|
||||||
onClick: _onRemoveAccount,
|
onClick: _onRemoveAccount,
|
||||||
),
|
),
|
||||||
OptionBox(
|
StyledButtonBox(
|
||||||
instructions:
|
instructions:
|
||||||
translate('edit_account_page.destroy_account_description'),
|
translate('edit_account_page.destroy_account_description'),
|
||||||
buttonIcon: Icons.person_off,
|
buttonIcon: Icons.person_off,
|
||||||
|
|
|
@ -53,16 +53,16 @@ class EditProfileForm extends StatefulWidget {
|
||||||
..add(DiagnosticsProperty<AccountSpec>('initialValue', initialValue));
|
..add(DiagnosticsProperty<AccountSpec>('initialValue', initialValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
static const String formFieldName = 'name';
|
static const formFieldName = 'name';
|
||||||
static const String formFieldPronouns = 'pronouns';
|
static const formFieldPronouns = 'pronouns';
|
||||||
static const String formFieldAbout = 'about';
|
static const formFieldAbout = 'about';
|
||||||
static const String formFieldAvailability = 'availability';
|
static const formFieldAvailability = 'availability';
|
||||||
static const String formFieldFreeMessage = 'free_message';
|
static const formFieldFreeMessage = 'free_message';
|
||||||
static const String formFieldAwayMessage = 'away_message';
|
static const formFieldAwayMessage = 'away_message';
|
||||||
static const String formFieldBusyMessage = 'busy_message';
|
static const formFieldBusyMessage = 'busy_message';
|
||||||
static const String formFieldAvatar = 'avatar';
|
static const formFieldAvatar = 'avatar';
|
||||||
static const String formFieldAutoAway = 'auto_away';
|
static const formFieldAutoAway = 'auto_away';
|
||||||
static const String formFieldAutoAwayTimeout = 'auto_away_timeout';
|
static const formFieldAutoAwayTimeout = 'auto_away_timeout';
|
||||||
}
|
}
|
||||||
|
|
||||||
class _EditProfileFormState extends State<EditProfileForm> {
|
class _EditProfileFormState extends State<EditProfileForm> {
|
||||||
|
@ -98,6 +98,7 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
||||||
name: EditProfileForm.formFieldAvailability,
|
name: EditProfileForm.formFieldAvailability,
|
||||||
initialValue: initialValue,
|
initialValue: initialValue,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
contentPadding: const EdgeInsets.all(8).scaled(context),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||||
labelText: translate('account.form_availability'),
|
labelText: translate('account.form_availability'),
|
||||||
hintText: translate('account.empty_busy_message')),
|
hintText: translate('account.empty_busy_message')),
|
||||||
|
@ -110,7 +111,7 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
||||||
Text(x == proto.Availability.AVAILABILITY_OFFLINE
|
Text(x == proto.Availability.AVAILABILITY_OFFLINE
|
||||||
? translate('availability.always_show_offline')
|
? translate('availability.always_show_offline')
|
||||||
: AvailabilityWidget.availabilityName(x))
|
: AvailabilityWidget.availabilityName(x))
|
||||||
.paddingLTRB(8, 0, 0, 0),
|
.paddingLTRB(8.scaled(context), 0, 0, 0),
|
||||||
])))
|
])))
|
||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
|
@ -175,17 +176,8 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
) {
|
) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final scale = theme.extension<ScaleScheme>()!;
|
|
||||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
|
||||||
final textTheme = theme.textTheme;
|
final textTheme = theme.textTheme;
|
||||||
|
|
||||||
late final Color border;
|
|
||||||
if (scaleConfig.useVisualIndicators && !scaleConfig.preferBorders) {
|
|
||||||
border = scale.primaryScale.elementBackground;
|
|
||||||
} else {
|
|
||||||
border = scale.primaryScale.border;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FormBuilder(
|
return FormBuilder(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
@ -197,14 +189,9 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
||||||
children: [
|
children: [
|
||||||
Row(children: [
|
Row(children: [
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
AvatarWidget(
|
StyledAvatar(
|
||||||
name: _currentValueName,
|
name: _currentValueName,
|
||||||
size: 128,
|
size: 128.scaled(context),
|
||||||
borderColor: border,
|
|
||||||
foregroundColor: scale.primaryScale.primaryText,
|
|
||||||
backgroundColor: scale.primaryScale.primary,
|
|
||||||
scaleConfig: scaleConfig,
|
|
||||||
textStyle: theme.textTheme.titleLarge!.copyWith(fontSize: 64),
|
|
||||||
).paddingLTRB(0, 0, 0, 16),
|
).paddingLTRB(0, 0, 0, 16),
|
||||||
const Spacer()
|
const Spacer()
|
||||||
]),
|
]),
|
||||||
|
@ -218,6 +205,7 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
contentPadding: const EdgeInsets.all(8).scaled(context),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||||
labelText: translate('account.form_name'),
|
labelText: translate('account.form_name'),
|
||||||
hintText: translate('account.empty_name')),
|
hintText: translate('account.empty_name')),
|
||||||
|
@ -233,6 +221,7 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
||||||
initialValue: _savedValue.pronouns,
|
initialValue: _savedValue.pronouns,
|
||||||
maxLength: 64,
|
maxLength: 64,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
contentPadding: const EdgeInsets.all(8).scaled(context),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||||
labelText: translate('account.form_pronouns'),
|
labelText: translate('account.form_pronouns'),
|
||||||
hintText: translate('account.empty_pronouns')),
|
hintText: translate('account.empty_pronouns')),
|
||||||
|
@ -245,6 +234,7 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
||||||
maxLines: 8,
|
maxLines: 8,
|
||||||
minLines: 1,
|
minLines: 1,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
contentPadding: const EdgeInsets.all(8).scaled(context),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||||
labelText: translate('account.form_about'),
|
labelText: translate('account.form_about'),
|
||||||
hintText: translate('account.empty_about')),
|
hintText: translate('account.empty_about')),
|
||||||
|
@ -256,6 +246,7 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
||||||
initialValue: _savedValue.freeMessage,
|
initialValue: _savedValue.freeMessage,
|
||||||
maxLength: 128,
|
maxLength: 128,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
contentPadding: const EdgeInsets.all(8).scaled(context),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||||
labelText: translate('account.form_free_message'),
|
labelText: translate('account.form_free_message'),
|
||||||
hintText: translate('account.empty_free_message')),
|
hintText: translate('account.empty_free_message')),
|
||||||
|
@ -266,6 +257,7 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
||||||
initialValue: _savedValue.awayMessage,
|
initialValue: _savedValue.awayMessage,
|
||||||
maxLength: 128,
|
maxLength: 128,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
contentPadding: const EdgeInsets.all(8).scaled(context),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||||
labelText: translate('account.form_away_message'),
|
labelText: translate('account.form_away_message'),
|
||||||
hintText: translate('account.empty_away_message')),
|
hintText: translate('account.empty_away_message')),
|
||||||
|
@ -276,6 +268,7 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
||||||
initialValue: _savedValue.busyMessage,
|
initialValue: _savedValue.busyMessage,
|
||||||
maxLength: 128,
|
maxLength: 128,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
contentPadding: const EdgeInsets.all(8).scaled(context),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||||
labelText: translate('account.form_busy_message'),
|
labelText: translate('account.form_busy_message'),
|
||||||
hintText: translate('account.empty_busy_message')),
|
hintText: translate('account.empty_busy_message')),
|
||||||
|
@ -291,12 +284,13 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
||||||
_currentValueAutoAway = v ?? false;
|
_currentValueAutoAway = v ?? false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
).paddingLTRB(0, 0, 0, 16),
|
).paddingLTRB(0, 0, 0, 16.scaled(context)),
|
||||||
FormBuilderTextField(
|
FormBuilderTextField(
|
||||||
name: EditProfileForm.formFieldAutoAwayTimeout,
|
name: EditProfileForm.formFieldAutoAwayTimeout,
|
||||||
enabled: _currentValueAutoAway,
|
enabled: _currentValueAutoAway,
|
||||||
initialValue: _savedValue.autoAwayTimeout.toString(),
|
initialValue: _savedValue.autoAwayTimeout.toString(),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
contentPadding: const EdgeInsets.all(8).scaled(context),
|
||||||
labelText: translate('account.form_auto_away_timeout'),
|
labelText: translate('account.form_auto_away_timeout'),
|
||||||
),
|
),
|
||||||
validator: FormBuilderValidators.positiveNumber(),
|
validator: FormBuilderValidators.positiveNumber(),
|
||||||
|
@ -306,7 +300,7 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Text(widget.instructions).toCenter().flexible(flex: 6),
|
Text(widget.instructions).toCenter().flexible(flex: 6),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
]).paddingSymmetric(vertical: 16),
|
]).paddingSymmetric(vertical: 16.scaled(context)),
|
||||||
Row(children: [
|
Row(children: [
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Builder(builder: (context) {
|
Builder(builder: (context) {
|
||||||
|
@ -320,16 +314,18 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
||||||
|
|
||||||
return ElevatedButton(
|
return ElevatedButton(
|
||||||
onPressed: (networkReady && _isModified) ? _doSubmit : null,
|
onPressed: (networkReady && _isModified) ? _doSubmit : null,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsetsGeometry.all(4.scaled(context)),
|
||||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||||
Icon(networkReady ? Icons.check : Icons.hourglass_empty,
|
Icon(networkReady ? Icons.check : Icons.hourglass_empty,
|
||||||
size: 16)
|
size: 16.scaled(context))
|
||||||
.paddingLTRB(0, 0, 4, 0),
|
.paddingLTRB(0, 0, 4.scaled(context), 0),
|
||||||
Text(networkReady
|
Text(networkReady
|
||||||
? widget.submitText
|
? widget.submitText
|
||||||
: widget.submitDisabledText)
|
: widget.submitDisabledText)
|
||||||
.paddingLTRB(0, 0, 4, 0)
|
.paddingLTRB(0, 0, 4.scaled(context), 0)
|
||||||
]),
|
]),
|
||||||
);
|
));
|
||||||
}),
|
}),
|
||||||
const Spacer()
|
const Spacer()
|
||||||
])
|
])
|
||||||
|
@ -363,5 +359,5 @@ class _EditProfileFormState extends State<EditProfileForm> {
|
||||||
late AccountSpec _savedValue;
|
late AccountSpec _savedValue;
|
||||||
late bool _currentValueAutoAway;
|
late bool _currentValueAutoAway;
|
||||||
late String _currentValueName;
|
late String _currentValueName;
|
||||||
bool _isModified = false;
|
var _isModified = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,7 @@ class _NewAccountPageState extends WindowSetupState<NewAccountPage> {
|
||||||
|
|
||||||
return StyledScaffold(
|
return StyledScaffold(
|
||||||
appBar: DefaultAppBar(
|
appBar: DefaultAppBar(
|
||||||
|
context: context,
|
||||||
title: Text(translate('new_account_page.titlebar')),
|
title: Text(translate('new_account_page.titlebar')),
|
||||||
leading: GoRouterHelper(context).canPop()
|
leading: GoRouterHelper(context).canPop()
|
||||||
? IconButton(
|
? IconButton(
|
||||||
|
@ -111,6 +112,7 @@ class _NewAccountPageState extends WindowSetupState<NewAccountPage> {
|
||||||
const SignalStrengthMeterWidget(),
|
const SignalStrengthMeterWidget(),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.settings),
|
icon: const Icon(Icons.settings),
|
||||||
|
iconSize: 24.scaled(context),
|
||||||
tooltip: translate('menu.settings_tooltip'),
|
tooltip: translate('menu.settings_tooltip'),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await GoRouterHelper(context).push('/settings');
|
await GoRouterHelper(context).push('/settings');
|
||||||
|
|
|
@ -164,6 +164,7 @@ class _ShowRecoveryKeyPageState extends WindowSetupState<ShowRecoveryKeyPage> {
|
||||||
|
|
||||||
return StyledScaffold(
|
return StyledScaffold(
|
||||||
appBar: DefaultAppBar(
|
appBar: DefaultAppBar(
|
||||||
|
context: context,
|
||||||
title: Text(translate('show_recovery_key_page.titlebar')),
|
title: Text(translate('show_recovery_key_page.titlebar')),
|
||||||
actions: [
|
actions: [
|
||||||
const SignalStrengthMeterWidget(),
|
const SignalStrengthMeterWidget(),
|
||||||
|
@ -193,7 +194,7 @@ class _ShowRecoveryKeyPageState extends WindowSetupState<ShowRecoveryKeyPage> {
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
translate('show_recovery_key_page.instructions_options'))
|
translate('show_recovery_key_page.instructions_options'))
|
||||||
.paddingLTRB(12, 0, 12, 24),
|
.paddingLTRB(12, 0, 12, 24),
|
||||||
OptionBox(
|
StyledButtonBox(
|
||||||
instructions:
|
instructions:
|
||||||
translate('show_recovery_key_page.instructions_print'),
|
translate('show_recovery_key_page.instructions_print'),
|
||||||
buttonIcon: Icons.print,
|
buttonIcon: Icons.print,
|
||||||
|
@ -209,7 +210,7 @@ class _ShowRecoveryKeyPageState extends WindowSetupState<ShowRecoveryKeyPage> {
|
||||||
_codeHandled = true;
|
_codeHandled = true;
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
OptionBox(
|
StyledButtonBox(
|
||||||
instructions:
|
instructions:
|
||||||
translate('show_recovery_key_page.instructions_view'),
|
translate('show_recovery_key_page.instructions_view'),
|
||||||
buttonIcon: Icons.edit_document,
|
buttonIcon: Icons.edit_document,
|
||||||
|
@ -229,7 +230,7 @@ class _ShowRecoveryKeyPageState extends WindowSetupState<ShowRecoveryKeyPage> {
|
||||||
_codeHandled = true;
|
_codeHandled = true;
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
OptionBox(
|
StyledButtonBox(
|
||||||
instructions:
|
instructions:
|
||||||
translate('show_recovery_key_page.instructions_share'),
|
translate('show_recovery_key_page.instructions_share'),
|
||||||
buttonIcon: Icons.ios_share,
|
buttonIcon: Icons.ios_share,
|
||||||
|
|
|
@ -31,7 +31,7 @@ class VeilidChatApp extends StatelessWidget {
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
static const String name = 'VeilidChat';
|
static const name = 'VeilidChat';
|
||||||
|
|
||||||
final ThemeData initialThemeData;
|
final ThemeData initialThemeData;
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ class VeilidChatApp extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => FutureProvider<VeilidChatGlobalInit?>(
|
Widget build(BuildContext context) => FutureProvider<VeilidChatGlobalInit?>(
|
||||||
initialData: null,
|
initialData: null,
|
||||||
create: (context) async => VeilidChatGlobalInit.initialize(),
|
create: (context) => VeilidChatGlobalInit.initialize(),
|
||||||
builder: (context, __) {
|
builder: (context, __) {
|
||||||
final globalInit = context.watch<VeilidChatGlobalInit?>();
|
final globalInit = context.watch<VeilidChatGlobalInit?>();
|
||||||
if (globalInit == null) {
|
if (globalInit == null) {
|
||||||
|
|
|
@ -355,6 +355,7 @@ class _VcComposerState extends State<VcComposerWidget> {
|
||||||
borderRadius: BorderRadius.all(Radius.circular(
|
borderRadius: BorderRadius.all(Radius.circular(
|
||||||
8 * config.borderRadiusScale))),
|
8 * config.borderRadiusScale))),
|
||||||
hintText: widget.hintText,
|
hintText: widget.hintText,
|
||||||
|
hintMaxLines: 1,
|
||||||
hintStyle: chatTheme.typography.bodyMedium.copyWith(
|
hintStyle: chatTheme.typography.bodyMedium.copyWith(
|
||||||
color: widget.hintColor ??
|
color: widget.hintColor ??
|
||||||
chatTheme.colors.onSurface
|
chatTheme.colors.onSurface
|
||||||
|
|
|
@ -12,7 +12,7 @@ class VcTextMessageWidget extends StatelessWidget {
|
||||||
const VcTextMessageWidget({
|
const VcTextMessageWidget({
|
||||||
required this.message,
|
required this.message,
|
||||||
required this.index,
|
required this.index,
|
||||||
this.padding = const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
this.padding,
|
||||||
this.borderRadius,
|
this.borderRadius,
|
||||||
this.onlyEmojiFontSize,
|
this.onlyEmojiFontSize,
|
||||||
this.sentBackgroundColor,
|
this.sentBackgroundColor,
|
||||||
|
@ -72,10 +72,6 @@ class VcTextMessageWidget extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final scaleTheme = theme.extension<ScaleTheme>()!;
|
final scaleTheme = theme.extension<ScaleTheme>()!;
|
||||||
final config = scaleTheme.config;
|
|
||||||
final scheme = scaleTheme.scheme;
|
|
||||||
final scale = scaleTheme.scheme.scale(ScaleKind.primary);
|
|
||||||
final textTheme = theme.textTheme;
|
|
||||||
final scaleChatTheme = scaleTheme.chatTheme();
|
final scaleChatTheme = scaleTheme.chatTheme();
|
||||||
final chatTheme = scaleChatTheme.chatTheme;
|
final chatTheme = scaleChatTheme.chatTheme;
|
||||||
|
|
||||||
|
@ -243,15 +239,16 @@ class TimeAndStatus extends StatelessWidget {
|
||||||
if (showStatus && status != null)
|
if (showStatus && status != null)
|
||||||
if (status == MessageStatus.sending)
|
if (status == MessageStatus.sending)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 6,
|
width: 6.scaled(context),
|
||||||
height: 6,
|
height: 6.scaled(context),
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
color: textStyle?.color,
|
color: textStyle?.color,
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
Icon(getIconForStatus(status!), color: textStyle?.color, size: 12),
|
Icon(getIconForStatus(status!),
|
||||||
|
color: textStyle?.color, size: 12.scaled(context)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,17 +132,9 @@ class _ChatComponentWidgetState extends State<ChatComponentWidget> {
|
||||||
final scale = scaleTheme.scheme.scale(ScaleKind.primary);
|
final scale = scaleTheme.scheme.scale(ScaleKind.primary);
|
||||||
final textTheme = theme.textTheme;
|
final textTheme = theme.textTheme;
|
||||||
final scaleChatTheme = scaleTheme.chatTheme();
|
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
|
// Get the enclosing chat component cubit that contains our state
|
||||||
// (created by ChatComponentWidget.builder())
|
// (created by ChatComponentWidget.singleContact())
|
||||||
final chatComponentCubit = context.watch<ChatComponentCubit>();
|
final chatComponentCubit = context.watch<ChatComponentCubit>();
|
||||||
final chatComponentState = chatComponentCubit.state;
|
final chatComponentState = chatComponentCubit.state;
|
||||||
|
|
||||||
|
@ -275,12 +267,17 @@ class _ChatComponentWidgetState extends State<ChatComponentWidget> {
|
||||||
VcTextMessageWidget(
|
VcTextMessageWidget(
|
||||||
message: message,
|
message: message,
|
||||||
index: index,
|
index: index,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 12, horizontal: 16)
|
||||||
|
.scaled(context)
|
||||||
// showTime: true,
|
// showTime: true,
|
||||||
// showStatus: true,
|
// showStatus: true,
|
||||||
),
|
),
|
||||||
// Composer builder
|
// Composer builder
|
||||||
composerBuilder: (ctx) => VcComposerWidget(
|
composerBuilder: (ctx) => VcComposerWidget(
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
|
padding: const EdgeInsets.all(4).scaled(context),
|
||||||
|
gap: 8.scaled(context),
|
||||||
focusNode: _focusNode,
|
focusNode: _focusNode,
|
||||||
textInputAction: isAnyMobile
|
textInputAction: isAnyMobile
|
||||||
? TextInputAction.newline
|
? TextInputAction.newline
|
||||||
|
|
|
@ -24,11 +24,9 @@ class ChatSingleContactItemWidget extends StatelessWidget {
|
||||||
final bool _disabled;
|
final bool _disabled;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
// ignore: prefer_expression_function_bodies
|
|
||||||
Widget build(
|
Widget build(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
) {
|
) {
|
||||||
final theme = Theme.of(context);
|
|
||||||
final scaleTheme = Theme.of(context).extension<ScaleTheme>()!;
|
final scaleTheme = Theme.of(context).extension<ScaleTheme>()!;
|
||||||
|
|
||||||
final activeChatCubit = context.watch<ActiveChatCubit>();
|
final activeChatCubit = context.watch<ActiveChatCubit>();
|
||||||
|
@ -48,23 +46,12 @@ class ChatSingleContactItemWidget extends StatelessWidget {
|
||||||
selected: selected,
|
selected: selected,
|
||||||
);
|
);
|
||||||
|
|
||||||
final avatar = AvatarWidget(
|
final avatar = StyledAvatar(
|
||||||
name: name,
|
name: name,
|
||||||
size: 32,
|
size: 32.scaled(context),
|
||||||
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!,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return SliderTile(
|
return StyledSlideTile(
|
||||||
key: ValueKey(_localConversationRecordKey),
|
key: ValueKey(_localConversationRecordKey),
|
||||||
disabled: _disabled,
|
disabled: _disabled,
|
||||||
selected: selected,
|
selected: selected,
|
||||||
|
@ -75,14 +62,14 @@ class ChatSingleContactItemWidget extends StatelessWidget {
|
||||||
trailing: AvailabilityWidget(
|
trailing: AvailabilityWidget(
|
||||||
availability: availability,
|
availability: availability,
|
||||||
color: scaleTileTheme.textColor,
|
color: scaleTileTheme.textColor,
|
||||||
).fit(fit: BoxFit.scaleDown),
|
).fit(fit: BoxFit.fill),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
singleFuture(activeChatCubit, () async {
|
singleFuture(activeChatCubit, () async {
|
||||||
activeChatCubit.setActiveChat(_localConversationRecordKey);
|
activeChatCubit.setActiveChat(_localConversationRecordKey);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
endActions: [
|
endActions: [
|
||||||
SliderTileAction(
|
SlideTileAction(
|
||||||
//icon: Icons.delete,
|
//icon: Icons.delete,
|
||||||
label: translate('button.delete'),
|
label: translate('button.delete'),
|
||||||
actionScale: ScaleKind.tertiary,
|
actionScale: ScaleKind.tertiary,
|
||||||
|
|
|
@ -44,7 +44,7 @@ class ContactInvitationItemWidget extends StatelessWidget {
|
||||||
title = contactInvitationRecord.message;
|
title = contactInvitationRecord.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SliderTile(
|
return StyledSlideTile(
|
||||||
key: ObjectKey(contactInvitationRecord),
|
key: ObjectKey(contactInvitationRecord),
|
||||||
disabled: tileDisabled,
|
disabled: tileDisabled,
|
||||||
selected: selected,
|
selected: selected,
|
||||||
|
@ -67,7 +67,7 @@ class ContactInvitationItemWidget extends StatelessWidget {
|
||||||
)));
|
)));
|
||||||
},
|
},
|
||||||
endActions: [
|
endActions: [
|
||||||
SliderTileAction(
|
SlideTileAction(
|
||||||
// icon: Icons.delete,
|
// icon: Icons.delete,
|
||||||
label: translate('button.delete'),
|
label: translate('button.delete'),
|
||||||
actionScale: ScaleKind.tertiary,
|
actionScale: ScaleKind.tertiary,
|
||||||
|
|
|
@ -1,37 +1,34 @@
|
||||||
import 'package:awesome_extensions/awesome_extensions.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:flutter_translate/flutter_translate.dart';
|
import 'package:flutter_translate/flutter_translate.dart';
|
||||||
|
|
||||||
import '../../proto/proto.dart' as proto;
|
import '../../proto/proto.dart' as proto;
|
||||||
import '../../theme/theme.dart';
|
|
||||||
|
|
||||||
class AvailabilityWidget extends StatelessWidget {
|
class AvailabilityWidget extends StatelessWidget {
|
||||||
const AvailabilityWidget(
|
const AvailabilityWidget(
|
||||||
{required this.availability,
|
{required this.availability,
|
||||||
required this.color,
|
required this.color,
|
||||||
this.vertical = true,
|
this.vertical = true,
|
||||||
this.size = 32,
|
|
||||||
super.key});
|
super.key});
|
||||||
|
|
||||||
static Widget availabilityIcon(proto.Availability availability, Color color,
|
static Widget availabilityIcon(
|
||||||
{double size = 24}) {
|
proto.Availability availability,
|
||||||
|
Color color,
|
||||||
|
) {
|
||||||
late final Widget icon;
|
late final Widget icon;
|
||||||
switch (availability) {
|
switch (availability) {
|
||||||
case proto.Availability.AVAILABILITY_AWAY:
|
case proto.Availability.AVAILABILITY_AWAY:
|
||||||
icon = SvgPicture.asset('assets/images/toilet.svg',
|
icon = SvgPicture.asset('assets/images/toilet.svg',
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
colorFilter: ColorFilter.mode(color, BlendMode.srcATop));
|
colorFilter: ColorFilter.mode(color, BlendMode.srcATop));
|
||||||
case proto.Availability.AVAILABILITY_BUSY:
|
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:
|
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:
|
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:
|
case proto.Availability.AVAILABILITY_UNSPECIFIED:
|
||||||
icon = Icon(Icons.question_mark, size: size);
|
icon = const Icon(Icons.question_mark, applyTextScaling: true);
|
||||||
}
|
}
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
@ -59,23 +56,17 @@ class AvailabilityWidget extends StatelessWidget {
|
||||||
final textTheme = theme.textTheme;
|
final textTheme = theme.textTheme;
|
||||||
|
|
||||||
final name = availabilityName(availability);
|
final name = availabilityName(availability);
|
||||||
final icon = availabilityIcon(availability, color, size: size * 2 / 3);
|
final icon = availabilityIcon(availability, color);
|
||||||
|
|
||||||
return vertical
|
return vertical
|
||||||
? ConstrainedBox(
|
? Column(mainAxisSize: MainAxisSize.min, children: [
|
||||||
constraints: BoxConstraints.tightFor(width: size),
|
|
||||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
|
||||||
icon,
|
icon,
|
||||||
Text(name, style: textTheme.labelSmall!.copyWith(color: color))
|
Text(name, style: textTheme.labelSmall!.copyWith(color: color))
|
||||||
.fit(fit: BoxFit.scaleDown)
|
])
|
||||||
]))
|
: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||||
: ConstrainedBox(
|
|
||||||
constraints: BoxConstraints.tightFor(height: size),
|
|
||||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
|
||||||
icon,
|
icon,
|
||||||
Text(name, style: textTheme.labelLarge!.copyWith(color: color))
|
Text(' $name', style: textTheme.labelLarge!.copyWith(color: color))
|
||||||
.paddingLTRB(size / 4, 0, 0, 0)
|
]);
|
||||||
]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -83,7 +74,6 @@ class AvailabilityWidget extends StatelessWidget {
|
||||||
final proto.Availability availability;
|
final proto.Availability availability;
|
||||||
final Color color;
|
final Color color;
|
||||||
final bool vertical;
|
final bool vertical;
|
||||||
final double size;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
@ -92,7 +82,6 @@ class AvailabilityWidget extends StatelessWidget {
|
||||||
..add(
|
..add(
|
||||||
DiagnosticsProperty<proto.Availability>('availability', availability))
|
DiagnosticsProperty<proto.Availability>('availability', availability))
|
||||||
..add(DiagnosticsProperty<bool>('vertical', vertical))
|
..add(DiagnosticsProperty<bool>('vertical', vertical))
|
||||||
..add(DoubleProperty('size', size))
|
|
||||||
..add(ColorProperty('color', color));
|
..add(ColorProperty('color', color));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,30 +26,16 @@ class ContactItemWidget extends StatelessWidget {
|
||||||
Widget build(
|
Widget build(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
) {
|
) {
|
||||||
final theme = Theme.of(context);
|
|
||||||
final scale = theme.extension<ScaleScheme>()!;
|
|
||||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
|
||||||
|
|
||||||
final name = _contact.nameOrNickname;
|
final name = _contact.nameOrNickname;
|
||||||
final title = _contact.displayName;
|
final title = _contact.displayName;
|
||||||
final subtitle = _contact.profile.status;
|
final subtitle = _contact.profile.status;
|
||||||
|
|
||||||
final avatar = AvatarWidget(
|
final avatar = StyledAvatar(
|
||||||
name: name,
|
name: name,
|
||||||
size: 34,
|
size: 34.scaled(context),
|
||||||
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!,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return SliderTile(
|
return StyledSlideTile(
|
||||||
key: ObjectKey(_contact),
|
key: ObjectKey(_contact),
|
||||||
disabled: _disabled,
|
disabled: _disabled,
|
||||||
selected: _selected,
|
selected: _selected,
|
||||||
|
@ -69,7 +55,7 @@ class ContactItemWidget extends StatelessWidget {
|
||||||
}),
|
}),
|
||||||
startActions: [
|
startActions: [
|
||||||
if (_onDoubleTap != null)
|
if (_onDoubleTap != null)
|
||||||
SliderTileAction(
|
SlideTileAction(
|
||||||
//icon: Icons.edit,
|
//icon: Icons.edit,
|
||||||
label: translate('button.chat'),
|
label: translate('button.chat'),
|
||||||
actionScale: ScaleKind.secondary,
|
actionScale: ScaleKind.secondary,
|
||||||
|
@ -81,7 +67,7 @@ class ContactItemWidget extends StatelessWidget {
|
||||||
],
|
],
|
||||||
endActions: [
|
endActions: [
|
||||||
if (_onTap != null)
|
if (_onTap != null)
|
||||||
SliderTileAction(
|
SlideTileAction(
|
||||||
//icon: Icons.edit,
|
//icon: Icons.edit,
|
||||||
label: translate('button.edit'),
|
label: translate('button.edit'),
|
||||||
actionScale: ScaleKind.secondary,
|
actionScale: ScaleKind.secondary,
|
||||||
|
@ -91,7 +77,7 @@ class ContactItemWidget extends StatelessWidget {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
if (_onDelete != null)
|
if (_onDelete != null)
|
||||||
SliderTileAction(
|
SlideTileAction(
|
||||||
//icon: Icons.delete,
|
//icon: Icons.delete,
|
||||||
label: translate('button.delete'),
|
label: translate('button.delete'),
|
||||||
actionScale: ScaleKind.tertiary,
|
actionScale: ScaleKind.tertiary,
|
||||||
|
|
|
@ -92,7 +92,7 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
||||||
|
|
||||||
final menuParams = StarMenuParameters(
|
final menuParams = StarMenuParameters(
|
||||||
shape: MenuShape.linear,
|
shape: MenuShape.linear,
|
||||||
centerOffset: const Offset(0, 64),
|
centerOffset: Offset(0, 64.scaled(context)),
|
||||||
boundaryBackground: BoundaryBackground(
|
boundaryBackground: BoundaryBackground(
|
||||||
color: menuBackgroundColor,
|
color: menuBackgroundColor,
|
||||||
decoration: ShapeDecoration(
|
decoration: ShapeDecoration(
|
||||||
|
@ -113,13 +113,14 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
iconData,
|
iconData,
|
||||||
size: 32,
|
size: 32.scaled(context),
|
||||||
).paddingSTEB(0, 8, 0, 8),
|
).paddingSTEB(0, 8.scaled(context), 0, 8.scaled(context)),
|
||||||
label: Text(
|
label: Text(
|
||||||
text,
|
text,
|
||||||
|
textScaler: MediaQuery.of(context).textScaler,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
).paddingSTEB(0, 8, 0, 8));
|
).paddingSTEB(0, 8.scaled(context), 0, 8.scaled(context)));
|
||||||
|
|
||||||
final inviteMenuItems = [
|
final inviteMenuItems = [
|
||||||
makeMenuButton(
|
makeMenuButton(
|
||||||
|
@ -135,14 +136,14 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
_invitationMenuController.closeMenu!();
|
_invitationMenuController.closeMenu!();
|
||||||
await ScanInvitationDialog.show(context);
|
await ScanInvitationDialog.show(context);
|
||||||
}).paddingLTRB(0, 0, 0, 8),
|
}).paddingLTRB(0, 0, 0, 8.scaled(context)),
|
||||||
makeMenuButton(
|
makeMenuButton(
|
||||||
iconData: Icons.contact_page,
|
iconData: Icons.contact_page,
|
||||||
text: translate('add_contact_sheet.create_invite'),
|
text: translate('add_contact_sheet.create_invite'),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
_invitationMenuController.closeMenu!();
|
_invitationMenuController.closeMenu!();
|
||||||
await CreateInvitationDialog.show(context);
|
await CreateInvitationDialog.show(context);
|
||||||
}).paddingLTRB(0, 0, 0, 8),
|
}).paddingLTRB(0, 0, 0, 8.scaled(context)),
|
||||||
];
|
];
|
||||||
|
|
||||||
return StarMenu(
|
return StarMenu(
|
||||||
|
@ -154,7 +155,7 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
||||||
params: menuParams,
|
params: menuParams,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: () {},
|
onPressed: () {},
|
||||||
iconSize: 24,
|
iconSize: 24.scaled(context),
|
||||||
icon: Icon(Icons.person_add, color: menuIconColor),
|
icon: Icon(Icons.person_add, color: menuIconColor),
|
||||||
tooltip: translate('add_contact_sheet.add_contact')),
|
tooltip: translate('add_contact_sheet.add_contact')),
|
||||||
);
|
);
|
||||||
|
@ -202,13 +203,13 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
||||||
onDoubleTap: _onStartChat,
|
onDoubleTap: _onStartChat,
|
||||||
onTap: onContactSelected,
|
onTap: onContactSelected,
|
||||||
onDelete: _onContactDeleted)
|
onDelete: _onContactDeleted)
|
||||||
.paddingLTRB(0, 4, 0, 0);
|
.paddingLTRB(0, 4.scaled(context), 0, 0);
|
||||||
case ContactsBrowserElementKind.invitation:
|
case ContactsBrowserElementKind.invitation:
|
||||||
final invitation = element.invitation!;
|
final invitation = element.invitation!;
|
||||||
return ContactInvitationItemWidget(
|
return ContactInvitationItemWidget(
|
||||||
contactInvitationRecord: invitation,
|
contactInvitationRecord: invitation,
|
||||||
disabled: false)
|
disabled: false)
|
||||||
.paddingLTRB(0, 4, 0, 0);
|
.paddingLTRB(0, 4.scaled(context), 0, 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
filter: (value) {
|
filter: (value) {
|
||||||
|
@ -242,9 +243,11 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
||||||
}
|
}
|
||||||
return filtered;
|
return filtered;
|
||||||
},
|
},
|
||||||
searchFieldHeight: 40,
|
searchFieldHeight: 40.scaled(context),
|
||||||
listViewPadding: const EdgeInsets.fromLTRB(4, 0, 4, 4),
|
listViewPadding:
|
||||||
searchFieldPadding: const EdgeInsets.fromLTRB(4, 8, 4, 4),
|
const EdgeInsets.fromLTRB(4, 0, 4, 4).scaled(context),
|
||||||
|
searchFieldPadding:
|
||||||
|
const EdgeInsets.fromLTRB(4, 8, 4, 4).scaled(context),
|
||||||
emptyWidget: contactList == null
|
emptyWidget: contactList == null
|
||||||
? waitingPage(
|
? waitingPage(
|
||||||
text: translate('contact_list.loading_contacts'))
|
text: translate('contact_list.loading_contacts'))
|
||||||
|
@ -254,8 +257,8 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
||||||
searchFieldEnabled: contactList != null,
|
searchFieldEnabled: contactList != null,
|
||||||
inputDecoration:
|
inputDecoration:
|
||||||
InputDecoration(labelText: translate('contact_list.search')),
|
InputDecoration(labelText: translate('contact_list.search')),
|
||||||
secondaryWidget:
|
secondaryWidget: buildInvitationButton(context)
|
||||||
buildInvitationButton(context).paddingLTRB(4, 0, 0, 0))
|
.paddingLTRB(4.scaled(context), 0, 0, 0))
|
||||||
.expanded()
|
.expanded()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ class _ContactsPageState extends State<ContactsPage> {
|
||||||
|
|
||||||
return StyledScaffold(
|
return StyledScaffold(
|
||||||
appBar: DefaultAppBar(
|
appBar: DefaultAppBar(
|
||||||
|
context: context,
|
||||||
title: Text(
|
title: Text(
|
||||||
!enableSplit && enableRight
|
!enableSplit && enableRight
|
||||||
? translate('contacts_dialog.edit_contact')
|
? translate('contacts_dialog.edit_contact')
|
||||||
|
@ -47,6 +48,7 @@ class _ContactsPageState extends State<ContactsPage> {
|
||||||
),
|
),
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(Icons.arrow_back),
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
iconSize: 24.scaled(context),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
singleFuture((this, _kDoBackArrow), () async {
|
singleFuture((this, _kDoBackArrow), () async {
|
||||||
final confirmed = await _onContactSelected(null);
|
final confirmed = await _onContactSelected(null);
|
||||||
|
@ -65,21 +67,21 @@ class _ContactsPageState extends State<ContactsPage> {
|
||||||
if (_selectedContact != null)
|
if (_selectedContact != null)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.chat_bubble),
|
icon: const Icon(Icons.chat_bubble),
|
||||||
iconSize: 24,
|
iconSize: 24.scaled(context),
|
||||||
color: appBarTheme.iconColor,
|
color: appBarTheme.iconColor,
|
||||||
tooltip: translate('contacts_dialog.new_chat'),
|
tooltip: translate('contacts_dialog.new_chat'),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await _onChatStarted(_selectedContact!);
|
await _onChatStarted(_selectedContact!);
|
||||||
}).paddingLTRB(8, 0, 8, 0),
|
}),
|
||||||
if (enableSplit && _selectedContact != null)
|
if (enableSplit && _selectedContact != null)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.close),
|
icon: const Icon(Icons.close),
|
||||||
iconSize: 24,
|
iconSize: 24.scaled(context),
|
||||||
color: appBarTheme.iconColor,
|
color: appBarTheme.iconColor,
|
||||||
tooltip: translate('contacts_dialog.close_contact'),
|
tooltip: translate('contacts_dialog.close_contact'),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await _onContactSelected(null);
|
await _onContactSelected(null);
|
||||||
}).paddingLTRB(8, 0, 8, 0),
|
}),
|
||||||
]),
|
]),
|
||||||
body: LayoutBuilder(builder: (context, constraint) {
|
body: LayoutBuilder(builder: (context, constraint) {
|
||||||
final maxWidth = constraint.maxWidth;
|
final maxWidth = constraint.maxWidth;
|
||||||
|
|
|
@ -92,16 +92,8 @@ class _EditContactFormState extends State<EditContactForm> {
|
||||||
Widget _editContactForm(BuildContext context) {
|
Widget _editContactForm(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final scale = theme.extension<ScaleScheme>()!;
|
final scale = theme.extension<ScaleScheme>()!;
|
||||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
|
||||||
final textTheme = theme.textTheme;
|
final textTheme = theme.textTheme;
|
||||||
|
|
||||||
late final Color border;
|
|
||||||
if (scaleConfig.useVisualIndicators && !scaleConfig.preferBorders) {
|
|
||||||
border = scale.primaryScale.elementBackground;
|
|
||||||
} else {
|
|
||||||
border = scale.primaryScale.subtleBorder;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FormBuilder(
|
return FormBuilder(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
@ -116,18 +108,12 @@ class _EditContactFormState extends State<EditContactForm> {
|
||||||
children: [
|
children: [
|
||||||
Row(children: [
|
Row(children: [
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
AvatarWidget(
|
StyledAvatar(
|
||||||
name: _currentValueNickname.isNotEmpty
|
name: _currentValueNickname.isNotEmpty
|
||||||
? _currentValueNickname
|
? _currentValueNickname
|
||||||
: widget.contact.profile.name,
|
: widget.contact.profile.name,
|
||||||
size: 128,
|
size: 128)
|
||||||
borderColor: border,
|
.paddingLTRB(0, 0, 0, 16),
|
||||||
foregroundColor: scale.primaryScale.primaryText,
|
|
||||||
backgroundColor: scale.primaryScale.primary,
|
|
||||||
scaleConfig: scaleConfig,
|
|
||||||
textStyle: theme.textTheme.titleLarge!
|
|
||||||
.copyWith(fontSize: 64),
|
|
||||||
).paddingLTRB(0, 0, 0, 16),
|
|
||||||
const Spacer()
|
const Spacer()
|
||||||
]),
|
]),
|
||||||
SelectableText(widget.contact.profile.name,
|
SelectableText(widget.contact.profile.name,
|
||||||
|
@ -211,10 +197,11 @@ class _EditContactFormState extends State<EditContactForm> {
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: _isModified ? _doSubmit : null,
|
onPressed: _isModified ? _doSubmit : null,
|
||||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||||
const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0),
|
Icon(Icons.check, size: 24.scaled(context))
|
||||||
Text(widget.submitText).paddingLTRB(0, 0, 4, 0)
|
.paddingLTRB(0, 0, 4, 0),
|
||||||
]),
|
Text(widget.submitText).paddingLTRB(0, 0, 4.scaled(context), 0)
|
||||||
).paddingSymmetric(vertical: 4).alignAtCenter(),
|
]).paddingAll(4.scaled(context)),
|
||||||
|
).paddingSymmetric(vertical: 4.scaled(context)).alignAtCenter(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -32,6 +32,14 @@ class DeveloperPageIntent extends Intent {
|
||||||
const DeveloperPageIntent();
|
const DeveloperPageIntent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DisplayScaleUpIntent extends Intent {
|
||||||
|
const DisplayScaleUpIntent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class DisplayScaleDownIntent extends Intent {
|
||||||
|
const DisplayScaleDownIntent();
|
||||||
|
}
|
||||||
|
|
||||||
class KeyboardShortcuts extends StatelessWidget {
|
class KeyboardShortcuts extends StatelessWidget {
|
||||||
const KeyboardShortcuts({required this.child, super.key});
|
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 {
|
singleFuture(this, () async {
|
||||||
final prefs = PreferencesRepository.instance.value;
|
final prefs = PreferencesRepository.instance.value;
|
||||||
|
|
||||||
|
@ -79,7 +87,7 @@ class KeyboardShortcuts extends StatelessWidget {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeColor(BuildContext context) {
|
void _changeColor(BuildContext context) {
|
||||||
singleFuture(this, () async {
|
singleFuture(this, () async {
|
||||||
final prefs = PreferencesRepository.instance.value;
|
final prefs = PreferencesRepository.instance.value;
|
||||||
final oldColor = prefs.themePreference.colorPreference;
|
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) {
|
void _attachDetach(BuildContext context) {
|
||||||
singleFuture(this, () async {
|
singleFuture(this, () async {
|
||||||
if (ProcessorRepository.instance.processorConnectionState.isAttached) {
|
if (ProcessorRepository.instance.processorConnectionState.isAttached) {
|
||||||
|
@ -125,44 +181,88 @@ class KeyboardShortcuts extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => ThemeSwitcher(
|
Widget build(BuildContext context) => ThemeSwitcher(
|
||||||
builder: (context) => Shortcuts(
|
builder: (context) => Shortcuts(
|
||||||
shortcuts: const <ShortcutActivator, Intent>{
|
shortcuts: <ShortcutActivator, Intent>{
|
||||||
SingleActivator(
|
////////////////////////// Reload Theme
|
||||||
|
const SingleActivator(
|
||||||
LogicalKeyboardKey.keyR,
|
LogicalKeyboardKey.keyR,
|
||||||
control: true,
|
control: true,
|
||||||
alt: true,
|
alt: true,
|
||||||
): ReloadThemeIntent(),
|
): const ReloadThemeIntent(),
|
||||||
SingleActivator(
|
////////////////////////// Switch Brightness
|
||||||
|
const SingleActivator(
|
||||||
LogicalKeyboardKey.keyB,
|
LogicalKeyboardKey.keyB,
|
||||||
control: true,
|
control: true,
|
||||||
alt: true,
|
alt: true,
|
||||||
): ChangeBrightnessIntent(),
|
): const ChangeBrightnessIntent(),
|
||||||
SingleActivator(
|
////////////////////////// Change Color
|
||||||
|
const SingleActivator(
|
||||||
LogicalKeyboardKey.keyC,
|
LogicalKeyboardKey.keyC,
|
||||||
control: true,
|
control: true,
|
||||||
alt: true,
|
alt: true,
|
||||||
): ChangeColorIntent(),
|
): const ChangeColorIntent(),
|
||||||
SingleActivator(
|
////////////////////////// Attach/Detach
|
||||||
|
if (kIsDebugMode)
|
||||||
|
const SingleActivator(
|
||||||
LogicalKeyboardKey.keyA,
|
LogicalKeyboardKey.keyA,
|
||||||
control: true,
|
control: true,
|
||||||
alt: true,
|
alt: true,
|
||||||
): AttachDetachIntent(),
|
): const AttachDetachIntent(),
|
||||||
SingleActivator(
|
////////////////////////// Show Developer Page
|
||||||
|
const SingleActivator(
|
||||||
LogicalKeyboardKey.keyD,
|
LogicalKeyboardKey.keyD,
|
||||||
control: true,
|
control: true,
|
||||||
alt: 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: <Type, Action<Intent>>{
|
child: Actions(actions: <Type, Action<Intent>>{
|
||||||
ReloadThemeIntent: CallbackAction<ReloadThemeIntent>(
|
ReloadThemeIntent: CallbackAction<ReloadThemeIntent>(
|
||||||
onInvoke: (intent) => reloadTheme(context)),
|
onInvoke: (intent) => reloadTheme(context)),
|
||||||
ChangeBrightnessIntent: CallbackAction<ChangeBrightnessIntent>(
|
ChangeBrightnessIntent: CallbackAction<ChangeBrightnessIntent>(
|
||||||
onInvoke: (intent) => changeBrightness(context)),
|
onInvoke: (intent) => _changeBrightness(context)),
|
||||||
ChangeColorIntent: CallbackAction<ChangeColorIntent>(
|
ChangeColorIntent: CallbackAction<ChangeColorIntent>(
|
||||||
onInvoke: (intent) => changeColor(context)),
|
onInvoke: (intent) => _changeColor(context)),
|
||||||
AttachDetachIntent: CallbackAction<AttachDetachIntent>(
|
AttachDetachIntent: CallbackAction<AttachDetachIntent>(
|
||||||
onInvoke: (intent) => _attachDetach(context)),
|
onInvoke: (intent) => _attachDetach(context)),
|
||||||
DeveloperPageIntent: CallbackAction<DeveloperPageIntent>(
|
DeveloperPageIntent: CallbackAction<DeveloperPageIntent>(
|
||||||
onInvoke: (intent) => _developerPage(context)),
|
onInvoke: (intent) => _developerPage(context)),
|
||||||
|
DisplayScaleUpIntent: CallbackAction<DisplayScaleUpIntent>(
|
||||||
|
onInvoke: (intent) => _displayScaleUp(context)),
|
||||||
|
DisplayScaleDownIntent: CallbackAction<DisplayScaleDownIntent>(
|
||||||
|
onInvoke: (intent) => _displayScaleDown(context)),
|
||||||
}, child: Focus(autofocus: true, child: child))));
|
}, child: Focus(autofocus: true, child: child))));
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -2,21 +2,27 @@ import 'package:awesome_extensions/awesome_extensions.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
|
||||||
|
import '../theme/theme.dart';
|
||||||
|
|
||||||
class DefaultAppBar extends AppBar {
|
class DefaultAppBar extends AppBar {
|
||||||
DefaultAppBar(
|
DefaultAppBar(
|
||||||
{super.title,
|
{required BuildContext context,
|
||||||
|
super.title,
|
||||||
super.flexibleSpace,
|
super.flexibleSpace,
|
||||||
super.key,
|
super.key,
|
||||||
Widget? leading,
|
Widget? leading,
|
||||||
super.actions})
|
super.actions})
|
||||||
: super(
|
: super(
|
||||||
|
toolbarHeight: 40.scaled(context),
|
||||||
|
leadingWidth: 40.scaled(context),
|
||||||
leading: leading ??
|
leading: leading ??
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.all(4),
|
margin: const EdgeInsets.all(4).scaled(context),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.black.withAlpha(32),
|
color: Colors.black.withAlpha(32),
|
||||||
shape: BoxShape.circle),
|
shape: BoxShape.circle),
|
||||||
child:
|
child: SvgPicture.asset('assets/images/vlogo.svg',
|
||||||
SvgPicture.asset('assets/images/vlogo.svg', height: 24)
|
width: 24.scaled(context),
|
||||||
.paddingAll(4)));
|
height: 24.scaled(context))
|
||||||
|
.paddingAll(4.scaled(context))));
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,19 +95,15 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||||
activeBorder = scale.primary;
|
activeBorder = scale.primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
final avatar = AvatarWidget(
|
final avatar = StyledAvatar(
|
||||||
name: name,
|
name: name,
|
||||||
size: 34,
|
size: 34.scaled(context),
|
||||||
borderColor: border,
|
|
||||||
foregroundColor: loggedIn ? scale.primaryText : scale.subtleText,
|
|
||||||
backgroundColor: loggedIn ? scale.primary : scale.elementBackground,
|
|
||||||
scaleConfig: scaleConfig,
|
|
||||||
textStyle: theme.textTheme.titleLarge!,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return AnimatedPadding(
|
return AnimatedPadding(
|
||||||
padding: EdgeInsets.fromLTRB(selected ? 0 : 8, selected ? 0 : 2,
|
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),
|
duration: const Duration(milliseconds: 50),
|
||||||
child: MenuItemWidget(
|
child: MenuItemWidget(
|
||||||
title: name,
|
title: name,
|
||||||
|
@ -144,7 +140,7 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||||
(scaleConfig.preferBorders || scaleConfig.useVisualIndicators)
|
(scaleConfig.preferBorders || scaleConfig.useVisualIndicators)
|
||||||
? null
|
? null
|
||||||
: activeBorder,
|
: activeBorder,
|
||||||
minHeight: 48,
|
minHeight: 48.scaled(context),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +192,8 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||||
color: scaleScheme.errorScale.subtleBorder,
|
color: scaleScheme.errorScale.subtleBorder,
|
||||||
borderRadius: 12 * scaleConfig.borderRadiusScale),
|
borderRadius: 12 * scaleConfig.borderRadiusScale),
|
||||||
);
|
);
|
||||||
loggedInAccounts.add(loggedInAccount.paddingLTRB(0, 0, 0, 8));
|
loggedInAccounts
|
||||||
|
.add(loggedInAccount.paddingLTRB(0, 0, 0, 8.scaled(context)));
|
||||||
} else {
|
} else {
|
||||||
// Account is not logged in
|
// Account is not logged in
|
||||||
final scale = theme.extension<ScaleScheme>()!.grayScale;
|
final scale = theme.extension<ScaleScheme>()!.grayScale;
|
||||||
|
@ -246,8 +243,8 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||||
}
|
}
|
||||||
return IconButton(
|
return IconButton(
|
||||||
icon: icon,
|
icon: icon,
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
color: border,
|
color: border,
|
||||||
constraints: const BoxConstraints.expand(height: 48, width: 48),
|
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor: WidgetStateProperty.resolveWith((states) {
|
backgroundColor: WidgetStateProperty.resolveWith((states) {
|
||||||
if (states.contains(WidgetState.hovered)) {
|
if (states.contains(WidgetState.hovered)) {
|
||||||
|
@ -286,7 +283,10 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||||
final scale = scaleScheme.scale(_scaleKind);
|
final scale = scaleScheme.scale(_scaleKind);
|
||||||
|
|
||||||
final settingsButton = _getButton(
|
final settingsButton = _getButton(
|
||||||
icon: const Icon(Icons.settings),
|
icon: const Icon(
|
||||||
|
Icons.settings,
|
||||||
|
applyTextScaling: true,
|
||||||
|
),
|
||||||
tooltip: translate('menu.settings_tooltip'),
|
tooltip: translate('menu.settings_tooltip'),
|
||||||
scale: scale,
|
scale: scale,
|
||||||
scaleConfig: scaleConfig,
|
scaleConfig: scaleConfig,
|
||||||
|
@ -295,7 +295,10 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||||
}).paddingLTRB(0, 0, 16, 0);
|
}).paddingLTRB(0, 0, 16, 0);
|
||||||
|
|
||||||
final addButton = _getButton(
|
final addButton = _getButton(
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(
|
||||||
|
Icons.add,
|
||||||
|
applyTextScaling: true,
|
||||||
|
),
|
||||||
tooltip: translate('menu.add_account_tooltip'),
|
tooltip: translate('menu.add_account_tooltip'),
|
||||||
scale: scale,
|
scale: scale,
|
||||||
scaleConfig: scaleConfig,
|
scaleConfig: scaleConfig,
|
||||||
|
@ -364,7 +367,7 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||||
// : null)
|
// : null)
|
||||||
// .paddingLTRB(0, 0, 16, 0),
|
// .paddingLTRB(0, 0, 16, 0),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onLongPress: () async {
|
onLongPress: () {
|
||||||
context
|
context
|
||||||
.findAncestorWidgetOfExactType<KeyboardShortcuts>()!
|
.findAncestorWidgetOfExactType<KeyboardShortcuts>()!
|
||||||
.reloadTheme(context);
|
.reloadTheme(context);
|
||||||
|
|
|
@ -2,6 +2,8 @@ import 'package:awesome_extensions/awesome_extensions.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../theme/views/preferences/preferences.dart';
|
||||||
|
|
||||||
class MenuItemWidget extends StatelessWidget {
|
class MenuItemWidget extends StatelessWidget {
|
||||||
const MenuItemWidget({
|
const MenuItemWidget({
|
||||||
required this.title,
|
required this.title,
|
||||||
|
@ -81,7 +83,7 @@ class MenuItemWidget extends StatelessWidget {
|
||||||
hoverColor: footerButtonIconHoverColor,
|
hoverColor: footerButtonIconHoverColor,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
footerButtonIcon,
|
footerButtonIcon,
|
||||||
size: 24,
|
size: 24.scaled(context),
|
||||||
),
|
),
|
||||||
onPressed: footerCallback),
|
onPressed: footerCallback),
|
||||||
],
|
],
|
||||||
|
|
|
@ -28,12 +28,16 @@ class _HomeAccountReadyState extends State<HomeAccountReady> {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final scale = theme.extension<ScaleScheme>()!;
|
final scale = theme.extension<ScaleScheme>()!;
|
||||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||||
return IconButton(
|
return AspectRatio(
|
||||||
icon: const Icon(Icons.menu),
|
aspectRatio: 1,
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.menu,
|
||||||
|
applyTextScaling: true,
|
||||||
|
),
|
||||||
color: scaleConfig.preferBorders
|
color: scaleConfig.preferBorders
|
||||||
? scale.primaryScale.border
|
? scale.primaryScale.border
|
||||||
: scale.primaryScale.borderText,
|
: scale.primaryScale.borderText,
|
||||||
constraints: const BoxConstraints.expand(height: 40, width: 40),
|
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor: WidgetStateProperty.all(
|
backgroundColor: WidgetStateProperty.all(
|
||||||
scaleConfig.preferBorders
|
scaleConfig.preferBorders
|
||||||
|
@ -49,26 +53,30 @@ class _HomeAccountReadyState extends State<HomeAccountReady> {
|
||||||
? scale.primaryScale.border
|
? scale.primaryScale.border
|
||||||
: scale.primaryScale.borderText,
|
: scale.primaryScale.borderText,
|
||||||
width: 2),
|
width: 2),
|
||||||
borderRadius: BorderRadius.all(
|
borderRadius: BorderRadius.all(Radius.circular(
|
||||||
Radius.circular(8 * scaleConfig.borderRadiusScale))),
|
8 * scaleConfig.borderRadiusScale))),
|
||||||
)),
|
)),
|
||||||
tooltip: translate('menu.accounts_menu_tooltip'),
|
tooltip: translate('menu.accounts_menu_tooltip'),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final ctrl = context.read<ZoomDrawerController>();
|
final ctrl = context.read<ZoomDrawerController>();
|
||||||
await ctrl.toggle?.call();
|
await ctrl.toggle?.call();
|
||||||
});
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
Widget buildContactsButton() => Builder(builder: (context) {
|
Widget buildContactsButton() => Builder(builder: (context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final scale = theme.extension<ScaleScheme>()!;
|
final scale = theme.extension<ScaleScheme>()!;
|
||||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||||
return IconButton(
|
return AspectRatio(
|
||||||
icon: const Icon(Icons.contacts),
|
aspectRatio: 1,
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.contacts,
|
||||||
|
applyTextScaling: true,
|
||||||
|
),
|
||||||
color: scaleConfig.preferBorders
|
color: scaleConfig.preferBorders
|
||||||
? scale.primaryScale.border
|
? scale.primaryScale.border
|
||||||
: scale.primaryScale.borderText,
|
: scale.primaryScale.borderText,
|
||||||
constraints: const BoxConstraints.expand(height: 40, width: 40),
|
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor: WidgetStateProperty.all(
|
backgroundColor: WidgetStateProperty.all(
|
||||||
scaleConfig.preferBorders
|
scaleConfig.preferBorders
|
||||||
|
@ -84,8 +92,8 @@ class _HomeAccountReadyState extends State<HomeAccountReady> {
|
||||||
? scale.primaryScale.border
|
? scale.primaryScale.border
|
||||||
: scale.primaryScale.borderText,
|
: scale.primaryScale.borderText,
|
||||||
width: 2),
|
width: 2),
|
||||||
borderRadius: BorderRadius.all(
|
borderRadius: BorderRadius.all(Radius.circular(
|
||||||
Radius.circular(8 * scaleConfig.borderRadiusScale))),
|
8 * scaleConfig.borderRadiusScale))),
|
||||||
)),
|
)),
|
||||||
tooltip: translate('menu.contacts_tooltip'),
|
tooltip: translate('menu.contacts_tooltip'),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
@ -94,7 +102,7 @@ class _HomeAccountReadyState extends State<HomeAccountReady> {
|
||||||
builder: (_) => const ContactsPage(),
|
builder: (_) => const ContactsPage(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
Widget buildLeftPane(BuildContext context) => Builder(
|
Widget buildLeftPane(BuildContext context) => Builder(
|
||||||
|
@ -112,14 +120,17 @@ class _HomeAccountReadyState extends State<HomeAccountReady> {
|
||||||
? scale.primaryScale.subtleBackground
|
? scale.primaryScale.subtleBackground
|
||||||
: scale.primaryScale.subtleBorder,
|
: scale.primaryScale.subtleBorder,
|
||||||
child: Column(children: <Widget>[
|
child: Column(children: <Widget>[
|
||||||
Row(children: [
|
IntrinsicHeight(
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
buildMenuButton().paddingLTRB(0, 0, 8, 0),
|
buildMenuButton().paddingLTRB(0, 0, 8, 0),
|
||||||
ProfileWidget(
|
ProfileWidget(
|
||||||
profile: profile,
|
profile: profile,
|
||||||
showPronouns: false,
|
showPronouns: false,
|
||||||
).expanded(),
|
).expanded(),
|
||||||
buildContactsButton().paddingLTRB(8, 0, 0, 0),
|
buildContactsButton().paddingLTRB(8, 0, 0, 0),
|
||||||
]).paddingAll(8),
|
])).paddingAll(8),
|
||||||
const ChatListWidget().expanded()
|
const ChatListWidget().expanded()
|
||||||
]));
|
]));
|
||||||
})));
|
})));
|
||||||
|
|
|
@ -71,8 +71,9 @@ class HomeScreenState extends State<HomeScreen>
|
||||||
context: context,
|
context: context,
|
||||||
title: translate('splash.beta_title'),
|
title: translate('splash.beta_title'),
|
||||||
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
|
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||||
const Icon(Icons.warning, size: 64),
|
Icon(Icons.warning, size: 64.scaled(context)),
|
||||||
RichText(
|
RichText(
|
||||||
|
textScaler: MediaQuery.of(context).textScaler,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
children: <TextSpan>[
|
children: <TextSpan>[
|
||||||
|
@ -206,11 +207,7 @@ class HomeScreenState extends State<HomeScreen>
|
||||||
.indexWhere((x) => x.superIdentity.recordKey == activeLocalAccount);
|
.indexWhere((x) => x.superIdentity.recordKey == activeLocalAccount);
|
||||||
final canClose = activeIndex != -1;
|
final canClose = activeIndex != -1;
|
||||||
|
|
||||||
return DefaultTextStyle(
|
final drawer = ZoomDrawer(
|
||||||
style: theme.textTheme.bodySmall!,
|
|
||||||
child: KeyboardAvoider(
|
|
||||||
curve: Curves.ease,
|
|
||||||
child: ZoomDrawer(
|
|
||||||
controller: _zoomDrawerController,
|
controller: _zoomDrawerController,
|
||||||
menuScreen: Builder(builder: (context) {
|
menuScreen: Builder(builder: (context) {
|
||||||
final zoomDrawer = ZoomDrawer.of(context);
|
final zoomDrawer = ZoomDrawer.of(context);
|
||||||
|
@ -233,7 +230,13 @@ class HomeScreenState extends State<HomeScreen>
|
||||||
disableDragGesture: !canClose,
|
disableDragGesture: !canClose,
|
||||||
mainScreenScale: .25,
|
mainScreenScale: .25,
|
||||||
slideWidth: min(360, MediaQuery.of(context).size.width * 0.9),
|
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: drawerWithAvoider);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -1,24 +1,13 @@
|
||||||
import 'package:awesome_extensions/awesome_extensions.dart';
|
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
|
||||||
import 'package:flutter_translate/flutter_translate.dart';
|
import 'package:flutter_translate/flutter_translate.dart';
|
||||||
|
|
||||||
import '../../settings/settings.dart';
|
import '../../settings/settings.dart';
|
||||||
import '../../theme/theme.dart';
|
import '../../theme/theme.dart';
|
||||||
import '../notifications.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(
|
Widget buildSettingsPageNotificationPreferences(
|
||||||
{required BuildContext context, required void Function() onChanged}) {
|
{required BuildContext context}) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final scale = theme.extension<ScaleScheme>()!;
|
final scale = theme.extension<ScaleScheme>()!;
|
||||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||||
|
@ -33,7 +22,6 @@ Widget buildSettingsPageNotificationPreferences(
|
||||||
final newPrefs = preferencesRepository.value
|
final newPrefs = preferencesRepository.value
|
||||||
.copyWith(notificationsPreference: newNotificationsPreference);
|
.copyWith(notificationsPreference: newNotificationsPreference);
|
||||||
await preferencesRepository.set(newPrefs);
|
await preferencesRepository.set(newPrefs);
|
||||||
onChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DropdownMenuItem<NotificationMode>> notificationModeItems() {
|
List<DropdownMenuItem<NotificationMode>> notificationModeItems() {
|
||||||
|
@ -54,9 +42,10 @@ Widget buildSettingsPageNotificationPreferences(
|
||||||
enabled: x.$2,
|
enabled: x.$2,
|
||||||
child: Text(
|
child: Text(
|
||||||
x.$3,
|
x.$3,
|
||||||
style: textTheme.labelSmall,
|
softWrap: false,
|
||||||
|
style: textTheme.labelMedium,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
)));
|
).fit(fit: BoxFit.scaleDown)));
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -77,7 +66,8 @@ Widget buildSettingsPageNotificationPreferences(
|
||||||
enabled: x.$2,
|
enabled: x.$2,
|
||||||
child: Text(
|
child: Text(
|
||||||
x.$3,
|
x.$3,
|
||||||
style: textTheme.labelSmall,
|
softWrap: false,
|
||||||
|
style: textTheme.labelMedium,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -110,7 +100,8 @@ Widget buildSettingsPageNotificationPreferences(
|
||||||
enabled: x.$2,
|
enabled: x.$2,
|
||||||
child: Text(
|
child: Text(
|
||||||
x.$3,
|
x.$3,
|
||||||
style: textTheme.labelSmall,
|
softWrap: false,
|
||||||
|
style: textTheme.labelMedium,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -127,66 +118,45 @@ Widget buildSettingsPageNotificationPreferences(
|
||||||
),
|
),
|
||||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||||
// Display Beta Warning
|
// Display Beta Warning
|
||||||
FormBuilderCheckbox(
|
StyledCheckbox(
|
||||||
name: formFieldDisplayBetaWarning,
|
label: translate('settings_page.display_beta_warning'),
|
||||||
title: Text(translate('settings_page.display_beta_warning'),
|
value: notificationsPreference.displayBetaWarning,
|
||||||
style: textTheme.labelMedium),
|
|
||||||
initialValue: notificationsPreference.displayBetaWarning,
|
|
||||||
onChanged: (value) async {
|
onChanged: (value) async {
|
||||||
if (value == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final newNotificationsPreference =
|
final newNotificationsPreference =
|
||||||
notificationsPreference.copyWith(displayBetaWarning: value);
|
notificationsPreference.copyWith(displayBetaWarning: value);
|
||||||
|
|
||||||
await updatePreferences(newNotificationsPreference);
|
await updatePreferences(newNotificationsPreference);
|
||||||
}),
|
}),
|
||||||
// Enable Badge
|
// Enable Badge
|
||||||
FormBuilderCheckbox(
|
StyledCheckbox(
|
||||||
name: formFieldEnableBadge,
|
label: translate('settings_page.enable_badge'),
|
||||||
title: Text(translate('settings_page.enable_badge'),
|
value: notificationsPreference.enableBadge,
|
||||||
style: textTheme.labelMedium),
|
|
||||||
initialValue: notificationsPreference.enableBadge,
|
|
||||||
onChanged: (value) async {
|
onChanged: (value) async {
|
||||||
if (value == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final newNotificationsPreference =
|
final newNotificationsPreference =
|
||||||
notificationsPreference.copyWith(enableBadge: value);
|
notificationsPreference.copyWith(enableBadge: value);
|
||||||
await updatePreferences(newNotificationsPreference);
|
await updatePreferences(newNotificationsPreference);
|
||||||
}),
|
}),
|
||||||
// Enable Notifications
|
// Enable Notifications
|
||||||
FormBuilderCheckbox(
|
StyledCheckbox(
|
||||||
name: formFieldEnableNotifications,
|
label: translate('settings_page.enable_notifications'),
|
||||||
title: Text(translate('settings_page.enable_notifications'),
|
value: notificationsPreference.enableNotifications,
|
||||||
style: textTheme.labelMedium),
|
|
||||||
initialValue: notificationsPreference.enableNotifications,
|
|
||||||
onChanged: (value) async {
|
onChanged: (value) async {
|
||||||
if (value == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final newNotificationsPreference =
|
final newNotificationsPreference =
|
||||||
notificationsPreference.copyWith(enableNotifications: value);
|
notificationsPreference.copyWith(enableNotifications: value);
|
||||||
await updatePreferences(newNotificationsPreference);
|
await updatePreferences(newNotificationsPreference);
|
||||||
}),
|
}),
|
||||||
|
StyledDropdown<MessageNotificationContent>(
|
||||||
FormBuilderDropdown(
|
items: messageNotificationContentItems(),
|
||||||
name: formFieldMessageNotificationContent,
|
value: notificationsPreference.messageNotificationContent,
|
||||||
isDense: false,
|
decoratorLabel: translate('settings_page.message_notification_content'),
|
||||||
decoration: InputDecoration(
|
onChanged: !notificationsPreference.enableNotifications
|
||||||
labelText: translate('settings_page.message_notification_content')),
|
? null
|
||||||
enabled: notificationsPreference.enableNotifications,
|
: (value) async {
|
||||||
initialValue: notificationsPreference.messageNotificationContent,
|
final newNotificationsPreference = notificationsPreference
|
||||||
onChanged: (value) async {
|
.copyWith(messageNotificationContent: value);
|
||||||
if (value == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final newNotificationsPreference = notificationsPreference.copyWith(
|
|
||||||
messageNotificationContent: value);
|
|
||||||
await updatePreferences(newNotificationsPreference);
|
await updatePreferences(newNotificationsPreference);
|
||||||
},
|
},
|
||||||
items: messageNotificationContentItems(),
|
).paddingLTRB(0, 4.scaled(context), 0, 4.scaled(context)),
|
||||||
).paddingLTRB(0, 4, 0, 4),
|
|
||||||
|
|
||||||
// Notifications
|
// Notifications
|
||||||
Table(
|
Table(
|
||||||
|
@ -199,95 +169,85 @@ Widget buildSettingsPageNotificationPreferences(
|
||||||
color: scale.primaryScale.border,
|
color: scale.primaryScale.border,
|
||||||
decorationColor: scale.primaryScale.border,
|
decorationColor: scale.primaryScale.border,
|
||||||
decoration: TextDecoration.underline))
|
decoration: TextDecoration.underline))
|
||||||
.paddingAll(8),
|
.paddingAll(8.scaled(context)),
|
||||||
Text(translate('settings_page.delivery'),
|
Text(translate('settings_page.delivery'),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: textTheme.titleMedium!.copyWith(
|
style: textTheme.titleMedium!.copyWith(
|
||||||
color: scale.primaryScale.border,
|
color: scale.primaryScale.border,
|
||||||
decorationColor: scale.primaryScale.border,
|
decorationColor: scale.primaryScale.border,
|
||||||
decoration: TextDecoration.underline))
|
decoration: TextDecoration.underline))
|
||||||
.paddingAll(8),
|
.paddingAll(8.scaled(context)),
|
||||||
Text(translate('settings_page.sound'),
|
Text(translate('settings_page.sound'),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: textTheme.titleMedium!.copyWith(
|
style: textTheme.titleMedium!.copyWith(
|
||||||
color: scale.primaryScale.border,
|
color: scale.primaryScale.border,
|
||||||
decorationColor: scale.primaryScale.border,
|
decorationColor: scale.primaryScale.border,
|
||||||
decoration: TextDecoration.underline))
|
decoration: TextDecoration.underline))
|
||||||
.paddingAll(8),
|
.paddingAll(8.scaled(context)),
|
||||||
]),
|
]),
|
||||||
TableRow(children: [
|
TableRow(children: [
|
||||||
// Invitation accepted
|
// Invitation accepted
|
||||||
Text(
|
Text(
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
translate('settings_page.invitation_accepted'))
|
translate('settings_page.invitation_accepted'))
|
||||||
.paddingAll(8),
|
.paddingAll(4.scaled(context)),
|
||||||
FormBuilderDropdown(
|
StyledDropdown<NotificationMode>(
|
||||||
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);
|
|
||||||
},
|
|
||||||
items: notificationModeItems(),
|
items: notificationModeItems(),
|
||||||
).paddingAll(4),
|
value: notificationsPreference.onInvitationAcceptedMode,
|
||||||
FormBuilderDropdown(
|
onChanged: !notificationsPreference.enableNotifications
|
||||||
name: formFieldInvitationAcceptSound,
|
? null
|
||||||
isDense: false,
|
: (value) async {
|
||||||
enabled: notificationsPreference.enableNotifications,
|
final newNotificationsPreference =
|
||||||
initialValue: notificationsPreference.onInvitationAcceptedSound,
|
notificationsPreference.copyWith(
|
||||||
onChanged: (value) async {
|
onInvitationAcceptedMode: value);
|
||||||
if (value == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final newNotificationsPreference = notificationsPreference
|
|
||||||
.copyWith(onInvitationAcceptedSound: value);
|
|
||||||
await updatePreferences(newNotificationsPreference);
|
await updatePreferences(newNotificationsPreference);
|
||||||
},
|
},
|
||||||
|
).paddingAll(4.scaled(context)),
|
||||||
|
StyledDropdown<SoundEffect>(
|
||||||
items: soundEffectItems(),
|
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
|
// Message received
|
||||||
TableRow(children: [
|
TableRow(children: [
|
||||||
Text(
|
Text(
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
translate('settings_page.message_received'))
|
translate('settings_page.message_received'))
|
||||||
.paddingAll(8),
|
.paddingAll(4.scaled(context)),
|
||||||
FormBuilderDropdown(
|
StyledDropdown<NotificationMode>(
|
||||||
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);
|
|
||||||
},
|
|
||||||
items: notificationModeItems(),
|
items: notificationModeItems(),
|
||||||
).paddingAll(4),
|
value: notificationsPreference.onMessageReceivedMode,
|
||||||
FormBuilderDropdown(
|
onChanged: !notificationsPreference.enableNotifications
|
||||||
name: formFieldMessageReceivedSound,
|
? null
|
||||||
isDense: false,
|
: (value) async {
|
||||||
enabled: notificationsPreference.enableNotifications,
|
final newNotificationsPreference =
|
||||||
initialValue: notificationsPreference.onMessageReceivedSound,
|
notificationsPreference.copyWith(
|
||||||
onChanged: (value) async {
|
onMessageReceivedMode: value);
|
||||||
if (value == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final newNotificationsPreference = notificationsPreference
|
|
||||||
.copyWith(onMessageReceivedSound: value);
|
|
||||||
await updatePreferences(newNotificationsPreference);
|
await updatePreferences(newNotificationsPreference);
|
||||||
},
|
},
|
||||||
|
).paddingAll(4),
|
||||||
|
StyledDropdown<SoundEffect>(
|
||||||
items: soundEffectItems(),
|
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
|
// Message sent
|
||||||
|
@ -295,25 +255,23 @@ Widget buildSettingsPageNotificationPreferences(
|
||||||
Text(
|
Text(
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
translate('settings_page.message_sent'))
|
translate('settings_page.message_sent'))
|
||||||
.paddingAll(8),
|
.paddingAll(4.scaled(context)),
|
||||||
const SizedBox.shrink(),
|
const SizedBox.shrink(),
|
||||||
FormBuilderDropdown(
|
StyledDropdown<SoundEffect>(
|
||||||
name: formFieldMessageSentSound,
|
items: soundEffectItems(),
|
||||||
isDense: false,
|
value: notificationsPreference.onMessageSentSound,
|
||||||
enabled: notificationsPreference.enableNotifications,
|
onChanged: !notificationsPreference.enableNotifications
|
||||||
initialValue: notificationsPreference.onMessageSentSound,
|
? null
|
||||||
onChanged: (value) async {
|
: (value) async {
|
||||||
if (value == null) {
|
final newNotificationsPreference =
|
||||||
return;
|
notificationsPreference.copyWith(
|
||||||
}
|
onMessageSentSound: value);
|
||||||
final newNotificationsPreference = notificationsPreference
|
|
||||||
.copyWith(onMessageSentSound: value);
|
|
||||||
await updatePreferences(newNotificationsPreference);
|
await updatePreferences(newNotificationsPreference);
|
||||||
},
|
},
|
||||||
items: soundEffectItems(),
|
).paddingLTRB(
|
||||||
).paddingLTRB(4, 4, 0, 4)
|
4.scaled(context), 4.scaled(context), 0, 4.scaled(context))
|
||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
]).paddingAll(8),
|
]).paddingAll(8.scaled(context)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import '../../keyboard_shortcuts.dart';
|
import '../../keyboard_shortcuts.dart';
|
||||||
import '../../notifications/notifications.dart';
|
import '../../notifications/notifications.dart';
|
||||||
|
import '../../settings/settings.dart';
|
||||||
import '../../theme/theme.dart';
|
import '../../theme/theme.dart';
|
||||||
|
|
||||||
class RouterShell extends StatelessWidget {
|
class RouterShell extends StatelessWidget {
|
||||||
|
@ -10,7 +12,13 @@ class RouterShell extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => PopControl(
|
Widget build(BuildContext context) => PopControl(
|
||||||
dismissible: false,
|
dismissible: false,
|
||||||
child: NotificationsWidget(child: KeyboardShortcuts(child: _child)));
|
child: AsyncBlocBuilder<PreferencesCubit, Preferences>(
|
||||||
|
builder: (context, state) => MediaQuery(
|
||||||
|
data: MediaQuery.of(context).copyWith(
|
||||||
|
textScaler:
|
||||||
|
TextScaler.linear(state.themePreference.displayScale)),
|
||||||
|
child: NotificationsWidget(
|
||||||
|
child: KeyboardShortcuts(child: _child)))));
|
||||||
|
|
||||||
final Widget _child;
|
final Widget _child;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ sealed class LockPreference with _$LockPreference {
|
||||||
factory LockPreference.fromJson(dynamic json) =>
|
factory LockPreference.fromJson(dynamic json) =>
|
||||||
_$LockPreferenceFromJson(json as Map<String, dynamic>);
|
_$LockPreferenceFromJson(json as Map<String, dynamic>);
|
||||||
|
|
||||||
static const LockPreference defaults = LockPreference();
|
static const defaults = LockPreference();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Theme supports multiple translations
|
// Theme supports multiple translations
|
||||||
|
@ -49,5 +49,5 @@ sealed class Preferences with _$Preferences {
|
||||||
factory Preferences.fromJson(dynamic json) =>
|
factory Preferences.fromJson(dynamic json) =>
|
||||||
_$PreferencesFromJson(json as Map<String, dynamic>);
|
_$PreferencesFromJson(json as Map<String, dynamic>);
|
||||||
|
|
||||||
static const Preferences defaults = Preferences();
|
static const defaults = Preferences();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import 'package:animated_theme_switcher/animated_theme_switcher.dart';
|
import 'package:animated_theme_switcher/animated_theme_switcher.dart';
|
||||||
import 'package:awesome_extensions/awesome_extensions.dart';
|
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
|
||||||
import 'package:flutter_translate/flutter_translate.dart';
|
import 'package:flutter_translate/flutter_translate.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
@ -11,62 +10,53 @@ import '../theme/theme.dart';
|
||||||
import '../veilid_processor/veilid_processor.dart';
|
import '../veilid_processor/veilid_processor.dart';
|
||||||
import 'settings.dart';
|
import 'settings.dart';
|
||||||
|
|
||||||
class SettingsPage extends StatefulWidget {
|
class SettingsPage extends StatelessWidget {
|
||||||
const SettingsPage({super.key});
|
const SettingsPage({super.key});
|
||||||
|
|
||||||
@override
|
|
||||||
SettingsPageState createState() => SettingsPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class SettingsPageState extends State<SettingsPage> {
|
|
||||||
final _formKey = GlobalKey<FormBuilderState>();
|
|
||||||
static const String formFieldTheme = 'theme';
|
|
||||||
static const String formFieldBrightness = 'brightness';
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) =>
|
Widget build(BuildContext context) =>
|
||||||
AsyncBlocBuilder<PreferencesCubit, Preferences>(
|
AsyncBlocBuilder<PreferencesCubit, Preferences>(
|
||||||
builder: (context, state) => ThemeSwitcher.withTheme(
|
builder: (context, state) => ThemeSwitcher.withTheme(
|
||||||
builder: (_, switcher, theme) => StyledScaffold(
|
builder: (_, switcher, theme) => StyledScaffold(
|
||||||
appBar: DefaultAppBar(
|
appBar: DefaultAppBar(
|
||||||
|
context: context,
|
||||||
title: Text(translate('settings_page.titlebar')),
|
title: Text(translate('settings_page.titlebar')),
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(Icons.arrow_back),
|
iconSize: 24.scaled(context),
|
||||||
|
icon: Icon(Icons.arrow_back),
|
||||||
onPressed: () => GoRouterHelper(context).pop(),
|
onPressed: () => GoRouterHelper(context).pop(),
|
||||||
),
|
),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
const SignalStrengthMeterWidget()
|
const SignalStrengthMeterWidget()
|
||||||
.paddingLTRB(16, 0, 16, 0),
|
.paddingLTRB(16, 0, 16, 0),
|
||||||
]),
|
]),
|
||||||
body: ThemeSwitchingArea(
|
body: ListView(
|
||||||
child: FormBuilder(
|
padding: const EdgeInsets.all(8).scaled(context),
|
||||||
key: _formKey,
|
|
||||||
child: ListView(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
children: [
|
children: [
|
||||||
buildSettingsPageColorPreferences(
|
buildSettingsPageColorPreferences(
|
||||||
context: context,
|
context: context,
|
||||||
switcher: switcher,
|
switcher: switcher,
|
||||||
onChanged: () => setState(() {}))
|
),
|
||||||
.paddingLTRB(0, 8, 0, 0),
|
|
||||||
buildSettingsPageBrightnessPreferences(
|
buildSettingsPageBrightnessPreferences(
|
||||||
context: context,
|
context: context,
|
||||||
switcher: switcher,
|
switcher: switcher,
|
||||||
onChanged: () => setState(() {})),
|
),
|
||||||
|
buildSettingsPageDisplayScalePreferences(
|
||||||
|
context: context,
|
||||||
|
switcher: switcher,
|
||||||
|
),
|
||||||
buildSettingsPageWallpaperPreferences(
|
buildSettingsPageWallpaperPreferences(
|
||||||
context: context,
|
context: context,
|
||||||
switcher: switcher,
|
switcher: switcher,
|
||||||
onChanged: () => setState(() {})),
|
),
|
||||||
buildSettingsPageNotificationPreferences(
|
buildSettingsPageNotificationPreferences(
|
||||||
context: context,
|
context: context,
|
||||||
onChanged: () => setState(() {})),
|
|
||||||
].map((x) => x.paddingLTRB(0, 0, 0, 8)).toList(),
|
|
||||||
),
|
),
|
||||||
).paddingSymmetric(horizontal: 8, vertical: 8),
|
]
|
||||||
))));
|
.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)),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -308,6 +308,13 @@ ThemeData contrastGenerator({
|
||||||
side: elementBorderWidgetStateProperty(),
|
side: elementBorderWidgetStateProperty(),
|
||||||
backgroundColor: elementBackgroundWidgetStateProperty()));
|
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(
|
final themeData = baseThemeData.copyWith(
|
||||||
// chipTheme: baseThemeData.chipTheme.copyWith(
|
// chipTheme: baseThemeData.chipTheme.copyWith(
|
||||||
// backgroundColor: scaleScheme.primaryScale.elementBackground,
|
// backgroundColor: scaleScheme.primaryScale.elementBackground,
|
||||||
|
@ -316,6 +323,7 @@ ThemeData contrastGenerator({
|
||||||
// checkmarkColor: scaleScheme.primaryScale.border,
|
// checkmarkColor: scaleScheme.primaryScale.border,
|
||||||
// side: BorderSide(color: scaleScheme.primaryScale.border)),
|
// side: BorderSide(color: scaleScheme.primaryScale.border)),
|
||||||
elevatedButtonTheme: elevatedButtonTheme,
|
elevatedButtonTheme: elevatedButtonTheme,
|
||||||
|
sliderTheme: sliderTheme,
|
||||||
textSelectionTheme: TextSelectionThemeData(
|
textSelectionTheme: TextSelectionThemeData(
|
||||||
cursorColor: scheme.primaryScale.appText,
|
cursorColor: scheme.primaryScale.appText,
|
||||||
selectionColor: scheme.primaryScale.appText.withAlpha(0x7F),
|
selectionColor: scheme.primaryScale.appText.withAlpha(0x7F),
|
||||||
|
|
|
@ -132,6 +132,13 @@ class ScaleTheme extends ThemeExtension<ScaleTheme> {
|
||||||
iconColor: elementColorWidgetStateProperty(),
|
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(
|
final themeData = baseThemeData.copyWith(
|
||||||
scrollbarTheme: baseThemeData.scrollbarTheme.copyWith(
|
scrollbarTheme: baseThemeData.scrollbarTheme.copyWith(
|
||||||
thumbColor: WidgetStateProperty.resolveWith((states) {
|
thumbColor: WidgetStateProperty.resolveWith((states) {
|
||||||
|
@ -183,6 +190,7 @@ class ScaleTheme extends ThemeExtension<ScaleTheme> {
|
||||||
elevatedButtonTheme: elevatedButtonTheme,
|
elevatedButtonTheme: elevatedButtonTheme,
|
||||||
inputDecorationTheme:
|
inputDecorationTheme:
|
||||||
ScaleInputDecoratorTheme(scheme, config, textTheme),
|
ScaleInputDecoratorTheme(scheme, config, textTheme),
|
||||||
|
sliderTheme: sliderTheme,
|
||||||
extensions: <ThemeExtension<dynamic>>[scheme, config, this]);
|
extensions: <ThemeExtension<dynamic>>[scheme, config, this]);
|
||||||
|
|
||||||
return themeData;
|
return themeData;
|
||||||
|
|
|
@ -61,7 +61,7 @@ sealed class ThemePreferences with _$ThemePreferences {
|
||||||
factory ThemePreferences.fromJson(dynamic json) =>
|
factory ThemePreferences.fromJson(dynamic json) =>
|
||||||
_$ThemePreferencesFromJson(json as Map<String, dynamic>);
|
_$ThemePreferencesFromJson(json as Map<String, dynamic>);
|
||||||
|
|
||||||
static const ThemePreferences defaults = ThemePreferences();
|
static const defaults = ThemePreferences();
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ThemePreferencesExt on ThemePreferences {
|
extension ThemePreferencesExt on ThemePreferences {
|
||||||
|
|
|
@ -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<Object>? 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<Object>? _imageProvider;
|
|
||||||
}
|
|
|
@ -32,7 +32,7 @@ class _EnterPasswordDialogState extends State<EnterPasswordDialog> {
|
||||||
final passwordController = TextEditingController();
|
final passwordController = TextEditingController();
|
||||||
final focusNode = FocusNode();
|
final focusNode = FocusNode();
|
||||||
final formKey = GlobalKey<FormState>();
|
final formKey = GlobalKey<FormState>();
|
||||||
bool _passwordVisible = false;
|
var _passwordVisible = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -47,7 +47,6 @@ class _EnterPasswordDialogState extends State<EnterPasswordDialog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
// ignore: prefer_expression_function_bodies
|
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final scale = theme.extension<ScaleScheme>()!;
|
final scale = theme.extension<ScaleScheme>()!;
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
import 'package:animated_theme_switcher/animated_theme_switcher.dart';
|
import 'package:animated_theme_switcher/animated_theme_switcher.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
|
||||||
import 'package:flutter_translate/flutter_translate.dart';
|
import 'package:flutter_translate/flutter_translate.dart';
|
||||||
|
|
||||||
import '../../settings/settings.dart';
|
import '../../../settings/settings.dart';
|
||||||
import '../models/models.dart';
|
import '../../models/models.dart';
|
||||||
|
import '../views.dart';
|
||||||
|
|
||||||
const String formFieldBrightness = 'brightness';
|
List<DropdownMenuItem<BrightnessPreference>> _getBrightnessDropdownItems() {
|
||||||
|
|
||||||
List<DropdownMenuItem<dynamic>> _getBrightnessDropdownItems() {
|
|
||||||
const brightnessPrefs = BrightnessPreference.values;
|
const brightnessPrefs = BrightnessPreference.values;
|
||||||
final brightnessNames = {
|
final brightnessNames = {
|
||||||
BrightnessPreference.system: translate('brightness.system'),
|
BrightnessPreference.system: translate('brightness.system'),
|
||||||
|
@ -22,25 +20,21 @@ List<DropdownMenuItem<dynamic>> _getBrightnessDropdownItems() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildSettingsPageBrightnessPreferences(
|
Widget buildSettingsPageBrightnessPreferences(
|
||||||
{required BuildContext context,
|
{required BuildContext context, required ThemeSwitcherState switcher}) {
|
||||||
required void Function() onChanged,
|
|
||||||
required ThemeSwitcherState switcher}) {
|
|
||||||
final preferencesRepository = PreferencesRepository.instance;
|
final preferencesRepository = PreferencesRepository.instance;
|
||||||
final themePreferences = preferencesRepository.value.themePreference;
|
final themePreferences = preferencesRepository.value.themePreference;
|
||||||
return FormBuilderDropdown(
|
|
||||||
name: formFieldBrightness,
|
return StyledDropdown<BrightnessPreference>(
|
||||||
decoration: InputDecoration(
|
|
||||||
label: Text(translate('settings_page.brightness_mode'))),
|
|
||||||
items: _getBrightnessDropdownItems(),
|
items: _getBrightnessDropdownItems(),
|
||||||
initialValue: themePreferences.brightnessPreference,
|
value: themePreferences.brightnessPreference,
|
||||||
|
decoratorLabel: translate('settings_page.brightness_mode'),
|
||||||
onChanged: (value) async {
|
onChanged: (value) async {
|
||||||
final newThemePrefs = themePreferences.copyWith(
|
final newThemePrefs =
|
||||||
brightnessPreference: value as BrightnessPreference);
|
themePreferences.copyWith(brightnessPreference: value);
|
||||||
final newPrefs = preferencesRepository.value
|
final newPrefs = preferencesRepository.value
|
||||||
.copyWith(themePreference: newThemePrefs);
|
.copyWith(themePreference: newThemePrefs);
|
||||||
|
|
||||||
await preferencesRepository.set(newPrefs);
|
await preferencesRepository.set(newPrefs);
|
||||||
switcher.changeTheme(theme: newThemePrefs.themeData());
|
switcher.changeTheme(theme: newThemePrefs.themeData());
|
||||||
onChanged();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -1,16 +1,12 @@
|
||||||
import 'package:animated_theme_switcher/animated_theme_switcher.dart';
|
import 'package:animated_theme_switcher/animated_theme_switcher.dart';
|
||||||
import 'package:async_tools/async_tools.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
|
||||||
import 'package:flutter_translate/flutter_translate.dart';
|
import 'package:flutter_translate/flutter_translate.dart';
|
||||||
|
|
||||||
import '../../settings/settings.dart';
|
import '../../../settings/settings.dart';
|
||||||
import '../models/models.dart';
|
import '../../models/models.dart';
|
||||||
|
import '../views.dart';
|
||||||
|
|
||||||
const String formFieldTheme = 'theme';
|
List<DropdownMenuItem<ColorPreference>> _getThemeDropdownItems() {
|
||||||
const String _kSwitchTheme = 'switchTheme';
|
|
||||||
|
|
||||||
List<DropdownMenuItem<dynamic>> _getThemeDropdownItems() {
|
|
||||||
const colorPrefs = ColorPreference.values;
|
const colorPrefs = ColorPreference.values;
|
||||||
final colorNames = {
|
final colorNames = {
|
||||||
ColorPreference.scarlet: translate('themes.scarlet'),
|
ColorPreference.scarlet: translate('themes.scarlet'),
|
||||||
|
@ -34,27 +30,20 @@ List<DropdownMenuItem<dynamic>> _getThemeDropdownItems() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildSettingsPageColorPreferences(
|
Widget buildSettingsPageColorPreferences(
|
||||||
{required BuildContext context,
|
{required BuildContext context, required ThemeSwitcherState switcher}) {
|
||||||
required void Function() onChanged,
|
|
||||||
required ThemeSwitcherState switcher}) {
|
|
||||||
final preferencesRepository = PreferencesRepository.instance;
|
final preferencesRepository = PreferencesRepository.instance;
|
||||||
final themePreferences = preferencesRepository.value.themePreference;
|
final themePreferences = preferencesRepository.value.themePreference;
|
||||||
return FormBuilderDropdown(
|
|
||||||
name: formFieldTheme,
|
return StyledDropdown<ColorPreference>(
|
||||||
decoration:
|
|
||||||
InputDecoration(label: Text(translate('settings_page.color_theme'))),
|
|
||||||
items: _getThemeDropdownItems(),
|
items: _getThemeDropdownItems(),
|
||||||
initialValue: themePreferences.colorPreference,
|
value: themePreferences.colorPreference,
|
||||||
onChanged: (value) {
|
decoratorLabel: translate('settings_page.color_theme'),
|
||||||
singleFuture(_kSwitchTheme, () async {
|
onChanged: (value) async {
|
||||||
final newThemePrefs = themePreferences.copyWith(
|
final newThemePrefs = themePreferences.copyWith(colorPreference: value);
|
||||||
colorPreference: value as ColorPreference);
|
|
||||||
final newPrefs = preferencesRepository.value
|
final newPrefs = preferencesRepository.value
|
||||||
.copyWith(themePreference: newThemePrefs);
|
.copyWith(themePreference: newThemePrefs);
|
||||||
|
|
||||||
await preferencesRepository.set(newPrefs);
|
await preferencesRepository.set(newPrefs);
|
||||||
switcher.changeTheme(theme: newThemePrefs.themeData());
|
switcher.changeTheme(theme: newThemePrefs.themeData());
|
||||||
onChanged();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
109
lib/theme/views/preferences/display_scale_preferences.dart
Normal file
109
lib/theme/views/preferences/display_scale_preferences.dart
Normal file
|
@ -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 = <double>[
|
||||||
|
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<PreferencesCubit>().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<PreferencesCubit>().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]);
|
||||||
|
}
|
||||||
|
}
|
4
lib/theme/views/preferences/preferences.dart
Normal file
4
lib/theme/views/preferences/preferences.dart
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export 'brightness_preferences.dart';
|
||||||
|
export 'color_preferences.dart';
|
||||||
|
export 'display_scale_preferences.dart';
|
||||||
|
export 'wallpaper_preferences.dart';
|
25
lib/theme/views/preferences/wallpaper_preferences.dart
Normal file
25
lib/theme/views/preferences/wallpaper_preferences.dart
Normal file
|
@ -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());
|
||||||
|
});
|
||||||
|
}
|
|
@ -3,6 +3,10 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
final isAndroid = !kIsWeb && defaultTargetPlatform == TargetPlatform.android;
|
final isAndroid = !kIsWeb && defaultTargetPlatform == TargetPlatform.android;
|
||||||
final isiOS = !kIsWeb && defaultTargetPlatform == TargetPlatform.iOS;
|
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 &&
|
final isMobile = !kIsWeb &&
|
||||||
(defaultTargetPlatform == TargetPlatform.iOS ||
|
(defaultTargetPlatform == TargetPlatform.iOS ||
|
||||||
defaultTargetPlatform == TargetPlatform.android);
|
defaultTargetPlatform == TargetPlatform.android);
|
||||||
|
|
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_translate/flutter_translate.dart';
|
import 'package:flutter_translate/flutter_translate.dart';
|
||||||
import 'package:rflutter_alert/rflutter_alert.dart';
|
import 'package:rflutter_alert/rflutter_alert.dart';
|
||||||
|
|
||||||
import '../theme.dart';
|
import '../../theme.dart';
|
||||||
|
|
||||||
AlertStyle _alertStyle(BuildContext context) {
|
AlertStyle _alertStyle(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
|
@ -186,6 +186,7 @@ Future<void> showAlertWidgetModal(
|
||||||
child: Text(
|
child: Text(
|
||||||
translate('button.ok'),
|
translate('button.ok'),
|
||||||
style: _buttonTextStyle(context),
|
style: _buttonTextStyle(context),
|
||||||
|
softWrap: true,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
77
lib/theme/views/styled_widgets/styled_avatar.dart
Normal file
77
lib/theme/views/styled_widgets/styled_avatar.dart
Normal file
|
@ -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<Object>? 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<ScaleTheme>()!;
|
||||||
|
|
||||||
|
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<Object>? _imageProvider;
|
||||||
|
final bool _enabled;
|
||||||
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
import 'package:awesome_extensions/awesome_extensions.dart';
|
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../theme.dart';
|
import '../../theme.dart';
|
||||||
|
|
||||||
class OptionBox extends StatelessWidget {
|
class StyledButtonBox extends StatelessWidget {
|
||||||
const OptionBox(
|
const StyledButtonBox(
|
||||||
{required String instructions,
|
{required String instructions,
|
||||||
required IconData buttonIcon,
|
required IconData buttonIcon,
|
||||||
required String buttonText,
|
required String buttonText,
|
||||||
|
@ -41,12 +41,15 @@ class OptionBox extends StatelessWidget {
|
||||||
onPressed: _onClick,
|
onPressed: _onClick,
|
||||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||||
Icon(_buttonIcon,
|
Icon(_buttonIcon,
|
||||||
size: 24, color: scale.primaryScale.appText)
|
size: 24.scaled(context),
|
||||||
.paddingLTRB(0, 8, 8, 8),
|
color: scale.primaryScale.appText)
|
||||||
|
.paddingLTRB(0, 8.scaled(context),
|
||||||
|
8.scaled(context), 8.scaled(context)),
|
||||||
Text(textAlign: TextAlign.center, _buttonText)
|
Text(textAlign: TextAlign.center, _buttonText)
|
||||||
])).paddingLTRB(0, 12, 0, 0).toCenter()
|
])).paddingLTRB(0, 12.scaled(context), 0, 0).toCenter()
|
||||||
]).paddingAll(12))
|
]).paddingAll(12.scaled(context)))
|
||||||
.paddingLTRB(24, 0, 24, 12);
|
.paddingLTRB(
|
||||||
|
24.scaled(context), 0, 24.scaled(context), 12.scaled(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
final String _instructions;
|
final String _instructions;
|
63
lib/theme/views/styled_widgets/styled_checkbox.dart
Normal file
63
lib/theme/views/styled_widgets/styled_checkbox.dart
Normal file
|
@ -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<void> 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<void> Function(bool)? _onChanged;
|
||||||
|
final bool _value;
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ import 'package:awesome_extensions/awesome_extensions.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../theme.dart';
|
import '../../theme.dart';
|
||||||
|
|
||||||
class StyledDialog extends StatelessWidget {
|
class StyledDialog extends StatelessWidget {
|
||||||
const StyledDialog({required this.title, required this.child, super.key});
|
const StyledDialog({required this.title, required this.child, super.key});
|
59
lib/theme/views/styled_widgets/styled_dropdown.dart
Normal file
59
lib/theme/views/styled_widgets/styled_dropdown.dart
Normal file
|
@ -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<T> extends StatelessWidget {
|
||||||
|
const StyledDropdown(
|
||||||
|
{required List<DropdownMenuItem<T>> items,
|
||||||
|
required T value,
|
||||||
|
String? decoratorLabel,
|
||||||
|
Future<void> 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<ScaleScheme>()!;
|
||||||
|
|
||||||
|
Widget ctrl = DropdownButton<T>(
|
||||||
|
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<DropdownMenuItem<T>> _items;
|
||||||
|
final String? _decoratorLabel;
|
||||||
|
final Future<void> Function(T)? _onChanged;
|
||||||
|
final T _value;
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../theme.dart';
|
import '../../theme.dart';
|
||||||
|
|
||||||
class StyledScaffold extends StatelessWidget {
|
class StyledScaffold extends StatelessWidget {
|
||||||
const StyledScaffold({required this.appBar, required this.body, super.key});
|
const StyledScaffold({required this.appBar, required this.body, super.key});
|
|
@ -2,10 +2,10 @@ import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
|
|
||||||
import '../theme.dart';
|
import '../../theme.dart';
|
||||||
|
|
||||||
class SliderTileAction {
|
class SlideTileAction {
|
||||||
const SliderTileAction({
|
const SlideTileAction({
|
||||||
required this.actionScale,
|
required this.actionScale,
|
||||||
required this.onPressed,
|
required this.onPressed,
|
||||||
this.key,
|
this.key,
|
||||||
|
@ -20,8 +20,8 @@ class SliderTileAction {
|
||||||
final SlidableActionCallback? onPressed;
|
final SlidableActionCallback? onPressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
class SliderTile extends StatelessWidget {
|
class StyledSlideTile extends StatelessWidget {
|
||||||
const SliderTile(
|
const StyledSlideTile(
|
||||||
{required this.disabled,
|
{required this.disabled,
|
||||||
required this.selected,
|
required this.selected,
|
||||||
required this.tileScale,
|
required this.tileScale,
|
||||||
|
@ -38,8 +38,8 @@ class SliderTile extends StatelessWidget {
|
||||||
final bool disabled;
|
final bool disabled;
|
||||||
final bool selected;
|
final bool selected;
|
||||||
final ScaleKind tileScale;
|
final ScaleKind tileScale;
|
||||||
final List<SliderTileAction> endActions;
|
final List<SlideTileAction> endActions;
|
||||||
final List<SliderTileAction> startActions;
|
final List<SlideTileAction> startActions;
|
||||||
final GestureTapCallback? onTap;
|
final GestureTapCallback? onTap;
|
||||||
final GestureTapCallback? onDoubleTap;
|
final GestureTapCallback? onDoubleTap;
|
||||||
final Widget? leading;
|
final Widget? leading;
|
||||||
|
@ -54,8 +54,8 @@ class SliderTile extends StatelessWidget {
|
||||||
..add(DiagnosticsProperty<bool>('disabled', disabled))
|
..add(DiagnosticsProperty<bool>('disabled', disabled))
|
||||||
..add(DiagnosticsProperty<bool>('selected', selected))
|
..add(DiagnosticsProperty<bool>('selected', selected))
|
||||||
..add(DiagnosticsProperty<ScaleKind>('tileScale', tileScale))
|
..add(DiagnosticsProperty<ScaleKind>('tileScale', tileScale))
|
||||||
..add(IterableProperty<SliderTileAction>('endActions', endActions))
|
..add(IterableProperty<SlideTileAction>('endActions', endActions))
|
||||||
..add(IterableProperty<SliderTileAction>('startActions', startActions))
|
..add(IterableProperty<SlideTileAction>('startActions', startActions))
|
||||||
..add(ObjectFlagProperty<GestureTapCallback?>.has('onTap', onTap))
|
..add(ObjectFlagProperty<GestureTapCallback?>.has('onTap', onTap))
|
||||||
..add(DiagnosticsProperty<Widget?>('leading', leading))
|
..add(DiagnosticsProperty<Widget?>('leading', leading))
|
||||||
..add(StringProperty('title', title))
|
..add(StringProperty('title', title))
|
||||||
|
@ -66,7 +66,6 @@ class SliderTile extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
// ignore: prefer_expression_function_bodies
|
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final scaleTheme = theme.extension<ScaleTheme>()!;
|
final scaleTheme = theme.extension<ScaleTheme>()!;
|
||||||
|
@ -96,7 +95,8 @@ class SliderTile extends StatelessWidget {
|
||||||
foregroundColor: scaleActionTheme.textColor,
|
foregroundColor: scaleActionTheme.textColor,
|
||||||
icon: subtitle.isEmpty ? a.icon : null,
|
icon: subtitle.isEmpty ? a.icon : null,
|
||||||
label: a.label,
|
label: a.label,
|
||||||
padding: const EdgeInsets.all(2));
|
padding: const EdgeInsets.all(2).scaled(context),
|
||||||
|
);
|
||||||
}).toList()),
|
}).toList()),
|
||||||
startActionPane: startActions.isEmpty
|
startActionPane: startActions.isEmpty
|
||||||
? null
|
? null
|
||||||
|
@ -114,12 +114,13 @@ class SliderTile extends StatelessWidget {
|
||||||
foregroundColor: scaleActionTheme.textColor,
|
foregroundColor: scaleActionTheme.textColor,
|
||||||
icon: subtitle.isEmpty ? a.icon : null,
|
icon: subtitle.isEmpty ? a.icon : null,
|
||||||
label: a.label,
|
label: a.label,
|
||||||
padding: const EdgeInsets.all(2));
|
padding: const EdgeInsets.all(2).scaled(context),
|
||||||
|
);
|
||||||
}).toList()),
|
}).toList()),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: scaleTheme.config.useVisualIndicators
|
padding: scaleTheme.config.useVisualIndicators
|
||||||
? EdgeInsets.zero
|
? EdgeInsets.zero
|
||||||
: const EdgeInsets.fromLTRB(0, 2, 0, 2),
|
: const EdgeInsets.fromLTRB(0, 2, 0, 2).scaled(context),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onDoubleTap: onDoubleTap,
|
onDoubleTap: onDoubleTap,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
|
@ -131,7 +132,8 @@ class SliderTile extends StatelessWidget {
|
||||||
softWrap: false,
|
softWrap: false,
|
||||||
),
|
),
|
||||||
subtitle: subtitle.isNotEmpty ? Text(subtitle) : null,
|
subtitle: subtitle.isNotEmpty ? Text(subtitle) : null,
|
||||||
minTileHeight: 52,
|
contentPadding: const EdgeInsets.fromLTRB(8, 4, 8, 4)
|
||||||
|
.scaled(context),
|
||||||
iconColor: scaleTileTheme.textColor,
|
iconColor: scaleTileTheme.textColor,
|
||||||
textColor: scaleTileTheme.textColor,
|
textColor: scaleTileTheme.textColor,
|
||||||
leading:
|
leading:
|
79
lib/theme/views/styled_widgets/styled_slider.dart
Normal file
79
lib/theme/views/styled_widgets/styled_slider.dart
Normal file
|
@ -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<void> 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<ScaleTheme>()!;
|
||||||
|
|
||||||
|
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<void> Function(double)? _onChanged;
|
||||||
|
final double _value;
|
||||||
|
final Widget? _leftWidget;
|
||||||
|
final Widget? _rightWidget;
|
||||||
|
final double _min;
|
||||||
|
final double _max;
|
||||||
|
final int? _divisions;
|
||||||
|
}
|
8
lib/theme/views/styled_widgets/styled_widgets.dart
Normal file
8
lib/theme/views/styled_widgets/styled_widgets.dart
Normal file
|
@ -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';
|
|
@ -1,16 +1,10 @@
|
||||||
export 'avatar_widget.dart';
|
|
||||||
export 'brightness_preferences.dart';
|
|
||||||
export 'color_preferences.dart';
|
|
||||||
export 'enter_password.dart';
|
export 'enter_password.dart';
|
||||||
export 'enter_pin.dart';
|
export 'enter_pin.dart';
|
||||||
export 'option_box.dart';
|
|
||||||
export 'pop_control.dart';
|
export 'pop_control.dart';
|
||||||
|
export 'preferences/preferences.dart';
|
||||||
export 'recovery_key_widget.dart';
|
export 'recovery_key_widget.dart';
|
||||||
export 'responsive.dart';
|
export 'responsive.dart';
|
||||||
export 'scanner_error_widget.dart';
|
export 'scanner_error_widget.dart';
|
||||||
export 'slider_tile.dart';
|
export 'styled_widgets/styled_button_box.dart';
|
||||||
export 'styled_alert.dart';
|
export 'styled_widgets/styled_widgets.dart';
|
||||||
export 'styled_dialog.dart';
|
|
||||||
export 'styled_scaffold.dart';
|
|
||||||
export 'wallpaper_preferences.dart';
|
|
||||||
export 'widget_helpers.dart';
|
export 'widget_helpers.dart';
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -222,13 +222,16 @@ class _DeveloperPageState extends State<DeveloperPage> {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: scale.primaryScale.border,
|
backgroundColor: scale.primaryScale.border,
|
||||||
appBar: DefaultAppBar(
|
appBar: DefaultAppBar(
|
||||||
|
context: context,
|
||||||
title: Text(translate('developer.title')),
|
title: Text(translate('developer.title')),
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
|
iconSize: 24.scaled(context),
|
||||||
icon: Icon(Icons.arrow_back, color: scale.primaryScale.borderText),
|
icon: Icon(Icons.arrow_back, color: scale.primaryScale.borderText),
|
||||||
onPressed: () => GoRouterHelper(context).pop(),
|
onPressed: () => GoRouterHelper(context).pop(),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
iconSize: 24.scaled(context),
|
||||||
icon: const Icon(Icons.copy),
|
icon: const Icon(Icons.copy),
|
||||||
color: scale.primaryScale.borderText,
|
color: scale.primaryScale.borderText,
|
||||||
disabledColor: scale.primaryScale.borderText.withAlpha(0x3F),
|
disabledColor: scale.primaryScale.borderText.withAlpha(0x3F),
|
||||||
|
@ -238,6 +241,7 @@ class _DeveloperPageState extends State<DeveloperPage> {
|
||||||
await copySelection(context);
|
await copySelection(context);
|
||||||
}),
|
}),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
iconSize: 24.scaled(context),
|
||||||
icon: const Icon(Icons.copy_all),
|
icon: const Icon(Icons.copy_all),
|
||||||
color: scale.primaryScale.borderText,
|
color: scale.primaryScale.borderText,
|
||||||
disabledColor: scale.primaryScale.borderText.withAlpha(0x3F),
|
disabledColor: scale.primaryScale.borderText.withAlpha(0x3F),
|
||||||
|
@ -245,6 +249,7 @@ class _DeveloperPageState extends State<DeveloperPage> {
|
||||||
await copyAll(context);
|
await copyAll(context);
|
||||||
}),
|
}),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
iconSize: 24.scaled(context),
|
||||||
icon: const Icon(Icons.clear_all),
|
icon: const Icon(Icons.clear_all),
|
||||||
color: scale.primaryScale.borderText,
|
color: scale.primaryScale.borderText,
|
||||||
disabledColor: scale.primaryScale.borderText.withAlpha(0x3F),
|
disabledColor: scale.primaryScale.borderText.withAlpha(0x3F),
|
||||||
|
@ -259,7 +264,7 @@ class _DeveloperPageState extends State<DeveloperPage> {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
SizedBox.fromSize(
|
SizedBox.fromSize(
|
||||||
size: const Size(140, 48),
|
size: Size(140.scaled(context), 48),
|
||||||
child: CustomDropdown<LogLevelDropdownItem>(
|
child: CustomDropdown<LogLevelDropdownItem>(
|
||||||
items: _logLevelDropdownItems,
|
items: _logLevelDropdownItems,
|
||||||
initialItem: _logLevelDropdownItems
|
initialItem: _logLevelDropdownItems
|
||||||
|
@ -300,6 +305,7 @@ class _DeveloperPageState extends State<DeveloperPage> {
|
||||||
Image.asset('assets/images/ellet.png'),
|
Image.asset('assets/images/ellet.png'),
|
||||||
TerminalView(globalDebugTerminal,
|
TerminalView(globalDebugTerminal,
|
||||||
textStyle: kDefaultTerminalStyle,
|
textStyle: kDefaultTerminalStyle,
|
||||||
|
textScaler: TextScaler.noScaling,
|
||||||
controller: _terminalController,
|
controller: _terminalController,
|
||||||
keyboardType: TextInputType.none,
|
keyboardType: TextInputType.none,
|
||||||
backgroundOpacity: _showEllet ? 0.75 : 1.0,
|
backgroundOpacity: _showEllet ? 0.75 : 1.0,
|
||||||
|
|
|
@ -19,7 +19,7 @@ class SignalStrengthMeterWidget extends StatelessWidget {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final scale = theme.extension<ScaleScheme>()!;
|
final scale = theme.extension<ScaleScheme>()!;
|
||||||
|
|
||||||
const iconSize = 16.0;
|
final iconSize = 16.0.scaled(context);
|
||||||
|
|
||||||
return BlocBuilder<ConnectionStateCubit,
|
return BlocBuilder<ConnectionStateCubit,
|
||||||
AsyncValue<ProcessorConnectionState>>(builder: (context, state) {
|
AsyncValue<ProcessorConnectionState>>(builder: (context, state) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue