diff --git a/lib/src/utils/misc.dart b/lib/src/utils/misc.dart index ab0f0c79..6109b7b0 100644 --- a/lib/src/utils/misc.dart +++ b/lib/src/utils/misc.dart @@ -14,11 +14,17 @@ import 'package:twonly/src/model/error_code.dart'; import 'package:twonly/src/providers/settings.provider.dart'; import 'package:twonly/src/services/backup.service.dart'; import 'package:twonly/src/utils/log.dart'; -import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/themes/colors.dart'; + +export 'package:twonly/src/visual/themes/colors.dart' show AppColor; extension ShortCutsExtension on BuildContext { AppLocalizations get lang => AppLocalizations.of(this)!; ColorScheme get color => Theme.of(this).colorScheme; + Color appColor(AppColor color) { + return Theme.of(this).extension()![color]; + } + Future navPush(Widget route) async { return Navigator.push( this, diff --git a/lib/src/visual/components/app_outdated.comp.dart b/lib/src/visual/components/app_outdated.comp.dart index 52895192..4c7b71de 100644 --- a/lib/src/visual/components/app_outdated.comp.dart +++ b/lib/src/visual/components/app_outdated.comp.dart @@ -60,7 +60,7 @@ class _AppOutdatedCompState extends State { child: Container( padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8), decoration: BoxDecoration( - color: Colors.red.withAlpha(100), + color: context.color.errorContainer, borderRadius: BorderRadius.circular(10), ), child: Column( @@ -71,7 +71,7 @@ class _AppOutdatedCompState extends State { textAlign: TextAlign.center, softWrap: true, style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Colors.white, + color: context.color.onErrorContainer, fontSize: 16, ), ), @@ -90,7 +90,7 @@ class _AppOutdatedCompState extends State { child: Container( padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8), decoration: BoxDecoration( - color: Colors.red, + color: context.color.error, borderRadius: BorderRadius.circular(10), ), child: Column( @@ -101,7 +101,7 @@ class _AppOutdatedCompState extends State { textAlign: TextAlign.center, softWrap: true, style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Colors.white, + color: context.color.onError, fontSize: 16, ), ), @@ -123,7 +123,7 @@ class _AppOutdatedCompState extends State { child: Text( context.lang.appOutdatedBtn, style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Colors.white, + color: context.color.onSurface, fontSize: 16, ), ), diff --git a/lib/src/visual/components/connection_status.comp.dart b/lib/src/visual/components/connection_status.comp.dart index 19ff05f2..d7cf612c 100644 --- a/lib/src/visual/components/connection_status.comp.dart +++ b/lib/src/visual/components/connection_status.comp.dart @@ -17,9 +17,9 @@ class ConnectionStatusComp extends StatelessWidget { return Stack( children: [ if (!isConnected) - const Positioned.fill( + Positioned.fill( child: SpinKitRipple( - color: Colors.red, + color: context.color.error, ), ), Container( @@ -29,7 +29,7 @@ class ConnectionStatusComp extends StatelessWidget { border: Border.all( color: isConnected ? context.color.primary.withAlpha(100) - : Colors.red, + : context.color.error, ), ), padding: const EdgeInsets.all(0.5), diff --git a/lib/src/visual/components/contact_request_badge.comp.dart b/lib/src/visual/components/contact_request_badge.comp.dart index a048c547..4a54c38d 100644 --- a/lib/src/visual/components/contact_request_badge.comp.dart +++ b/lib/src/visual/components/contact_request_badge.comp.dart @@ -34,13 +34,11 @@ class ContactRequestBadgeComp extends StatelessWidget { ), Center( child: NotificationBadgeComp( - backgroundColor: isDarkMode(context) - ? Colors.white - : Colors.black, - textColor: isDarkMode(context) ? Colors.black : Colors.white, + backgroundColor: context.color.inverseSurface, + textColor: context.color.onInverseSurface, count: count.toString(), child: IconButton( - color: Colors.black, + color: context.color.onPrimary, icon: const FaIcon( FontAwesomeIcons.userPlus, size: 18, diff --git a/lib/src/visual/components/draggable_scrollbar.comp.dart b/lib/src/visual/components/draggable_scrollbar.comp.dart index 4f481577..f60e5b1e 100644 --- a/lib/src/visual/components/draggable_scrollbar.comp.dart +++ b/lib/src/visual/components/draggable_scrollbar.comp.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:twonly/src/utils/misc.dart'; typedef TextLabelBuilder = Widget Function(String label); @@ -99,14 +100,9 @@ class _DraggableScrollbarState extends State child: ValueListenableBuilder( valueListenable: _thumbOffsetNotifier, builder: (context, thumbOffset, child) { - final isDark = - Theme.of(context).brightness == Brightness.dark; - final handleColor = isDark - ? Colors.grey.shade900 - : Colors.white; - final iconColor = isDark - ? Colors.white70 - : Colors.black54; + final colors = context.color; + final handleColor = colors.surfaceContainerLowest; + final iconColor = colors.onSurfaceVariant; final label = widget.labelBuilder?.call( _viewOffsetNotifier.value, @@ -145,17 +141,14 @@ class _DraggableScrollbarState extends State horizontal: 12, ), decoration: BoxDecoration( - color: - (isDark - ? Colors.grey.shade900 - : Colors.grey.shade200) - .withValues(alpha: 0.95), + color: colors.surfaceContainerHigh + .withValues(alpha: 0.95), borderRadius: BorderRadius.circular( 8, ), boxShadow: [ BoxShadow( - color: Colors.black.withValues( + color: colors.shadow.withValues( alpha: 0.2, ), blurRadius: 8, @@ -166,9 +159,7 @@ class _DraggableScrollbarState extends State child: Text( label, style: TextStyle( - color: isDark - ? Colors.white - : Colors.black, + color: colors.onSurface, fontSize: 14, fontWeight: FontWeight.bold, ), @@ -182,9 +173,7 @@ class _DraggableScrollbarState extends State decoration: BoxDecoration( color: handleColor, border: Border.all( - color: isDark - ? Colors.white10 - : Colors.black12, + color: colors.outlineVariant, width: 0.5, ), borderRadius: const BorderRadius.only( diff --git a/lib/src/visual/components/emoji_picker.bottom.dart b/lib/src/visual/components/emoji_picker.bottom.dart index 86ac02c0..b5e05328 100755 --- a/lib/src/visual/components/emoji_picker.bottom.dart +++ b/lib/src/visual/components/emoji_picker.bottom.dart @@ -33,7 +33,7 @@ class EmojiPickerBottom extends StatelessWidget { margin: const EdgeInsets.all(10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(32), - color: Colors.grey, + color: context.color.outline, ), height: 3, width: 60, @@ -66,7 +66,7 @@ class EmojiPickerBottom extends StatelessWidget { ), searchViewConfig: SearchViewConfig( backgroundColor: context.color.surfaceContainer, - buttonIconColor: Colors.white, + buttonIconColor: context.color.onSurface, ), categoryViewConfig: CategoryViewConfig( backgroundColor: context.color.surfaceContainer, diff --git a/lib/src/visual/components/notification_badge.comp.dart b/lib/src/visual/components/notification_badge.comp.dart index 2e4b7617..312c8dfc 100644 --- a/lib/src/visual/components/notification_badge.comp.dart +++ b/lib/src/visual/components/notification_badge.comp.dart @@ -1,16 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:twonly/src/utils/misc.dart'; class NotificationBadgeComp extends StatelessWidget { const NotificationBadgeComp({ required this.count, required this.child, - this.backgroundColor = Colors.red, - this.textColor = Colors.white, + this.backgroundColor, + this.textColor, super.key, }); final String count; - final Color backgroundColor; - final Color textColor; + final Color? backgroundColor; + final Color? textColor; final Widget child; @override @@ -27,14 +28,14 @@ class NotificationBadgeComp extends StatelessWidget { height: 18, width: 18, child: CircleAvatar( - backgroundColor: backgroundColor, + backgroundColor: backgroundColor ?? context.color.error, child: Center( child: Transform.rotate( angle: infinity ? 90 * (3.141592653589793 / 180) : 0, child: Text( infinity ? '8' : count, style: TextStyle( - color: textColor, + color: textColor ?? context.color.onError, fontSize: 10, ), ), diff --git a/lib/src/visual/components/profile_qr_code.comp.dart b/lib/src/visual/components/profile_qr_code.comp.dart index e8dc3af6..eb7c84c0 100644 --- a/lib/src/visual/components/profile_qr_code.comp.dart +++ b/lib/src/visual/components/profile_qr_code.comp.dart @@ -143,13 +143,13 @@ class _ProfileQrCodeCompState extends State { ? Container( key: const ValueKey('qr_code_container'), decoration: BoxDecoration( - color: context.color.primary, + color: context.appColor(AppColor.qrBackground), borderRadius: BorderRadius.circular(12), - boxShadow: const [ + boxShadow: [ BoxShadow( - color: Colors.black26, + color: context.color.shadow.withValues(alpha: 0.2), blurRadius: 3, - offset: Offset(0, 2), + offset: const Offset(0, 2), ), ], ), @@ -159,11 +159,11 @@ class _ProfileQrCodeCompState extends State { errorCorrectLevel: QrErrorCorrectLevel.M, ), eyeStyle: QrEyeStyle( - color: isDarkMode(context) ? Colors.black : Colors.white, + color: context.appColor(AppColor.qrForeground), borderRadius: 2, ), dataModuleStyle: QrDataModuleStyle( - color: isDarkMode(context) ? Colors.black : Colors.white, + color: context.appColor(AppColor.qrForeground), borderRadius: 2, ), gapless: false, @@ -173,7 +173,7 @@ class _ProfileQrCodeCompState extends State { embeddedImageStyle: QrEmbeddedImageStyle( size: const Size(60, 66), embeddedImageShape: EmbeddedImageShape.square, - shapeColor: context.color.primary, + shapeColor: context.appColor(AppColor.qrBackground), safeArea: true, ), size: widget.size, diff --git a/lib/src/visual/components/select_chat_deletion_time.comp.dart b/lib/src/visual/components/select_chat_deletion_time.comp.dart index bb13a998..6e14b46e 100644 --- a/lib/src/visual/components/select_chat_deletion_time.comp.dart +++ b/lib/src/visual/components/select_chat_deletion_time.comp.dart @@ -68,7 +68,7 @@ class _SelectChatDeletionTimeListTitleState bottom: MediaQuery.of(context).viewInsets.bottom, ), // Provide a background color for the popup. - color: CupertinoColors.systemBackground.resolveFrom(context), + color: context.color.surface, // Use a SafeArea widget to avoid system overlaps. child: SafeArea(top: false, child: child), ), diff --git a/lib/src/visual/components/selectable_thumbnail.comp.dart b/lib/src/visual/components/selectable_thumbnail.comp.dart index 743c0ee1..fd76d12f 100644 --- a/lib/src/visual/components/selectable_thumbnail.comp.dart +++ b/lib/src/visual/components/selectable_thumbnail.comp.dart @@ -20,11 +20,11 @@ class SelectableThumbnailComp extends StatelessWidget { curve: Curves.easeInOut, decoration: BoxDecoration( color: isSelected ? context.color.primary : Colors.transparent, - boxShadow: const [ + boxShadow: [ BoxShadow( - color: Colors.black12, + color: context.color.shadow.withValues(alpha: 0.12), blurRadius: 4, - offset: Offset(0, 2), + offset: const Offset(0, 2), ), ], ), @@ -49,20 +49,20 @@ class SelectableThumbnailComp extends StatelessWidget { child: Container( padding: const EdgeInsets.all(2), decoration: BoxDecoration( - color: isSelected ? context.color.primary : Colors.black38, + color: isSelected + ? context.color.primary + : context.color.scrim.withValues(alpha: 0.38), shape: BoxShape.circle, border: Border.all( - color: Theme.of(context).brightness == Brightness.dark - ? Colors.white - : Colors.black, + color: context.color.onSurface, width: 1.5, ), ), child: isSelected - ? const Icon( + ? Icon( Icons.check, size: 14, - color: Colors.white, + color: context.color.onPrimary, ) : const SizedBox(width: 14, height: 14), ), diff --git a/lib/src/visual/components/snackbar.dart b/lib/src/visual/components/snackbar.dart index 37f015b3..49b8a915 100644 --- a/lib/src/visual/components/snackbar.dart +++ b/lib/src/visual/components/snackbar.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:twonly/src/utils/misc.dart'; enum SnackbarLevel { info, @@ -14,23 +15,28 @@ void showSnackbar( String message, { SnackbarLevel level = SnackbarLevel.error, }) { - Color backgroundColor; - IconData iconData; - - switch (level) { - case SnackbarLevel.info: - backgroundColor = Colors.blue.shade700; - iconData = Icons.info_outline; - case SnackbarLevel.success: - backgroundColor = Colors.green.shade700; - iconData = Icons.check_circle_outline; - case SnackbarLevel.warning: - backgroundColor = Colors.orange.shade800; - iconData = Icons.warning_amber_rounded; - case SnackbarLevel.error: - backgroundColor = Colors.red.shade700; - iconData = Icons.error_outline; - } + final (backgroundColor, foregroundColor, iconData) = switch (level) { + SnackbarLevel.info => ( + context.appColor(AppColor.info), + context.appColor(AppColor.onInfo), + Icons.info_outline, + ), + SnackbarLevel.success => ( + context.appColor(AppColor.success), + context.appColor(AppColor.onSuccess), + Icons.check_circle_outline, + ), + SnackbarLevel.warning => ( + context.appColor(AppColor.warning), + context.appColor(AppColor.onWarning), + Icons.warning_amber_rounded, + ), + SnackbarLevel.error => ( + context.color.error, + context.color.onError, + Icons.error_outline, + ), + }; AnimationController? localAnimationController; @@ -44,7 +50,8 @@ void showSnackbar( child: _SnackbarWidget( message: message, backgroundColor: backgroundColor, - icon: Icon(iconData, color: Colors.white, size: 28), + foregroundColor: foregroundColor, + icon: Icon(iconData, color: foregroundColor, size: 28), onCloseClick: () { localAnimationController?.reverse(); }, @@ -101,11 +108,13 @@ class _SnackbarWidget extends StatelessWidget { const _SnackbarWidget({ required this.message, required this.backgroundColor, + required this.foregroundColor, required this.icon, required this.onCloseClick, }); final String message; final Color backgroundColor; + final Color foregroundColor; final Icon icon; final VoidCallback onCloseClick; @@ -118,9 +127,9 @@ class _SnackbarWidget extends StatelessWidget { decoration: BoxDecoration( color: backgroundColor, borderRadius: const BorderRadius.all(Radius.circular(12)), - boxShadow: const [ + boxShadow: [ BoxShadow( - color: Colors.black26, + color: Theme.of(context).colorScheme.shadow.withValues(alpha: 0.2), spreadRadius: 1, blurRadius: 30, ), @@ -138,10 +147,10 @@ class _SnackbarWidget extends StatelessWidget { child: Text( message, style: theme.textTheme.bodyMedium?.merge( - const TextStyle( + TextStyle( fontWeight: FontWeight.w600, fontSize: 16, - color: Colors.white, + color: foregroundColor, ), ), textAlign: TextAlign.start, @@ -151,9 +160,13 @@ class _SnackbarWidget extends StatelessWidget { GestureDetector( onTap: onCloseClick, behavior: HitTestBehavior.opaque, - child: const Padding( - padding: EdgeInsets.all(16), - child: Icon(Icons.close, color: Colors.white70, size: 20), + child: Padding( + padding: const EdgeInsets.all(16), + child: Icon( + Icons.close, + color: foregroundColor.withValues(alpha: 0.75), + size: 20, + ), ), ), ], diff --git a/lib/src/visual/components/verification_badge.comp.dart b/lib/src/visual/components/verification_badge.comp.dart index 651961a7..3cfb6e17 100644 --- a/lib/src/visual/components/verification_badge.comp.dart +++ b/lib/src/visual/components/verification_badge.comp.dart @@ -7,7 +7,7 @@ import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/daos/key_verification.dao.dart'; import 'package:twonly/src/database/tables/contacts.table.dart'; import 'package:twonly/src/database/twonly.db.dart'; -import 'package:twonly/src/visual/components/verification_badge_info.comp.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/elements/svg_icon.element.dart'; class VerificationBadgeComp extends StatefulWidget { @@ -198,7 +198,7 @@ class _VerificationBadgeCompState extends State { ? SvgIcons.verifiedNumeric(_verifiedByTransferredTrustCount) : SvgIcons.verifiedRed, color: (_verifiedByTransferredTrustCount > 0 && !_isVerified) - ? colorVerificationBadgeYellow + ? context.appColor(AppColor.verificationPending) : null, size: widget.size, ), diff --git a/lib/src/visual/components/verification_badge_info.comp.dart b/lib/src/visual/components/verification_badge_info.comp.dart index 5dfa23b5..5a34f755 100644 --- a/lib/src/visual/components/verification_badge_info.comp.dart +++ b/lib/src/visual/components/verification_badge_info.comp.dart @@ -10,8 +10,6 @@ import 'package:twonly/src/visual/components/profile_qr_code.comp.dart'; import 'package:twonly/src/visual/elements/my_button.element.dart'; import 'package:twonly/src/visual/elements/svg_icon.element.dart'; -const colorVerificationBadgeYellow = Color.fromARGB(255, 0, 182, 238); - class VerificationBadgeInfo extends StatelessWidget { const VerificationBadgeInfo({ this.displayButtons = false, @@ -121,19 +119,19 @@ class VerificationBadgeInfo extends StatelessWidget { ), _buildItem( context, - icon: const SvgIcon( + icon: SvgIcon( assetPath: SvgIcons.verifiedGreen, size: 40, - color: colorVerificationBadgeYellow, + color: context.appColor(AppColor.verificationPending), ), description: context.lang.verificationBadgeYellowDesc, - boldTextColor: colorVerificationBadgeYellow, + boldTextColor: context.appColor(AppColor.verificationPending), ), _buildItem( context, icon: const SvgIcon(assetPath: SvgIcons.verifiedRed, size: 40), description: context.lang.verificationBadgeRedDesc, - boldTextColor: const Color(0xffff0000), + boldTextColor: context.color.error, ), ], ); diff --git a/lib/src/visual/components/verification_success_animation.comp.dart b/lib/src/visual/components/verification_success_animation.comp.dart index a7c0c421..792bcc3c 100644 --- a/lib/src/visual/components/verification_success_animation.comp.dart +++ b/lib/src/visual/components/verification_success_animation.comp.dart @@ -3,7 +3,7 @@ import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:twonly/src/visual/themes/light.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:vector_graphics/vector_graphics.dart'; /// Animated chain-link logo for the "verification success" moment. @@ -85,9 +85,6 @@ class VerificationSuccessAnimationState static const _svg2 = ''; - static const _grey = Color(0xFF8E9AAF); - static const Color _green = defaultPrimaryColor; - @override void initState() { super.initState(); @@ -196,6 +193,7 @@ class VerificationSuccessAnimationState animation: Listenable.merge([_ctrl, _idleCtrl]), builder: (context, _) { final scale = widget.size / 640; + final successColor = context.color.primary; final t = _flyIn.value; // 0 → 1 (fly-in progress) final sep = 1.0 - t; // 1 → 0 (separation) @@ -209,7 +207,11 @@ class VerificationSuccessAnimationState final rotation = sep * 0.7; // Interpolated colour. - final color = Color.lerp(_grey, _green, _colorFade.value)!; + final color = Color.lerp( + context.appColor(AppColor.verificationIdle), + successColor, + _colorFade.value, + )!; // Pulse: 1 → 1.08 → 1 (smooth sine bump). final pulseScale = 1.0 + math.sin(_pulse.value * math.pi) * 0.08; @@ -232,7 +234,9 @@ class VerificationSuccessAnimationState shape: BoxShape.circle, boxShadow: [ BoxShadow( - color: _green.withValues(alpha: 0.3 * _glow.value), + color: successColor.withValues( + alpha: 0.3 * _glow.value, + ), blurRadius: widget.size * 0.45 * _glow.value, spreadRadius: widget.size * 0.08 * _glow.value, ), @@ -247,7 +251,7 @@ class VerificationSuccessAnimationState child: CustomPaint( painter: _RipplePainter( progress: _ripple.value, - color: _green, + color: successColor, maxRadius: widget.size * 0.45, ), ), @@ -323,7 +327,7 @@ class VerificationSuccessAnimationState child: CustomPaint( painter: _SparklePainter( progress: _sparkle.value, - color: _green, + color: successColor, maxRadius: widget.size * 0.4, ), ), diff --git a/lib/src/visual/decorations/input_text.decoration.dart b/lib/src/visual/decorations/input_text.decoration.dart index 9a42f916..0ae7dfc5 100644 --- a/lib/src/visual/decorations/input_text.decoration.dart +++ b/lib/src/visual/decorations/input_text.decoration.dart @@ -20,7 +20,10 @@ InputDecoration inputTextMessageDeco(BuildContext context, String hintText) { ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(20), - borderSide: const BorderSide(color: Colors.grey, width: 2), + borderSide: BorderSide( + color: Theme.of(context).colorScheme.outline, + width: 2, + ), ), ); } diff --git a/lib/src/visual/elements/better_text.element.dart b/lib/src/visual/elements/better_text.element.dart index a5640806..57d821c8 100644 --- a/lib/src/visual/elements/better_text.element.dart +++ b/lib/src/visual/elements/better_text.element.dart @@ -90,9 +90,9 @@ class _BetterTextState extends State { spans.add( TextSpan( text: url, - style: const TextStyle( + style: TextStyle( decoration: TextDecoration.underline, - decorationColor: Colors.white, + decorationColor: widget.textColor, ), recognizer: recognizer, ), diff --git a/lib/src/visual/elements/contact_chip.element.dart b/lib/src/visual/elements/contact_chip.element.dart index 650199ed..d157440b 100644 --- a/lib/src/visual/elements/contact_chip.element.dart +++ b/lib/src/visual/elements/contact_chip.element.dart @@ -68,9 +68,9 @@ class ContactChip extends StatelessWidget { ], if (onTap != null && contact != null) ...[ const SizedBox(width: 15), - const FaIcon( + FaIcon( FontAwesomeIcons.xmark, - color: Colors.grey, + color: context.color.onSurfaceVariant, size: 12, ), ], diff --git a/lib/src/visual/elements/my_button.element.dart b/lib/src/visual/elements/my_button.element.dart index 3efdce34..6450a162 100644 --- a/lib/src/visual/elements/my_button.element.dart +++ b/lib/src/visual/elements/my_button.element.dart @@ -37,20 +37,16 @@ class _MyButtonState extends State { @override Widget build(BuildContext context) { final isEnabled = widget.onPressed != null || widget.onLongPress != null; - final isDark = isDarkMode(context); - final disabledBgColor = isDark - ? const Color(0xFF353535) - : const Color(0xFFE0E0E0); - final disabledFgColor = isDark - ? const Color(0xFF757575) - : const Color(0xFF9E9E9E); + final colors = context.color; + final disabledBgColor = colors.surfaceContainerHighest; + final disabledFgColor = colors.onSurface.withValues(alpha: 0.38); late final ButtonStyle buttonStyle; switch (widget.variant) { case MyButtonVariant.primary: buttonStyle = FilledButton.styleFrom( backgroundColor: context.color.primary, - foregroundColor: Colors.black87, + foregroundColor: colors.onPrimary, disabledBackgroundColor: disabledBgColor, disabledForegroundColor: disabledFgColor, minimumSize: const Size.fromHeight(60), @@ -65,8 +61,8 @@ class _MyButtonState extends State { ); case MyButtonVariant.secondary: buttonStyle = FilledButton.styleFrom( - backgroundColor: isDark ? Colors.grey[800] : Colors.grey[200], - foregroundColor: isDark ? Colors.white : Colors.black87, + backgroundColor: colors.surfaceContainerHigh, + foregroundColor: colors.onSurface, disabledBackgroundColor: disabledBgColor, disabledForegroundColor: disabledFgColor, minimumSize: const Size.fromHeight(60), @@ -82,9 +78,7 @@ class _MyButtonState extends State { case MyButtonVariant.text: buttonStyle = TextButton.styleFrom( minimumSize: const Size(0, 50), - foregroundColor: isDark - ? Colors.white.withValues(alpha: 0.7) - : Colors.black.withValues(alpha: 0.7), + foregroundColor: colors.onSurfaceVariant, disabledForegroundColor: disabledFgColor, textStyle: const TextStyle( fontSize: 15, @@ -97,7 +91,7 @@ class _MyButtonState extends State { case MyButtonVariant.primaryMiddle: buttonStyle = FilledButton.styleFrom( backgroundColor: context.color.primary, - foregroundColor: Colors.black87, + foregroundColor: colors.onPrimary, disabledBackgroundColor: disabledBgColor, disabledForegroundColor: disabledFgColor, minimumSize: const Size(0, 48), @@ -116,7 +110,7 @@ class _MyButtonState extends State { case MyButtonVariant.primaryDense: buttonStyle = FilledButton.styleFrom( backgroundColor: context.color.primary, - foregroundColor: Colors.black87, + foregroundColor: colors.onPrimary, disabledBackgroundColor: disabledBgColor, disabledForegroundColor: disabledFgColor, minimumSize: const Size(0, 40), @@ -134,8 +128,8 @@ class _MyButtonState extends State { ); case MyButtonVariant.secondaryDense: buttonStyle = FilledButton.styleFrom( - backgroundColor: isDark ? Colors.grey[800] : Colors.grey[200], - foregroundColor: isDark ? Colors.white : Colors.black87, + backgroundColor: colors.surfaceContainerHigh, + foregroundColor: colors.onSurface, disabledBackgroundColor: disabledBgColor, disabledForegroundColor: disabledFgColor, minimumSize: const Size(0, 40), @@ -153,8 +147,8 @@ class _MyButtonState extends State { ); case MyButtonVariant.secondaryMiddle: buttonStyle = FilledButton.styleFrom( - backgroundColor: isDark ? Colors.grey[800] : Colors.grey[200], - foregroundColor: isDark ? Colors.white : Colors.black87, + backgroundColor: colors.surfaceContainerHigh, + foregroundColor: colors.onSurface, disabledBackgroundColor: disabledBgColor, disabledForegroundColor: disabledFgColor, minimumSize: const Size(0, 48), @@ -172,8 +166,8 @@ class _MyButtonState extends State { ); case MyButtonVariant.secondaryTiny: buttonStyle = FilledButton.styleFrom( - backgroundColor: isDark ? Colors.grey[800] : Colors.grey[200], - foregroundColor: isDark ? Colors.white : Colors.black87, + backgroundColor: colors.surfaceContainerHigh, + foregroundColor: colors.onSurface, disabledBackgroundColor: disabledBgColor, disabledForegroundColor: disabledFgColor, minimumSize: const Size(40, 23), diff --git a/lib/src/visual/elements/my_chip.element.dart b/lib/src/visual/elements/my_chip.element.dart index 8d6fd14f..c4119cbf 100644 --- a/lib/src/visual/elements/my_chip.element.dart +++ b/lib/src/visual/elements/my_chip.element.dart @@ -22,17 +22,9 @@ class MyChip extends StatelessWidget { @override Widget build(BuildContext context) { - final isDark = isDarkMode(context); - final background = selected - ? context.color.primary - : isDark - ? Colors.grey[800]! - : Colors.grey[200]!; - final foreground = selected - ? Colors.black87 - : isDark - ? Colors.white - : Colors.black87; + final colors = context.color; + final background = selected ? colors.primary : colors.surfaceContainerHigh; + final foreground = selected ? colors.onPrimary : colors.onSurface; Widget chip = AnimatedContainer( duration: const Duration(milliseconds: 150), height: 36, diff --git a/lib/src/visual/elements/my_icon_button.element.dart b/lib/src/visual/elements/my_icon_button.element.dart index 43e761b6..7a27f071 100644 --- a/lib/src/visual/elements/my_icon_button.element.dart +++ b/lib/src/visual/elements/my_icon_button.element.dart @@ -29,23 +29,19 @@ class _MyIconButtonState extends State { @override Widget build(BuildContext context) { final isEnabled = widget.onPressed != null || widget.onLongPress != null; - final isDark = isDarkMode(context); - final disabledBgColor = isDark - ? const Color(0xFF353535) - : const Color(0xFFE0E0E0); - final disabledFgColor = isDark - ? const Color(0xFF757575) - : const Color(0xFF9E9E9E); + final colors = context.color; + final disabledBgColor = colors.surfaceContainerHighest; + final disabledFgColor = colors.onSurface.withValues(alpha: 0.38); late final Color bgColor; late final Color fgColor; if (widget.variant == MyIconButtonVariant.primary) { bgColor = context.color.primary; - fgColor = Colors.black87; + fgColor = colors.onPrimary; } else { - bgColor = isDark ? Colors.grey[800]! : Colors.grey[200]!; - fgColor = isDark ? Colors.white : Colors.black87; + bgColor = colors.surfaceContainerHigh; + fgColor = colors.onSurface; } final childButton = FilledButton( diff --git a/lib/src/visual/elements/my_input.element.dart b/lib/src/visual/elements/my_input.element.dart index 2274f554..799817f5 100644 --- a/lib/src/visual/elements/my_input.element.dart +++ b/lib/src/visual/elements/my_input.element.dart @@ -45,23 +45,11 @@ class MyInput extends StatefulWidget { class _MyInputState extends State { @override Widget build(BuildContext context) { - final isDark = isDarkMode(context); - - final inputFillColor = isDark - ? Colors.white.withValues(alpha: 0.08) - : Colors.black.withValues(alpha: 0.05); - - final inputBorderColor = isDark - ? Colors.white.withValues(alpha: 0.05) - : Colors.black.withValues(alpha: 0.05); - - final inputHintColor = isDark - ? Colors.white.withValues(alpha: 0.5) - : Colors.black.withValues(alpha: 0.5); - - final prefixIconColor = isDark - ? Colors.white.withValues(alpha: 0.6) - : Colors.black.withValues(alpha: 0.6); + final colors = context.color; + final inputFillColor = colors.surfaceContainerLow; + final inputBorderColor = colors.outlineVariant.withValues(alpha: 0.6); + final inputHintColor = colors.onSurfaceVariant.withValues(alpha: 0.75); + final prefixIconColor = colors.onSurfaceVariant; return ReactiveTapFeedback( behavior: HitTestBehavior.translucent, @@ -109,7 +97,7 @@ class _MyInputState extends State { style: TextStyle( fontSize: widget.dense ? 16 : 18, fontWeight: widget.fontWeight ?? FontWeight.w500, - color: isDark ? Colors.white : Colors.black87, + color: colors.onSurface, ), decoration: InputDecoration( isDense: widget.dense, @@ -139,25 +127,25 @@ class _MyInputState extends State { focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(widget.dense ? 12 : 18), borderSide: BorderSide( - color: isDark ? Colors.white : Colors.black87, + color: colors.onSurface, width: 2, ), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(widget.dense ? 12 : 18), - borderSide: const BorderSide( - color: Colors.redAccent, + borderSide: BorderSide( + color: colors.error, ), ), focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(widget.dense ? 12 : 18), - borderSide: const BorderSide( - color: Colors.redAccent, + borderSide: BorderSide( + color: colors.error, width: 2, ), ), - errorStyle: const TextStyle( - color: Colors.redAccent, + errorStyle: TextStyle( + color: colors.error, fontSize: 14, fontWeight: FontWeight.w500, ), @@ -173,7 +161,7 @@ class _MyInputState extends State { suffixIcon: widget.suffixIcon != null ? IconTheme( data: IconThemeData( - color: isDark ? Colors.white : Colors.black87, + color: colors.onSurface, ), child: widget.suffixIcon!, ) diff --git a/lib/src/visual/elements/pro_badge.element.dart b/lib/src/visual/elements/pro_badge.element.dart index 97b02947..b0d77b71 100644 --- a/lib/src/visual/elements/pro_badge.element.dart +++ b/lib/src/visual/elements/pro_badge.element.dart @@ -10,23 +10,23 @@ class ProBadge extends StatelessWidget { return Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( - color: Colors.amber.shade700, + color: context.appColor(AppColor.premium), borderRadius: BorderRadius.circular(6), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ - const FaIcon( + FaIcon( FontAwesomeIcons.star, size: 10, - color: Colors.white, + color: context.appColor(AppColor.onWarning), ), const SizedBox(width: 4), Text( context.lang.backupCloudProBadge, - style: const TextStyle( + style: TextStyle( fontSize: 10, - color: Colors.white, + color: context.appColor(AppColor.onWarning), fontWeight: FontWeight.bold, ), ), diff --git a/lib/src/visual/helpers/video_player.helper.dart b/lib/src/visual/helpers/video_player.helper.dart index cf3048bb..f99ad8d3 100644 --- a/lib/src/visual/helpers/video_player.helper.dart +++ b/lib/src/visual/helpers/video_player.helper.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:video_player/video_player.dart'; class VideoPlayerHelper extends StatefulWidget { @@ -98,15 +99,15 @@ class _VideoPlayerHelperState extends State child: Container( width: 64, height: 64, - decoration: const BoxDecoration( - color: Colors.black54, + decoration: BoxDecoration( + color: context.appColor(AppColor.mediaScrim), shape: BoxShape.circle, ), child: Icon( _isPaused ? Icons.pause_rounded : Icons.play_arrow_rounded, - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), size: 36, ), ), diff --git a/lib/src/visual/themes/colors.dart b/lib/src/visual/themes/colors.dart index 704a7586..18515e46 100644 --- a/lib/src/visual/themes/colors.dart +++ b/lib/src/visual/themes/colors.dart @@ -1,41 +1,97 @@ import 'package:flutter/material.dart'; -import 'package:twonly/src/database/tables/mediafiles.table.dart'; -import 'package:twonly/src/database/tables/messages.table.dart'; -import 'package:twonly/src/database/twonly.db.dart'; -import 'package:twonly/src/utils/misc.dart'; -class DefaultColors { - static const messageSelf = Color.fromARGB(255, 58, 136, 102); - static const messageOther = Color.fromARGB(233, 68, 137, 255); -} +/// Every Twonly-specific color, with its light and dark values side by side. +/// +/// To add a color, add exactly one entry here and use it with: +/// `context.appColor(AppColor.yourColor)`. +enum AppColor { + // Chat bubbles + chatBubbleSent(light: Color(0xFFE8EBE9), dark: Color(0xFF292C2A)), + chatBubbleReceived(light: Color(0xFFF3F4F3), dark: Color(0xFF1D201E)), + onChatBubbleSent(light: Color(0xFF191C1A), dark: Color(0xFFF0F2F0)), + onChatBubbleReceived(light: Color(0xFF191C1A), dark: Color(0xFFE2E4E1)), + chatBubbleDeleted(light: Color(0xFFE1E4E1), dark: Color(0xFF101311)), + onChatBubbleDeleted(light: Color(0xFF626863), dark: Color(0xFF8A918B)), + chatMessageMeta(light: Color(0xFF69706A), dark: Color(0xFFAAB2AC)), + reactionBackground(light: Color(0xFFE1E5E2), dark: Color(0xFF343936)), + onReaction(light: Color(0xFF191C1A), dark: Color(0xFFF0F2F0)), -Color getMessageColorFromType( - Message message, - MediaFile? mediaFile, - BuildContext context, -) { - Color color; + // Feedback and status + success(light: Color(0xFF177245), dark: Color(0xFF65D99A)), + onSuccess(light: Color(0xFFFFFFFF), dark: Color(0xFF00391F)), + successContainer(light: Color(0xFFC0F0D2), dark: Color(0xFF00522F)), + onSuccessContainer(light: Color(0xFF002112), dark: Color(0xFF83F7B5)), + warning(light: Color(0xFF8A5700), dark: Color(0xFFFFB95F)), + onWarning(light: Color(0xFFFFFFFF), dark: Color(0xFF492900)), + warningContainer(light: Color(0xFFFFDDB4), dark: Color(0xFF673D00)), + onWarningContainer(light: Color(0xFF2C1700), dark: Color(0xFFFFDDB4)), + info(light: Color(0xFF35618D), dark: Color(0xFF9FCBFF)), + onInfo(light: Color(0xFFFFFFFF), dark: Color(0xFF003257)), + infoContainer(light: Color(0xFFD1E4FF), dark: Color(0xFF164A70)), + onInfoContainer(light: Color(0xFF001D35), dark: Color(0xFFD1E4FF)), + premium(light: Color(0xFF8A6500), dark: Color(0xFFFFC94C)), + verificationPending(light: Color(0xFF007FA3), dark: Color(0xFF5DD5F5)), + verificationIdle(light: Color(0xFF7A8497), dark: Color(0xFFAEB8CC)), - if (message.type == MessageType.restoreFlameCounter.name) { - color = Colors.orange; - } else if (message.type == MessageType.text.name) { - color = Colors.blueAccent; - } else if (message.isWidgetMedia) { - color = const Color.fromARGB(255, 155, 89, 182); - } else if (mediaFile != null) { - if (mediaFile.requiresAuthentication) { - color = context.color.primary; - } else { - if (mediaFile.type == MediaType.video) { - color = const Color.fromARGB(255, 243, 33, 208); - } else if (mediaFile.type == MediaType.audio) { - color = const Color.fromARGB(255, 252, 149, 85); - } else { - color = Colors.redAccent; - } - } - } else { - return (isDarkMode(context)) ? Colors.white : Colors.black; + // QR codes + qrForeground(light: Color(0xFF000000), dark: Color(0xFF000000)), + qrBackground(light: Color(0xFFFFFFFF), dark: Color(0xFFFFFFFF)), + + // Controls displayed over photos and videos + mediaForeground(light: Color(0xFFFFFFFF), dark: Color(0xFFFFFFFF)), + mediaForegroundMuted(light: Color(0xB3FFFFFF), dark: Color(0xB3FFFFFF)), + mediaBackground(light: Color(0xFF000000), dark: Color(0xFF000000)), + mediaScrim(light: Color(0x8A000000), dark: Color(0x8A000000)), + recording(light: Color(0xFFD32F2F), dark: Color(0xFFFF5252)), + scanHighlight(light: Color(0xFF76FF03), dark: Color(0xFF76FF03)), + + // Message-kind indicators + messageText(light: Color(0xFF35618D), dark: Color(0xFF9FCBFF)), + messageRestore(light: Color(0xFFB85F00), dark: Color(0xFFFFB95F)), + messageWidget(light: Color(0xFF79528D), dark: Color(0xFFD7B9E8)), + messageVideo(light: Color(0xFF9C3A8D), dark: Color(0xFFFFA9E5)), + messageAudio(light: Color(0xFFB85F00), dark: Color(0xFFFFB68A)), + messageImage(light: Color(0xFFBA1A1A), dark: Color(0xFFFFB4AB)); + + const AppColor({required this.light, required this.dark}); + + final Color light; + final Color dark; + + Color resolve(Brightness brightness) { + return brightness == Brightness.dark ? dark : light; + } +} + +/// Resolves [AppColor] tokens for one theme and keeps theme transitions smooth. +@immutable +class AppColors extends ThemeExtension { + const AppColors.light() + : _brightness = Brightness.light, + _interpolatedColors = null; + + const AppColors.dark() + : _brightness = Brightness.dark, + _interpolatedColors = null; + + const AppColors._interpolated(this._interpolatedColors) : _brightness = null; + + final Brightness? _brightness; + final Map? _interpolatedColors; + + Color operator [](AppColor color) { + return _interpolatedColors?[color] ?? color.resolve(_brightness!); + } + + @override + AppColors copyWith() => this; + + @override + AppColors lerp(covariant AppColors? other, double t) { + if (other == null) return this; + return AppColors._interpolated({ + for (final color in AppColor.values) + color: Color.lerp(this[color], other[color], t)!, + }); } - return color; } diff --git a/lib/src/visual/themes/dark.dart b/lib/src/visual/themes/dark.dart index 62c5f3a4..eb789ab4 100644 --- a/lib/src/visual/themes/dark.dart +++ b/lib/src/visual/themes/dark.dart @@ -1,21 +1,55 @@ import 'dart:io'; + import 'package:flutter/material.dart'; +import 'package:twonly/src/visual/themes/colors.dart'; import 'package:twonly/src/visual/themes/light.dart'; +Color _onPrimary(Color primary) { + return ThemeData.estimateBrightnessForColor(primary) == Brightness.dark + ? const Color(0xFFFFFFFF) + : const Color(0xFF082117); +} + +ColorScheme _darkColorScheme(Color primary) { + return ColorScheme.fromSeed( + brightness: Brightness.dark, + seedColor: primary, + primary: primary, + ).copyWith( + onPrimary: _onPrimary(primary), + surface: const Color(0xFF111412), + onSurface: const Color(0xFFE2E4E1), + onSurfaceVariant: const Color(0xFFBEC9C0), + surfaceDim: const Color(0xFF111412), + surfaceBright: const Color(0xFF373A37), + surfaceContainerLowest: const Color(0xFF0B0E0C), + surfaceContainerLow: const Color(0xFF171A18), + surfaceContainer: const Color(0xFF1D211E), + surfaceContainerHigh: const Color(0xFF282C29), + surfaceContainerHighest: const Color(0xFF333734), + outline: const Color(0xFF89938B), + outlineVariant: const Color(0xFF404842), + inverseSurface: const Color(0xFFE2E4E1), + onInverseSurface: const Color(0xFF2E312F), + shadow: const Color(0xFF000000), + scrim: const Color(0xFF000000), + error: const Color(0xFFFFB4AB), + onError: const Color(0xFF690005), + errorContainer: const Color(0xFF93000A), + onErrorContainer: const Color(0xFFFFDAD6), + ); +} + ThemeData getDarkTheme([Color primary = defaultPrimaryColor]) { - final base = ThemeData.dark().copyWith( - colorScheme: ColorScheme.fromSeed( - brightness: Brightness.dark, - seedColor: primary, - primary: primary, - surface: const Color.fromARGB(255, 20, 18, 23), - surfaceContainer: const Color.fromARGB(255, 45, 41, 54), - surfaceContainerLow: const Color.fromARGB(255, 38, 34, 45), - surfaceContainerHigh: const Color.fromARGB(255, 52, 48, 62), - ), + final colorScheme = _darkColorScheme(primary); + final base = ThemeData( + brightness: Brightness.dark, + colorScheme: colorScheme, + scaffoldBackgroundColor: colorScheme.surface, inputDecorationTheme: const InputDecorationTheme( border: OutlineInputBorder(), ), + extensions: const [AppColors.dark()], ); return base.copyWith( textTheme: base.textTheme.apply( diff --git a/lib/src/visual/themes/light.dart b/lib/src/visual/themes/light.dart index b0536295..2e24da36 100644 --- a/lib/src/visual/themes/light.dart +++ b/lib/src/visual/themes/light.dart @@ -1,20 +1,56 @@ import 'dart:io'; -import 'package:flutter/material.dart'; -import 'package:twonly/src/utils/misc.dart'; -const defaultPrimaryColor = Color(0xFF32be80); +import 'package:flutter/material.dart'; +import 'package:twonly/src/visual/themes/colors.dart'; + +const defaultPrimaryColor = Color(0xFF32BE80); + +Color _onPrimary(Color primary) { + return ThemeData.estimateBrightnessForColor(primary) == Brightness.dark + ? const Color(0xFFFFFFFF) + : const Color(0xFF082117); +} + +/// Material colors for the light theme. Twonly-specific light/dark pairs live +/// together in `colors.dart`. +ColorScheme _lightColorScheme(Color primary) { + return ColorScheme.fromSeed( + seedColor: primary, + primary: primary, + ).copyWith( + onPrimary: _onPrimary(primary), + surface: const Color(0xFFFFFFFF), + onSurface: const Color(0xFF191C1A), + onSurfaceVariant: const Color(0xFF414943), + surfaceDim: const Color(0xFFD9DCD9), + surfaceBright: const Color(0xFFFFFFFF), + surfaceContainerLowest: const Color(0xFFFFFFFF), + surfaceContainerLow: const Color(0xFFF5F7F5), + surfaceContainer: const Color(0xFFEFF2EF), + surfaceContainerHigh: const Color(0xFFE9ECE9), + surfaceContainerHighest: const Color(0xFFE3E7E3), + outline: const Color(0xFF717971), + outlineVariant: const Color(0xFFC1C9C1), + inverseSurface: const Color(0xFF2E312F), + onInverseSurface: const Color(0xFFF0F2F0), + shadow: const Color(0xFF000000), + scrim: const Color(0xFF000000), + error: const Color(0xFFBA1A1A), + onError: const Color(0xFFFFFFFF), + errorContainer: const Color(0xFFFFDAD6), + onErrorContainer: const Color(0xFF410002), + ); +} ThemeData getLightTheme([Color primary = defaultPrimaryColor]) { + final colorScheme = _lightColorScheme(primary); final base = ThemeData( - colorScheme: ColorScheme.fromSeed( - seedColor: primary, - primary: primary, - surface: Colors.white, - ), - scaffoldBackgroundColor: Colors.white, + colorScheme: colorScheme, + scaffoldBackgroundColor: colorScheme.surface, inputDecorationTheme: const InputDecorationTheme( border: OutlineInputBorder(), ), + extensions: const [AppColors.light()], ); return base.copyWith( textTheme: base.textTheme.apply( @@ -28,20 +64,17 @@ final ThemeData lightTheme = getLightTheme(); final ButtonStyle primaryColorButtonStyle = FilledButton.styleFrom( backgroundColor: defaultPrimaryColor, - foregroundColor: Colors.black87, + foregroundColor: const Color(0xFF082117), padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ); ButtonStyle secondaryGreyButtonStyle(BuildContext context) { + final colors = Theme.of(context).colorScheme; return FilledButton.styleFrom( - backgroundColor: isDarkMode(context) ? Colors.grey[800] : Colors.grey[200], - foregroundColor: isDarkMode(context) ? Colors.white : Colors.black87, + backgroundColor: colors.surfaceContainerHigh, + foregroundColor: colors.onSurface, padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ); } diff --git a/lib/src/visual/views/camera/camera_preview_components/camera_preview.dart b/lib/src/visual/views/camera/camera_preview_components/camera_preview.dart index ce544029..d99e460e 100644 --- a/lib/src/visual/views/camera/camera_preview_components/camera_preview.dart +++ b/lib/src/visual/views/camera/camera_preview_components/camera_preview.dart @@ -1,5 +1,6 @@ import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/helpers/media_view_sizing.helper.dart'; import 'package:twonly/src/visual/helpers/screenshot.helper.dart'; import 'package:twonly/src/visual/views/camera/camera_preview_components/main_camera_controller.dart'; @@ -96,7 +97,9 @@ class MainCameraPreview extends StatelessWidget { decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( - color: Colors.white.withAlpha(150), + color: context.appColor( + AppColor.mediaForegroundMuted, + ), ), ), ), diff --git a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/camera_bottom_controls.dart b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/camera_bottom_controls.dart index b74981b7..5fa4fecf 100644 --- a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/camera_bottom_controls.dart +++ b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/camera_bottom_controls.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:twonly/locator.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/zoom_tutorial_overlay.dart'; import 'package:twonly/src/visual/views/camera/camera_preview_components/face_filters.dart'; import 'package:twonly/src/visual/views/camera/camera_preview_components/main_camera_controller.dart'; @@ -66,13 +67,13 @@ class CameraBottomControls extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ if (!isVideoRecording) - _buildSideButtonLeft() + _buildSideButtonLeft(context) else - _buildLockOrStopButton(), - _buildShutterButton(), + _buildLockOrStopButton(context), + _buildShutterButton(context), if (!isVideoRecording) if (isFront) - _buildSideButtonRight() + _buildSideButtonRight(context) else const SizedBox(width: 80) else @@ -85,7 +86,7 @@ class CameraBottomControls extends StatelessWidget { ); } - Widget _buildLockOrStopButton() { + Widget _buildLockOrStopButton(BuildContext context) { if (videoRecordingLocked) { // Show stop button return GestureDetector( @@ -100,7 +101,7 @@ class CameraBottomControls extends StatelessWidget { width: 34, height: 34, decoration: BoxDecoration( - color: Colors.red, + color: context.appColor(AppColor.recording), borderRadius: BorderRadius.circular(6), ), ), @@ -113,7 +114,7 @@ class CameraBottomControls extends StatelessWidget { } } - Widget _buildSideButtonLeft() { + Widget _buildSideButtonLeft(BuildContext context) { return GestureDetector( onTap: onPressSideButtonLeft, child: Align( @@ -128,7 +129,7 @@ class CameraBottomControls extends StatelessWidget { ? FontAwesomeIcons.xmark : FontAwesomeIcons.arrowLeft : FontAwesomeIcons.photoFilm, - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), size: 25, ), ), @@ -137,7 +138,7 @@ class CameraBottomControls extends StatelessWidget { ); } - Widget _buildShutterButton() { + Widget _buildShutterButton(BuildContext context) { return StreamBuilder( stream: userService.onUserUpdated, builder: (context, snapshot) { @@ -156,7 +157,9 @@ class CameraBottomControls extends StatelessWidget { shape: BoxShape.circle, border: Border.all( width: 7, - color: isVideoRecording ? Colors.red : Colors.white, + color: isVideoRecording + ? context.appColor(AppColor.recording) + : context.appColor(AppColor.mediaForeground), ), ), child: mc.currentFilterType.preview, @@ -168,7 +171,7 @@ class CameraBottomControls extends StatelessWidget { ); } - Widget _buildSideButtonRight() { + Widget _buildSideButtonRight(BuildContext context) { return GestureDetector( onTap: onPressSideButtonRight, child: Align( @@ -184,7 +187,7 @@ class CameraBottomControls extends StatelessWidget { ? FontAwesomeIcons.xmark : FontAwesomeIcons.arrowRight : FontAwesomeIcons.faceGrinTongueSquint, - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), size: 25, ), ), @@ -249,10 +252,10 @@ class _AnimatedLockButtonState extends State<_AnimatedLockButton> height: 50, width: 80, padding: const EdgeInsets.all(2), - child: const Center( + child: Center( child: FaIcon( FontAwesomeIcons.lock, - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), size: 25, ), ), diff --git a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/camera_scanned_overlay.dart b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/camera_scanned_overlay.dart index e3ac8e78..a626f039 100644 --- a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/camera_scanned_overlay.dart +++ b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/camera_scanned_overlay.dart @@ -59,7 +59,7 @@ class CameraScannedOverlay extends StatelessWidget { color: Colors.transparent, child: FaIcon( FontAwesomeIcons.shareFromSquare, - color: isDarkMode(context) ? Colors.white : Colors.black, + color: context.color.onSurface, size: 17, ), ), diff --git a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/camera_selfie_flash.dart b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/camera_selfie_flash.dart index 8d5edf44..aec7ec2e 100644 --- a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/camera_selfie_flash.dart +++ b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/camera_selfie_flash.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:twonly/src/utils/misc.dart'; /// Full-screen white overlay used as a "flash" when taking selfies /// with the front camera and flash enabled. @@ -11,7 +12,7 @@ class CameraSelfieFlash extends StatelessWidget { child: ClipRRect( borderRadius: BorderRadius.circular(22), child: Container( - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), ), ), ); diff --git a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/camera_top_actions.dart b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/camera_top_actions.dart index 4f57de8c..88814cf9 100644 --- a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/camera_top_actions.dart +++ b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/camera_top_actions.dart @@ -42,14 +42,14 @@ class CameraTopActions extends StatelessWidget { : Icons.flash_off_rounded, tooltipText: context.lang.toggleFlashLight, color: selectedCameraDetails.isFlashOn - ? Colors.white - : Colors.white.withAlpha(160), + ? context.appColor(AppColor.mediaForeground) + : context.appColor(AppColor.mediaForegroundMuted), onPressed: onToggleFlash, ), if (!hasAudioPermission) ActionButton( Icons.mic_off_rounded, - color: Colors.white.withAlpha(160), + color: context.appColor(AppColor.mediaForegroundMuted), tooltipText: 'Allow microphone access for video recording.', onPressed: onRequestMicrophone, ), diff --git a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/zoom_tutorial_overlay.dart b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/zoom_tutorial_overlay.dart index 8e60841f..c4f62a29 100644 --- a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/zoom_tutorial_overlay.dart +++ b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_components/zoom_tutorial_overlay.dart @@ -105,13 +105,13 @@ class _ZoomTutorialOverlayState extends State vertical: 6, ), decoration: BoxDecoration( - color: Colors.black, + color: context.appColor(AppColor.mediaBackground), borderRadius: BorderRadius.circular(16), ), child: Text( context.lang.dragToZoom, - style: const TextStyle( - color: Colors.white, + style: TextStyle( + color: context.appColor(AppColor.mediaForeground), fontSize: 12, fontWeight: FontWeight.w700, letterSpacing: 0.3, @@ -131,13 +131,15 @@ class _ZoomTutorialOverlayState extends State height: 42, decoration: BoxDecoration( shape: BoxShape.circle, - color: Colors.white.withValues(alpha: 0.5), + color: context.appColor( + AppColor.mediaForegroundMuted, + ), ), - child: const Center( + child: Center( child: FaIcon( FontAwesomeIcons.handPointer, size: 18, - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), ), ), ), diff --git a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart index 65d12e7c..d62bc720 100644 --- a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart +++ b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart @@ -84,6 +84,9 @@ class _CameraPreviewControllerViewState @override Widget build(BuildContext context) { + widget.mainController.scanHighlightColor = context.appColor( + AppColor.scanHighlight, + ); if (AppState.hasCameraPermissions) { return CameraPreviewView( sendToGroup: widget.sendToGroup, diff --git a/lib/src/visual/views/camera/camera_preview_components/main_camera_controller.dart b/lib/src/visual/views/camera/camera_preview_components/main_camera_controller.dart index c702ab33..eb356241 100644 --- a/lib/src/visual/views/camera/camera_preview_components/main_camera_controller.dart +++ b/lib/src/visual/views/camera/camera_preview_components/main_camera_controller.dart @@ -104,6 +104,7 @@ class MainCameraController { bool _isBusy = false; bool _isBusyFaces = false; CustomPaint? qrCodePain; + Color? scanHighlightColor; CustomPaint? facePaint; Offset? focusPointOffset; @@ -529,12 +530,14 @@ class MainCameraController { final controller = cameraController; if (inputImage.metadata?.size != null && inputImage.metadata?.rotation != null && - controller != null) { + controller != null && + scanHighlightColor != null) { final painter = BarcodeDetectorPainter( barcodes, inputImage.metadata!.size, inputImage.metadata!.rotation, controller.description.lensDirection, + scanHighlightColor!, ); qrCodePain = CustomPaint(painter: painter); diff --git a/lib/src/visual/views/camera/camera_preview_components/painters/barcode_detector_painter.dart b/lib/src/visual/views/camera/camera_preview_components/painters/barcode_detector_painter.dart index a3a1230e..74d14acf 100644 --- a/lib/src/visual/views/camera/camera_preview_components/painters/barcode_detector_painter.dart +++ b/lib/src/visual/views/camera/camera_preview_components/painters/barcode_detector_painter.dart @@ -10,19 +10,21 @@ class BarcodeDetectorPainter extends CustomPainter { this.imageSize, this.rotation, this.cameraLensDirection, + this.color, ); final List barcodes; final Size imageSize; final InputImageRotation rotation; final CameraLensDirection cameraLensDirection; + final Color color; @override void paint(Canvas canvas, Size size) { final paint = Paint() ..style = PaintingStyle.stroke ..strokeWidth = 3.0 - ..color = Colors.lightGreenAccent; + ..color = color; for (final barcode in barcodes) { final cornerPoints = []; diff --git a/lib/src/visual/views/camera/camera_preview_components/painters/face_filters/face_filter_painter.dart b/lib/src/visual/views/camera/camera_preview_components/painters/face_filters/face_filter_painter.dart index b32ec7de..5d8868aa 100644 --- a/lib/src/visual/views/camera/camera_preview_components/painters/face_filters/face_filter_painter.dart +++ b/lib/src/visual/views/camera/camera_preview_components/painters/face_filters/face_filter_painter.dart @@ -1,6 +1,7 @@ import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart'; +import 'package:twonly/src/utils/misc.dart'; abstract class FaceFilterPainter extends CustomPainter { FaceFilterPainter( @@ -34,7 +35,7 @@ class Preview extends StatelessWidget { return Container( decoration: BoxDecoration( shape: BoxShape.circle, - color: Colors.grey.withValues(alpha: 0.2), + color: context.color.surfaceContainerHighest, ), child: Center( child: child, diff --git a/lib/src/visual/views/camera/camera_preview_components/save_to_gallery.dart b/lib/src/visual/views/camera/camera_preview_components/save_to_gallery.dart index 4a251ea2..75e65dc9 100644 --- a/lib/src/visual/views/camera/camera_preview_components/save_to_gallery.dart +++ b/lib/src/visual/views/camera/camera_preview_components/save_to_gallery.dart @@ -89,12 +89,14 @@ class SaveToGalleryButtonState extends State { mainAxisSize: MainAxisSize.min, children: [ if (_imageSaving || widget.isLoading) - const SizedBox( + SizedBox( width: 12, height: 12, child: CircularProgressIndicator.adaptive( strokeWidth: 1, - valueColor: AlwaysStoppedAnimation(Colors.white), + valueColor: AlwaysStoppedAnimation( + context.appColor(AppColor.mediaForeground), + ), ), ) else if (_imageSaved) diff --git a/lib/src/visual/views/camera/camera_preview_components/send_to.dart b/lib/src/visual/views/camera/camera_preview_components/send_to.dart index 5b55bc08..9f62047d 100644 --- a/lib/src/visual/views/camera/camera_preview_components/send_to.dart +++ b/lib/src/visual/views/camera/camera_preview_components/send_to.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart'; +import 'package:twonly/src/utils/misc.dart'; class ShowTitleText extends StatelessWidget { const ShowTitleText({ @@ -15,13 +16,13 @@ class ShowTitleText extends StatelessWidget { @override Widget build(BuildContext context) { final textStyle = TextStyle( - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), fontWeight: FontWeight.bold, fontSize: isLink ? 14 : 24, decoration: TextDecoration.none, - shadows: const [ + shadows: [ Shadow( - color: Color.fromARGB(122, 0, 0, 0), + color: context.appColor(AppColor.mediaScrim), blurRadius: 5, ), ], diff --git a/lib/src/visual/views/camera/camera_preview_components/video_recording_time.dart b/lib/src/visual/views/camera/camera_preview_components/video_recording_time.dart index 57589293..8a64bb3f 100644 --- a/lib/src/visual/views/camera/camera_preview_components/video_recording_time.dart +++ b/lib/src/visual/views/camera/camera_preview_components/video_recording_time.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:twonly/src/utils/misc.dart'; class VideoRecordingTimer extends StatelessWidget { const VideoRecordingTimer({ @@ -38,19 +39,23 @@ class VideoRecordingTimer extends StatelessWidget { maxRecordingTime.inMilliseconds) .clamp(0.0, 1.0), strokeWidth: 4, - valueColor: const AlwaysStoppedAnimation(Colors.red), - backgroundColor: Colors.grey[300], + valueColor: AlwaysStoppedAnimation( + context.appColor(AppColor.recording), + ), + backgroundColor: context.appColor( + AppColor.mediaForegroundMuted, + ), ), ), Center( child: Text( elapsed.inSeconds.toString(), textAlign: TextAlign.center, - style: const TextStyle( + style: TextStyle( fontSize: 17, shadows: [ Shadow( - color: Color.fromARGB(122, 0, 0, 0), + color: context.appColor(AppColor.mediaScrim), blurRadius: 5, ), ], diff --git a/lib/src/visual/views/camera/camera_preview_components/zoom_selector.dart b/lib/src/visual/views/camera/camera_preview_components/zoom_selector.dart index 51f38b59..3df41b23 100644 --- a/lib/src/visual/views/camera/camera_preview_components/zoom_selector.dart +++ b/lib/src/visual/views/camera/camera_preview_components/zoom_selector.dart @@ -6,6 +6,7 @@ import 'dart:math'; import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart'; String beautifulZoomScale(double scale) { @@ -60,7 +61,7 @@ class CameraZoomButtons extends StatelessWidget { final zoomButtonStyle = TextButton.styleFrom( padding: EdgeInsets.zero, - foregroundColor: Colors.white, + foregroundColor: context.appColor(AppColor.mediaForeground), minimumSize: const Size(40, 40), alignment: Alignment.center, tapTargetSize: MaterialTapTargetSize.shrinkWrap, @@ -90,7 +91,7 @@ class CameraZoomButtons extends StatelessWidget { child: ClipRRect( borderRadius: BorderRadius.circular(40), child: ColoredBox( - color: const Color.fromARGB(90, 0, 0, 0), + color: context.appColor(AppColor.mediaScrim), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -98,7 +99,9 @@ class CameraZoomButtons extends StatelessWidget { TextButton( style: zoomButtonStyle.copyWith( foregroundColor: WidgetStateProperty.all( - isSmallerFocused ? Colors.yellow : Colors.white, + isSmallerFocused + ? context.appColor(AppColor.warning) + : context.appColor(AppColor.mediaForeground), ), ), onPressed: () async { @@ -121,7 +124,9 @@ class CameraZoomButtons extends StatelessWidget { TextButton( style: zoomButtonStyle.copyWith( foregroundColor: WidgetStateProperty.all( - isMiddleFocused ? Colors.yellow : Colors.white, + isMiddleFocused + ? context.appColor(AppColor.warning) + : context.appColor(AppColor.mediaForeground), ), ), onPressed: () async { @@ -142,7 +147,9 @@ class CameraZoomButtons extends StatelessWidget { TextButton( style: zoomButtonStyle.copyWith( foregroundColor: WidgetStateProperty.all( - (scaleFactor >= 2) ? Colors.yellow : Colors.white, + (scaleFactor >= 2) + ? context.appColor(AppColor.warning) + : context.appColor(AppColor.mediaForeground), ), ), onPressed: () async { diff --git a/lib/src/visual/views/camera/camera_qr_scanner.view.dart b/lib/src/visual/views/camera/camera_qr_scanner.view.dart index 58b17452..ee5cb8e6 100644 --- a/lib/src/visual/views/camera/camera_qr_scanner.view.dart +++ b/lib/src/visual/views/camera/camera_qr_scanner.view.dart @@ -87,7 +87,7 @@ class QrCodeScannerViewState extends State { child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.black.withValues(alpha: 0.7), + color: context.appColor(AppColor.mediaScrim), borderRadius: BorderRadius.circular(12), ), child: Text( @@ -96,8 +96,8 @@ class QrCodeScannerViewState extends State { getContactDisplayName(widget.contact!), ) : context.lang.qrScannerVerifyHint, - style: const TextStyle( - color: Colors.white, + style: TextStyle( + color: context.appColor(AppColor.mediaForeground), fontSize: 14, ), textAlign: TextAlign.center, diff --git a/lib/src/visual/views/camera/share_image_contact_selection.view.dart b/lib/src/visual/views/camera/share_image_contact_selection.view.dart index a426409d..ebbe2f46 100644 --- a/lib/src/visual/views/camera/share_image_contact_selection.view.dart +++ b/lib/src/visual/views/camera/share_image_contact_selection.view.dart @@ -429,13 +429,13 @@ class _ShareImageView extends State { mainAxisSize: MainAxisSize.min, children: [ if (!mediaStoreFutureReady || sendingImage) - const SizedBox( + SizedBox( height: 14, width: 14, child: CircularProgressIndicator.adaptive( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( - Colors.black87, + context.color.onPrimary, ), ), ) diff --git a/lib/src/visual/views/camera/share_image_contact_selection_components/best_friends_selector.dart b/lib/src/visual/views/camera/share_image_contact_selection_components/best_friends_selector.dart index efc3ccc5..92c435d6 100644 --- a/lib/src/visual/views/camera/share_image_contact_selection_components/best_friends_selector.dart +++ b/lib/src/visual/views/camera/share_image_contact_selection_components/best_friends_selector.dart @@ -49,10 +49,10 @@ class BestFriendsSelector extends StatelessWidget { ), decoration: BoxDecoration( color: Theme.of(context).colorScheme.outline.withAlpha(50), - boxShadow: const [ + boxShadow: [ BoxShadow( blurRadius: 10.9, - color: Color.fromRGBO(0, 0, 0, 0.1), + color: context.color.shadow.withValues(alpha: 0.1), ), ], borderRadius: BorderRadius.circular(8), @@ -135,10 +135,10 @@ class UserCheckbox extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 10), decoration: BoxDecoration( color: Theme.of(context).colorScheme.outline.withAlpha(50), - boxShadow: const [ + boxShadow: [ BoxShadow( blurRadius: 10.9, - color: Color.fromRGBO(0, 0, 0, 0.1), + color: context.color.shadow.withValues(alpha: 0.1), ), ], borderRadius: BorderRadius.circular(8), diff --git a/lib/src/visual/views/camera/share_image_contact_selection_components/select_show_time.dart b/lib/src/visual/views/camera/share_image_contact_selection_components/select_show_time.dart index 2bf8b551..6f3267d6 100644 --- a/lib/src/visual/views/camera/share_image_contact_selection_components/select_show_time.dart +++ b/lib/src/visual/views/camera/share_image_contact_selection_components/select_show_time.dart @@ -28,7 +28,7 @@ class _SelectShowTimeState extends State { height: 350, padding: const EdgeInsets.only(top: 6), margin: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), - color: CupertinoColors.systemBackground.resolveFrom(context), + color: Theme.of(context).colorScheme.surface, child: SafeArea( top: false, child: Column( @@ -37,7 +37,7 @@ class _SelectShowTimeState extends State { margin: const EdgeInsets.all(10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(32), - color: Colors.grey, + color: Theme.of(context).colorScheme.outline, ), height: 3, width: 60, diff --git a/lib/src/visual/views/camera/share_image_editor.view.dart b/lib/src/visual/views/camera/share_image_editor.view.dart index 334611d9..9bbadaff 100644 --- a/lib/src/visual/views/camera/share_image_editor.view.dart +++ b/lib/src/visual/views/camera/share_image_editor.view.dart @@ -545,9 +545,7 @@ class _ShareImageEditorView extends State { await askToCloseThenClose(); }, child: Scaffold( - backgroundColor: widget.sharedFromGallery - ? null - : Colors.white.withAlpha(0), + backgroundColor: widget.sharedFromGallery ? null : Colors.transparent, resizeToAvoidBottomInset: false, body: Stack( key: _editorStackKey, diff --git a/lib/src/visual/views/camera/share_image_editor_components/action_button.dart b/lib/src/visual/views/camera/share_image_editor_components/action_button.dart index f403b48b..8e312739 100644 --- a/lib/src/visual/views/camera/share_image_editor_components/action_button.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/action_button.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:twonly/src/utils/misc.dart'; class ActionButton extends StatelessWidget { const ActionButton( @@ -27,11 +28,11 @@ class ActionButton extends StatelessWidget { icon as FaIconData?, size: 25, color: disable - ? const Color.fromARGB(154, 255, 255, 255) - : color ?? Colors.white, - shadows: const [ + ? context.appColor(AppColor.mediaForegroundMuted) + : color ?? context.appColor(AppColor.mediaForeground), + shadows: [ Shadow( - color: Color.fromARGB(122, 0, 0, 0), + color: context.appColor(AppColor.mediaScrim), blurRadius: 5, ), ], @@ -40,11 +41,11 @@ class ActionButton extends StatelessWidget { icon as IconData?, size: 30, color: disable - ? const Color.fromARGB(154, 255, 255, 255) - : color ?? Colors.white, - shadows: const [ + ? context.appColor(AppColor.mediaForegroundMuted) + : color ?? context.appColor(AppColor.mediaForeground), + shadows: [ Shadow( - color: Color.fromARGB(122, 0, 0, 0), + color: context.appColor(AppColor.mediaScrim), blurRadius: 5, ), ], diff --git a/lib/src/visual/views/camera/share_image_editor_components/display_time_picker.dart b/lib/src/visual/views/camera/share_image_editor_components/display_time_picker.dart index 7d9211ee..62db6f02 100644 --- a/lib/src/visual/views/camera/share_image_editor_components/display_time_picker.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/display_time_picker.dart @@ -53,7 +53,7 @@ Future showDisplayTimePicker( await showModalBottomSheet( context: context, - backgroundColor: Colors.black, + backgroundColor: Theme.of(context).colorScheme.surface, builder: (sheetContext) { return SelectShowTime( initialItem: initialItem, diff --git a/lib/src/visual/views/camera/share_image_editor_components/editor_bottom_bar.dart b/lib/src/visual/views/camera/share_image_editor_components/editor_bottom_bar.dart index f605ce2c..dfcf7393 100644 --- a/lib/src/visual/views/camera/share_image_editor_components/editor_bottom_bar.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/editor_bottom_bar.dart @@ -65,12 +65,14 @@ class EditorBottomBar extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ if (isSending) - const SizedBox( + SizedBox( height: 12, width: 12, child: CircularProgressIndicator.adaptive( strokeWidth: 2, - valueColor: AlwaysStoppedAnimation(Colors.black87), + valueColor: AlwaysStoppedAnimation( + context.color.onPrimary, + ), ), ) else diff --git a/lib/src/visual/views/camera/share_image_editor_components/editor_side_toolbar.dart b/lib/src/visual/views/camera/share_image_editor_components/editor_side_toolbar.dart index 1885df15..8088cdf6 100644 --- a/lib/src/visual/views/camera/share_image_editor_components/editor_side_toolbar.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/editor_side_toolbar.dart @@ -107,7 +107,7 @@ class EditorSideToolbar extends StatelessWidget { onPressed: () async { final layer = await showModalBottomSheet( context: context, - backgroundColor: Colors.black, + backgroundColor: context.color.surface, builder: (context) => const EmojiPickerBottom(), ); if (layer == null) return; @@ -124,7 +124,7 @@ class EditorSideToolbar extends StatelessWidget { tooltipText: context.lang.shareImageSendToWidget, color: sendToWidget || highlightWidgetOption ? Theme.of(context).colorScheme.primary - : Colors.white, + : context.appColor(AppColor.mediaForeground), disable: isUpdatingWidgetMode, onPressed: onToggleSendToWidget, ), @@ -148,7 +148,7 @@ class EditorSideToolbar extends StatelessWidget { tooltipText: 'Trim video', color: trimmerVisible ? Theme.of(context).colorScheme.primary - : Colors.white, + : context.appColor(AppColor.mediaForeground), onPressed: onToggleTrimmer, ), ], @@ -160,8 +160,8 @@ class EditorSideToolbar extends StatelessWidget { : Icons.volume_up_rounded, tooltipText: 'Enable Audio in Video', color: (mediaService.removeAudio) - ? Colors.white.withAlpha(160) - : Colors.white, + ? context.appColor(AppColor.mediaForegroundMuted) + : context.appColor(AppColor.mediaForeground), onPressed: onToggleAudio, ), ], @@ -170,7 +170,7 @@ class EditorSideToolbar extends StatelessWidget { ActionButton( Icons.crop_rotate_outlined, tooltipText: 'Crop or rotate image', - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), onPressed: () { layerStack.toggleBackgroundEditing(); onChanged(); @@ -184,7 +184,7 @@ class EditorSideToolbar extends StatelessWidget { tooltipText: context.lang.protectAsARealTwonly, color: media.requiresAuthentication ? Theme.of(context).colorScheme.primary - : Colors.white, + : context.appColor(AppColor.mediaForeground), onPressed: onToggleRequiresAuth, ), ], diff --git a/lib/src/visual/views/camera/share_image_editor_components/layers/draw.layer.dart b/lib/src/visual/views/camera/share_image_editor_components/layers/draw.layer.dart index 5f266195..d116e9ca 100644 --- a/lib/src/visual/views/camera/share_image_editor_components/layers/draw.layer.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/layers/draw.layer.dart @@ -109,8 +109,8 @@ class _DrawLayerState extends State { FontAwesomeIcons.arrowRotateLeft, tooltipText: context.lang.undo, color: widget.layerData.control.paths.isNotEmpty - ? Colors.white - : Colors.white.withAlpha(80), + ? context.appColor(AppColor.mediaForeground) + : context.appColor(AppColor.mediaForegroundMuted), onPressed: () { if (widget.layerData.control.paths.isEmpty) return; skipNextEvent = true; @@ -123,8 +123,8 @@ class _DrawLayerState extends State { tooltipText: context.lang.redo, FontAwesomeIcons.arrowRotateRight, color: undoList.isNotEmpty - ? Colors.white - : Colors.white.withAlpha(80), + ? context.appColor(AppColor.mediaForeground) + : context.appColor(AppColor.mediaForegroundMuted), onPressed: () { if (undoList.isEmpty) return; @@ -226,7 +226,7 @@ class MagnifyingGlass extends StatelessWidget { color: color, shape: BoxShape.circle, border: Border.all( - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), width: 2, ), ), diff --git a/lib/src/visual/views/camera/share_image_editor_components/layers/emoji.layer.dart b/lib/src/visual/views/camera/share_image_editor_components/layers/emoji.layer.dart index 79fff13e..a4f5cc67 100755 --- a/lib/src/visual/views/camera/share_image_editor_components/layers/emoji.layer.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/layers/emoji.layer.dart @@ -6,6 +6,7 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:twonly/src/utils/log.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/views/camera/share_image_editor_components/action_button.dart'; import 'package:twonly/src/visual/views/camera/share_image_editor_components/layer_data.dart'; @@ -171,7 +172,9 @@ class _EmojiLayerState extends State { child: ActionButton( FontAwesomeIcons.trashCan, tooltipText: '', - color: deleteLayer ? Colors.red : Colors.white, + color: deleteLayer + ? context.appColor(AppColor.recording) + : context.appColor(AppColor.mediaForeground), ), ), ), diff --git a/lib/src/visual/views/camera/share_image_editor_components/layers/filter.layer.dart b/lib/src/visual/views/camera/share_image_editor_components/layers/filter.layer.dart index 75e032fc..de9d353e 100644 --- a/lib/src/visual/views/camera/share_image_editor_components/layers/filter.layer.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/layers/filter.layer.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/views/camera/share_image_editor_components/layer_data.dart'; import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/filters/datetime_filter.dart'; import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/filters/image_filter.dart'; @@ -45,11 +46,11 @@ class FilterText extends StatelessWidget { this.text, { super.key, this.fontSize = 24, - this.color = Colors.white, + this.color, }); final String text; final double fontSize; - final Color color; + final Color? color; @override Widget build(BuildContext context) { @@ -58,10 +59,10 @@ class FilterText extends StatelessWidget { textAlign: TextAlign.start, style: TextStyle( fontSize: fontSize, - color: color, - shadows: const [ + color: color ?? context.appColor(AppColor.mediaForeground), + shadows: [ Shadow( - color: Color.fromARGB(122, 0, 0, 0), + color: context.appColor(AppColor.mediaScrim), blurRadius: 5, ), ], diff --git a/lib/src/visual/views/camera/share_image_editor_components/layers/filters/datetime_filter.dart b/lib/src/visual/views/camera/share_image_editor_components/layers/filters/datetime_filter.dart index 72f5564d..fe5684e2 100644 --- a/lib/src/visual/views/camera/share_image_editor_components/layers/filters/datetime_filter.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/layers/filters/datetime_filter.dart @@ -4,9 +4,9 @@ import 'package:intl/intl.dart'; import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/filter.layer.dart'; class DateTimeFilter extends StatelessWidget { - const DateTimeFilter({super.key, this.color = Colors.white}); + const DateTimeFilter({super.key, this.color}); - final Color color; + final Color? color; @override Widget build(BuildContext context) { diff --git a/lib/src/visual/views/camera/share_image_editor_components/layers/text.layer.dart b/lib/src/visual/views/camera/share_image_editor_components/layers/text.layer.dart index 6c2ea0ff..ca87c027 100755 --- a/lib/src/visual/views/camera/share_image_editor_components/layers/text.layer.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/layers/text.layer.dart @@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:provider/provider.dart'; import 'package:twonly/src/providers/image_editor.provider.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/views/camera/share_image_editor_components/action_button.dart'; import 'package:twonly/src/visual/views/camera/share_image_editor_components/layer_data.dart'; @@ -113,7 +114,7 @@ class _TextViewState extends State { right: 0, child: Container( decoration: BoxDecoration( - color: Colors.black.withAlpha(100), + color: context.appColor(AppColor.mediaScrim), ), child: TextField( controller: textController, @@ -151,8 +152,8 @@ class _TextViewState extends State { ), ), textAlign: TextAlign.center, - style: const TextStyle( - color: Colors.white, + style: TextStyle( + color: context.appColor(AppColor.mediaForeground), fontSize: 20, ), ), @@ -219,13 +220,13 @@ class _TextViewState extends State { child: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( - color: Colors.black.withAlpha(100), + color: context.appColor(AppColor.mediaScrim), ), child: Text( widget.layerData.text, textAlign: TextAlign.center, - style: const TextStyle( - color: Colors.white, + style: TextStyle( + color: context.appColor(AppColor.mediaForeground), fontSize: 20, ), ), @@ -245,7 +246,9 @@ class _TextViewState extends State { child: ActionButton( FontAwesomeIcons.trashCan, tooltipText: '', - color: deleteLayer ? Colors.red : Colors.white, + color: deleteLayer + ? context.appColor(AppColor.recording) + : context.appColor(AppColor.mediaForeground), ), ), ), diff --git a/lib/src/visual/views/camera/share_image_editor_components/video_trimmer.dart b/lib/src/visual/views/camera/share_image_editor_components/video_trimmer.dart index 3a8204f0..dd72fd60 100644 --- a/lib/src/visual/views/camera/share_image_editor_components/video_trimmer.dart +++ b/lib/src/visual/views/camera/share_image_editor_components/video_trimmer.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:video_player/video_player.dart'; /// Cuts a recorded video down to the part the user wants to send. @@ -183,7 +184,7 @@ class _VideoTrimmerState extends State { decoration: BoxDecoration( // Translucent rather than opaque: the frames being cut away stay // visible underneath, which is most of what makes a cut readable. - color: Colors.black.withAlpha(90), + color: context.appColor(AppColor.mediaScrim), borderRadius: BorderRadius.circular(16), ), child: Column( @@ -243,10 +244,15 @@ class _VideoTrimmerState extends State { : _format(_duration), style: TextStyle( fontSize: 11, - color: Colors.white.withAlpha(isTrimmed ? 235 : 150), + color: isTrimmed + ? context.appColor(AppColor.mediaForeground) + : context.appColor(AppColor.mediaForegroundMuted), fontFeatures: const [FontFeature.tabularFigures()], - shadows: const [ - Shadow(color: Color.fromARGB(122, 0, 0, 0), blurRadius: 4), + shadows: [ + Shadow( + color: context.appColor(AppColor.mediaScrim), + blurRadius: 4, + ), ], ), ), @@ -287,7 +293,9 @@ class _TrimTrack extends StatelessWidget { right: handleWidth, child: DecoratedBox( decoration: BoxDecoration( - color: Colors.white.withAlpha(46), + color: context + .appColor(AppColor.mediaForeground) + .withValues(alpha: 0.18), borderRadius: BorderRadius.circular(6), ), ), @@ -307,8 +315,14 @@ class _TrimTrack extends StatelessWidget { ), ), ), - _handle(left: startOffset), - _handle(left: endOffset + handleWidth), + _handle( + left: startOffset, + foregroundColor: context.appColor(AppColor.mediaForeground), + ), + _handle( + left: endOffset + handleWidth, + foregroundColor: context.appColor(AppColor.mediaForeground), + ), Positioned( left: playheadOffset + handleWidth - 1, top: 4, @@ -316,10 +330,13 @@ class _TrimTrack extends StatelessWidget { width: 3, child: DecoratedBox( decoration: BoxDecoration( - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), borderRadius: BorderRadius.circular(2), - boxShadow: const [ - BoxShadow(color: Color.fromARGB(122, 0, 0, 0), blurRadius: 4), + boxShadow: [ + BoxShadow( + color: context.appColor(AppColor.mediaScrim), + blurRadius: 4, + ), ], ), ), @@ -328,7 +345,7 @@ class _TrimTrack extends StatelessWidget { ); } - Widget _handle({required double left}) { + Widget _handle({required double left, required Color foregroundColor}) { return Positioned( left: left, top: 0, @@ -346,7 +363,7 @@ class _TrimTrack extends StatelessWidget { width: 2, height: 14, decoration: BoxDecoration( - color: Colors.white.withAlpha(220), + color: foregroundColor, borderRadius: BorderRadius.circular(1), ), ), diff --git a/lib/src/visual/views/chats/chat_list.view.dart b/lib/src/visual/views/chats/chat_list.view.dart index 82b8d104..a0810981 100644 --- a/lib/src/visual/views/chats/chat_list.view.dart +++ b/lib/src/visual/views/chats/chat_list.view.dart @@ -348,7 +348,6 @@ class _ChatListViewState extends State final plan = context.select( (p) => p.plan, ); - final dark = isDarkMode(context); return Scaffold( appBar: AppBar( title: Row( @@ -392,7 +391,7 @@ class _ChatListViewState extends State style: TextStyle( fontSize: 10, fontWeight: FontWeight.bold, - color: dark ? Colors.black : Colors.white, + color: context.color.onPrimary, ), ), ), @@ -421,11 +420,11 @@ class _ChatListViewState extends State ), Center( child: NotificationBadgeComp( - backgroundColor: dark ? Colors.white : Colors.black, - textColor: dark ? Colors.black : Colors.white, + backgroundColor: context.color.inverseSurface, + textColor: context.color.onInverseSurface, count: badgeCount.toString(), child: IconButton( - color: badgeCount > 0 ? Colors.black : null, + color: badgeCount > 0 ? context.color.onPrimary : null, key: searchForOtherUsers, icon: const FaIcon(FontAwesomeIcons.userPlus, size: 18), onPressed: () => context.push(Routes.chatsAddNewUser), @@ -555,12 +554,12 @@ class _ChatListViewState extends State FloatingActionButton( heroTag: 'qrcode_fab', elevation: 2, - backgroundColor: dark ? Colors.grey[800] : Colors.grey[200], - foregroundColor: dark ? Colors.white : Colors.black87, + backgroundColor: context.color.surfaceContainerHigh, + foregroundColor: context.color.onSurface, onPressed: () => context.push(Routes.settingsPublicProfile), child: FaIcon( FontAwesomeIcons.qrcode, - color: dark ? Colors.white : Colors.black87, + color: context.color.onSurface, ), ), const SizedBox(height: 12), @@ -568,11 +567,11 @@ class _ChatListViewState extends State heroTag: 'new_chat_fab', elevation: 2, backgroundColor: context.color.primary, - foregroundColor: Colors.black87, + foregroundColor: context.color.onPrimary, onPressed: () => context.push(Routes.chatsStartNewChat), - child: const FaIcon( + child: FaIcon( FontAwesomeIcons.penToSquare, - color: Colors.black87, + color: context.color.onPrimary, ), ), ], diff --git a/lib/src/visual/views/chats/chat_list_components/news_btn.comp.dart b/lib/src/visual/views/chats/chat_list_components/news_btn.comp.dart index 1fda75e3..8c768394 100644 --- a/lib/src/visual/views/chats/chat_list_components/news_btn.comp.dart +++ b/lib/src/visual/views/chats/chat_list_components/news_btn.comp.dart @@ -23,10 +23,10 @@ class NewsIconButtonComp extends StatelessWidget { return NotificationBadgeComp( count: count.toString(), backgroundColor: context.color.primary, - textColor: Colors.black87, + textColor: context.color.onPrimary, child: IconButton( onPressed: () => context.push(Routes.settingsHelpNews), - color: Colors.grey, + color: context.color.onSurfaceVariant, tooltip: context.lang.settingsHelpNews, icon: const FaIcon(FontAwesomeIcons.bullhorn, size: 19), ), diff --git a/lib/src/visual/views/chats/chat_list_components/typing_indicator_subtitle.comp.dart b/lib/src/visual/views/chats/chat_list_components/typing_indicator_subtitle.comp.dart index 673a5bce..af8b9826 100644 --- a/lib/src/visual/views/chats/chat_list_components/typing_indicator_subtitle.comp.dart +++ b/lib/src/visual/views/chats/chat_list_components/typing_indicator_subtitle.comp.dart @@ -77,7 +77,7 @@ class _TypingIndicatorSubtitleCompState child: Container( padding: const EdgeInsets.symmetric(vertical: 3), decoration: BoxDecoration( - color: getMessageColor(true), + color: getMessageColor(context, true), borderRadius: BorderRadius.circular(12), ), child: Transform.scale( diff --git a/lib/src/visual/views/chats/chat_messages.view.dart b/lib/src/visual/views/chats/chat_messages.view.dart index 0c059ffd..23bb82b0 100644 --- a/lib/src/visual/views/chats/chat_messages.view.dart +++ b/lib/src/visual/views/chats/chat_messages.view.dart @@ -21,7 +21,6 @@ import 'package:twonly/src/visual/components/avatar_icon.comp.dart'; import 'package:twonly/src/visual/components/contact_groups.comp.dart'; import 'package:twonly/src/visual/components/flame_counter.comp.dart'; import 'package:twonly/src/visual/components/verification_badge.comp.dart'; -import 'package:twonly/src/visual/themes/colors.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/animated_new_message.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/blink.component.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/chat_group_action.dart'; @@ -819,8 +818,10 @@ class _ChatMessagesViewState extends State } } -Color getMessageColor(bool isOther) { - return isOther ? DefaultColors.messageSelf : DefaultColors.messageOther; +Color getMessageColor(BuildContext context, bool isOther) { + return isOther + ? context.appColor(AppColor.chatBubbleReceived) + : context.appColor(AppColor.chatBubbleSent); } class ChatItem { diff --git a/lib/src/visual/views/chats/chat_messages_components/bottom_sheets/all_reactions.bottom_sheet.dart b/lib/src/visual/views/chats/chat_messages_components/bottom_sheets/all_reactions.bottom_sheet.dart index 5be6e09b..af254224 100644 --- a/lib/src/visual/views/chats/chat_messages_components/bottom_sheets/all_reactions.bottom_sheet.dart +++ b/lib/src/visual/views/chats/chat_messages_components/bottom_sheets/all_reactions.bottom_sheet.dart @@ -80,10 +80,10 @@ class _AllReactionsViewState extends State { topRight: Radius.circular(32), ), color: context.color.surface, - boxShadow: const [ + boxShadow: [ BoxShadow( blurRadius: 10.9, - color: Color.fromRGBO(0, 0, 0, 0.1), + color: context.color.shadow.withValues(alpha: 0.1), ), ], ), @@ -93,7 +93,7 @@ class _AllReactionsViewState extends State { margin: const EdgeInsets.all(30), decoration: BoxDecoration( borderRadius: BorderRadius.circular(32), - color: Colors.grey, + color: context.color.outline, ), height: 3, width: 60, diff --git a/lib/src/visual/views/chats/chat_messages_components/bottom_sheets/message_history.bottom_sheet.dart b/lib/src/visual/views/chats/chat_messages_components/bottom_sheets/message_history.bottom_sheet.dart index c33108fd..5893d1a2 100644 --- a/lib/src/visual/views/chats/chat_messages_components/bottom_sheets/message_history.bottom_sheet.dart +++ b/lib/src/visual/views/chats/chat_messages_components/bottom_sheets/message_history.bottom_sheet.dart @@ -30,10 +30,10 @@ class MessageHistoryView extends StatelessWidget { topRight: Radius.circular(32), ), color: context.color.surface, - boxShadow: const [ + boxShadow: [ BoxShadow( blurRadius: 10.9, - color: Color.fromRGBO(0, 0, 0, 0.1), + color: context.color.shadow.withValues(alpha: 0.1), ), ], ), @@ -43,7 +43,7 @@ class MessageHistoryView extends StatelessWidget { margin: const EdgeInsets.all(30), decoration: BoxDecoration( borderRadius: BorderRadius.circular(32), - color: Colors.grey, + color: context.color.outline, ), height: 3, width: 60, diff --git a/lib/src/visual/views/chats/chat_messages_components/bottom_sheets/share_additional.bottom_sheet.dart b/lib/src/visual/views/chats/chat_messages_components/bottom_sheets/share_additional.bottom_sheet.dart index 918d507e..aee2ae30 100644 --- a/lib/src/visual/views/chats/chat_messages_components/bottom_sheets/share_additional.bottom_sheet.dart +++ b/lib/src/visual/views/chats/chat_messages_components/bottom_sheets/share_additional.bottom_sheet.dart @@ -85,10 +85,10 @@ class _ShareAdditionalViewState extends State { topRight: Radius.circular(32), ), color: context.color.surface, - boxShadow: const [ + boxShadow: [ BoxShadow( blurRadius: 10.9, - color: Color.fromRGBO(0, 0, 0, 0.1), + color: context.color.shadow.withValues(alpha: 0.1), ), ], ), @@ -98,7 +98,7 @@ class _ShareAdditionalViewState extends State { margin: const EdgeInsets.only(top: 30), decoration: BoxDecoration( borderRadius: BorderRadius.circular(32), - color: Colors.grey, + color: context.color.outline, ), height: 3, width: 60, @@ -145,9 +145,7 @@ class _ShareAdditionalViewState extends State { color: context.color.surfaceContainer, borderRadius: BorderRadius.circular(12), ), - child: icon is IconData - ? Icon(icon) - : FaIcon(icon as FaIconData?), + child: icon is IconData ? Icon(icon) : FaIcon(icon as FaIconData?), ), const SizedBox(height: 8), Text(label, textAlign: TextAlign.center), diff --git a/lib/src/visual/views/chats/chat_messages_components/bottom_sheets/webxdc_store.bottom_sheet.dart b/lib/src/visual/views/chats/chat_messages_components/bottom_sheets/webxdc_store.bottom_sheet.dart index f7ce4c95..5bea12b4 100644 --- a/lib/src/visual/views/chats/chat_messages_components/bottom_sheets/webxdc_store.bottom_sheet.dart +++ b/lib/src/visual/views/chats/chat_messages_components/bottom_sheets/webxdc_store.bottom_sheet.dart @@ -109,7 +109,7 @@ class _WebxdcStoreViewState extends State { margin: const EdgeInsets.only(top: 12, bottom: 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(32), - color: Colors.grey, + color: context.color.outline, ), height: 3, width: 60, diff --git a/lib/src/visual/views/chats/chat_messages_components/chat_group_action.dart b/lib/src/visual/views/chats/chat_messages_components/chat_group_action.dart index a56d9b4a..b30b0dcf 100644 --- a/lib/src/visual/views/chats/chat_messages_components/chat_group_action.dart +++ b/lib/src/visual/views/chats/chat_messages_components/chat_group_action.dart @@ -161,13 +161,13 @@ class _ChatGroupActionState extends State { child: FaIcon( icon, size: 10, - color: Colors.grey, + color: context.color.onSurfaceVariant, ), ), const WidgetSpan(child: SizedBox(width: 8)), TextSpan( text: text, - style: const TextStyle(color: Colors.grey), + style: TextStyle(color: context.color.onSurfaceVariant), ), ], ), diff --git a/lib/src/visual/views/chats/chat_messages_components/chat_reaction_row.dart b/lib/src/visual/views/chats/chat_messages_components/chat_reaction_row.dart index 881f5543..87a07cf9 100644 --- a/lib/src/visual/views/chats/chat_messages_components/chat_reaction_row.dart +++ b/lib/src/visual/views/chats/chat_messages_components/chat_reaction_row.dart @@ -22,7 +22,7 @@ class ReactionRow extends StatelessWidget { // ignore: inference_failure_on_function_invocation await showModalBottomSheet( context: context, - backgroundColor: Colors.black, + backgroundColor: context.color.surface, builder: (context) { return AllReactionsView( message: message, @@ -103,9 +103,7 @@ class ReactionRow extends StatelessWidget { decoration: BoxDecoration( border: Border.all(), borderRadius: BorderRadius.circular(12), - color: isDarkMode(context) - ? const Color.fromARGB(255, 74, 74, 74) - : const Color.fromARGB(255, 197, 197, 197), + color: context.appColor(AppColor.reactionBackground), ), child: Row( children: [ @@ -119,9 +117,7 @@ class ReactionRow extends StatelessWidget { textAlign: TextAlign.center, style: TextStyle( fontSize: 13, - color: isDarkMode(context) - ? Colors.white - : Colors.black, + color: context.appColor(AppColor.onReaction), decoration: TextDecoration.none, fontWeight: FontWeight.normal, ), diff --git a/lib/src/visual/views/chats/chat_messages_components/entries/chat_audio_entry.dart b/lib/src/visual/views/chats/chat_messages_components/entries/chat_audio_entry.dart index 346f92fc..e5e95870 100644 --- a/lib/src/visual/views/chats/chat_messages_components/entries/chat_audio_entry.dart +++ b/lib/src/visual/views/chats/chat_messages_components/entries/chat_audio_entry.dart @@ -117,6 +117,7 @@ class _ChatAudioEntryState extends State { ? InChatAudioPlayer( path: mediaService.tempPath.path, message: message, + foregroundColor: info.textColor, trailing: messageTime, ) : Container()) @@ -137,12 +138,14 @@ class InChatAudioPlayer extends StatefulWidget { const InChatAudioPlayer({ required this.path, required this.message, + required this.foregroundColor, this.trailing, super.key, }); final String path; final Message message; + final Color foregroundColor; /// Shown below the waveform, next to the remaining playback time. final Widget? trailing; @@ -158,11 +161,14 @@ class _InChatAudioPlayerState extends State { static const double _cursorHeight = 20; static const double _playSlotWidth = 34; static const List _rates = [1, 1.5, 2, 0.5]; - static const _waveStyle = PlayerWaveStyle( + PlayerWaveStyle get _waveStyle => PlayerWaveStyle( spacing: 4, waveThickness: 2.5, scaleFactor: 80, showSeekLine: false, + fixedWaveColor: widget.foregroundColor.withValues(alpha: 0.45), + liveWaveColor: widget.foregroundColor, + backgroundColor: widget.foregroundColor.withAlpha(0), ); AudioPlayback? _playback; @@ -307,7 +313,7 @@ class _InChatAudioPlayerState extends State { ? FontAwesomeIcons.solidCirclePause : FontAwesomeIcons.solidCirclePlay, size: 28, - color: Colors.white, + color: widget.foregroundColor, ), ), ), @@ -347,7 +353,7 @@ class _InChatAudioPlayerState extends State { child: Container( width: _cursorWidth, decoration: BoxDecoration( - color: Colors.white, + color: widget.foregroundColor, borderRadius: BorderRadius.circular(2), ), ), @@ -367,8 +373,8 @@ class _InChatAudioPlayerState extends State { child: Text( formatMsToMinSec(remaining), textAlign: TextAlign.center, - style: const TextStyle( - color: Colors.white, + style: TextStyle( + color: widget.foregroundColor, fontSize: 10, ), ), diff --git a/lib/src/visual/views/chats/chat_messages_components/entries/chat_contacts.entry.dart b/lib/src/visual/views/chats/chat_messages_components/entries/chat_contacts.entry.dart index 7686359d..7aa5205f 100644 --- a/lib/src/visual/views/chats/chat_messages_components/entries/chat_contacts.entry.dart +++ b/lib/src/visual/views/chats/chat_messages_components/entries/chat_contacts.entry.dart @@ -95,12 +95,13 @@ class _ChatContactsEntryState extends State { if (i > 0) Divider( height: 1, - color: Colors.white.withValues(alpha: 0.2), + color: widget.info.textColor.withValues(alpha: 0.2), ), _ContactRow( contact: data.contacts[i], message: widget.message, contactsById: widget.contactsById, + foregroundColor: widget.info.textColor, ), ], ], @@ -114,11 +115,13 @@ class _ContactRow extends StatefulWidget { const _ContactRow({ required this.contact, required this.message, + required this.foregroundColor, this.contactsById, }); final SharedContact contact; final Message message; + final Color foregroundColor; final Map? contactsById; @override @@ -220,36 +223,36 @@ class _ContactRowState extends State<_ContactRow> { padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8), child: Row( children: [ - const FaIcon( + FaIcon( FontAwesomeIcons.user, - color: Colors.white, + color: widget.foregroundColor, size: 16, ), const SizedBox(width: 8), Flexible( child: BetterText( text: widget.contact.displayName, - textColor: Colors.white, + textColor: widget.foregroundColor, ), ), if (widget.message.senderId != null && !isAdded) ...[ const Spacer(), const SizedBox(width: 8), if (_isLoading) - const SizedBox( + SizedBox( width: 16, height: 16, child: CircularProgressIndicator.adaptive( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( - Colors.white, + widget.foregroundColor, ), ), ) else - const FaIcon( + FaIcon( FontAwesomeIcons.userPlus, - color: Colors.white, + color: widget.foregroundColor, size: 16, ), ], diff --git a/lib/src/visual/views/chats/chat_messages_components/entries/chat_date_chip.dart b/lib/src/visual/views/chats/chat_messages_components/entries/chat_date_chip.dart index 98b6e3fe..14817e58 100644 --- a/lib/src/visual/views/chats/chat_messages_components/entries/chat_date_chip.dart +++ b/lib/src/visual/views/chats/chat_messages_components/entries/chat_date_chip.dart @@ -31,9 +31,7 @@ class ChatDateChip extends StatelessWidget { return Center( child: Container( decoration: BoxDecoration( - color: isDarkMode(context) - ? const Color.fromARGB(255, 38, 38, 38) - : Colors.black.withAlpha(40), + color: context.color.surfaceContainerHigh, borderRadius: BorderRadius.circular(8), ), padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), @@ -43,7 +41,7 @@ class ChatDateChip extends StatelessWidget { textAlign: TextAlign.center, style: TextStyle( fontSize: 10, - color: isDarkMode(context) ? Colors.white : Colors.grey, + color: context.color.onSurfaceVariant, ), ), ), diff --git a/lib/src/visual/views/chats/chat_messages_components/entries/chat_flame_restored.entry.dart b/lib/src/visual/views/chats/chat_messages_components/entries/chat_flame_restored.entry.dart index dac7b1e9..3d24dda0 100644 --- a/lib/src/visual/views/chats/chat_messages_components/entries/chat_flame_restored.entry.dart +++ b/lib/src/visual/views/chats/chat_messages_components/entries/chat_flame_restored.entry.dart @@ -60,7 +60,7 @@ class ChatFlameRestoredEntry extends StatelessWidget { text: context.lang.chatEntryFlameRestored( data.restoredFlameCounter.toInt(), ), - textColor: isDarkMode(context) ? Colors.black : Colors.black, + textColor: info.textColor, ), ), ], diff --git a/lib/src/visual/views/chats/chat_messages_components/entries/chat_media_entry.dart b/lib/src/visual/views/chats/chat_messages_components/entries/chat_media_entry.dart index 07955caa..9e3fb7e7 100644 --- a/lib/src/visual/views/chats/chat_messages_components/entries/chat_media_entry.dart +++ b/lib/src/visual/views/chats/chat_messages_components/entries/chat_media_entry.dart @@ -14,9 +14,9 @@ import 'package:twonly/src/services/mediafiles/media_download_policy.dart' import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/elements/better_text.element.dart'; -import 'package:twonly/src/visual/themes/colors.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/common.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/in_chat_media_viewer.dart'; +import 'package:twonly/src/visual/views/chats/chat_messages_components/message_color.dart'; import 'package:twonly/src/visual/views/chats/media_viewer.view.dart'; class ChatMediaEntry extends StatefulWidget { diff --git a/lib/src/visual/views/chats/chat_messages_components/entries/chat_unknown.entry.dart b/lib/src/visual/views/chats/chat_messages_components/entries/chat_unknown.entry.dart index 93a1f5da..51d52a67 100644 --- a/lib/src/visual/views/chats/chat_messages_components/entries/chat_unknown.entry.dart +++ b/lib/src/visual/views/chats/chat_messages_components/entries/chat_unknown.entry.dart @@ -15,14 +15,12 @@ class ChatUnknownEntry extends StatelessWidget { ), padding: const EdgeInsets.only(left: 10, top: 6, bottom: 6, right: 10), decoration: BoxDecoration( - color: isDarkMode(context) ? Colors.black : Colors.grey, + color: context.appColor(AppColor.chatBubbleDeleted), borderRadius: BorderRadius.circular(12), ), child: BetterText( text: context.lang.updateTwonlyMessage, - textColor: isDarkMode(context) - ? const Color.fromARGB(255, 99, 99, 99) - : Colors.black, + textColor: context.appColor(AppColor.onChatBubbleDeleted), ), ); } diff --git a/lib/src/visual/views/chats/chat_messages_components/entries/common.dart b/lib/src/visual/views/chats/chat_messages_components/entries/common.dart index 0055a4a1..9e9b895f 100644 --- a/lib/src/visual/views/chats/chat_messages_components/entries/common.dart +++ b/lib/src/visual/views/chats/chat_messages_components/entries/common.dart @@ -28,8 +28,10 @@ BubbleInfo getBubbleInfo( ) { final info = BubbleInfo() ..text = message.content ?? '' - ..textColor = Colors.white - ..color = getMessageColor(message.senderId != null) + ..textColor = message.senderId != null + ? context.appColor(AppColor.onChatBubbleReceived) + : context.appColor(AppColor.onChatBubbleSent) + ..color = getMessageColor(context, message.senderId != null) ..displayTime = !combineTextMessageWithNext(message, nextMessage) ..displayUserName = '' ..minWidth = minWidth @@ -61,7 +63,7 @@ BubbleInfo getBubbleInfo( ..expanded = false ..color = message.quotesMessageId != null ? Colors.transparent - : getMessageColor(message.senderId != null); + : getMessageColor(context, message.senderId != null); if (message.isDeletedFromSender) { info ..color = context.color.surfaceBright @@ -73,10 +75,8 @@ BubbleInfo getBubbleInfo( if (message.isDeletedFromSender) { info ..text = context.lang.messageWasDeleted - ..color = isDarkMode(context) ? Colors.black : Colors.grey; - if (isDarkMode(context)) { - info.textColor = const Color.fromARGB(255, 99, 99, 99); - } + ..color = context.appColor(AppColor.chatBubbleDeleted) + ..textColor = context.appColor(AppColor.onChatBubbleDeleted); } return info; } diff --git a/lib/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart b/lib/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart index 8fd866f8..2d8dbe62 100644 --- a/lib/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart +++ b/lib/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart @@ -20,7 +20,10 @@ class FriendlyMessageTime extends StatelessWidget { @override Widget build(BuildContext context) { - final statusIcon = _buildStatusIcon(context, Colors.grey.shade400); + final statusIcon = _buildStatusIcon( + context, + context.appColor(AppColor.chatMessageMeta), + ); return Padding( padding: const EdgeInsets.only(left: 6), @@ -34,7 +37,7 @@ class FriendlyMessageTime extends StatelessWidget { height: 10, child: FaIcon( FontAwesomeIcons.pencil, - color: color ?? Colors.white.withAlpha(150), + color: color ?? context.appColor(AppColor.chatMessageMeta), size: 10, ), ), @@ -48,7 +51,7 @@ class FriendlyMessageTime extends StatelessWidget { ), style: TextStyle( fontSize: 10, - color: color ?? Colors.white.withAlpha(150), + color: color ?? context.appColor(AppColor.chatMessageMeta), decoration: TextDecoration.none, fontWeight: FontWeight.normal, ), diff --git a/lib/src/visual/views/chats/chat_messages_components/in_chat_media_viewer.dart b/lib/src/visual/views/chats/chat_messages_components/in_chat_media_viewer.dart index 1884dfd7..649e8e44 100644 --- a/lib/src/visual/views/chats/chat_messages_components/in_chat_media_viewer.dart +++ b/lib/src/visual/views/chats/chat_messages_components/in_chat_media_viewer.dart @@ -145,10 +145,7 @@ class _InChatMediaViewerState extends State { minHeight: 39, ), decoration: BoxDecoration( - color: widget.info.color.withValues(alpha: 0.3), - border: Border.all( - color: widget.info.color.withValues(alpha: 0.4), - ), + color: widget.info.color, borderRadius: widget.borderRadius, ), child: Padding( @@ -166,9 +163,7 @@ class _InChatMediaViewerState extends State { if (widget.info.displayTime || widget.message.modifiedAt != null) FriendlyMessageTime( message: widget.message, - color: isDarkMode(context) - ? Colors.white.withAlpha(100) - : Colors.black.withAlpha(100), + color: context.appColor(AppColor.chatMessageMeta), ), ], ), diff --git a/lib/src/visual/views/chats/chat_messages_components/message_color.dart b/lib/src/visual/views/chats/chat_messages_components/message_color.dart new file mode 100644 index 00000000..3b0f6f37 --- /dev/null +++ b/lib/src/visual/views/chats/chat_messages_components/message_color.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:twonly/src/database/tables/mediafiles.table.dart'; +import 'package:twonly/src/database/tables/messages.table.dart'; +import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/utils/misc.dart'; + +Color getMessageColorFromType( + Message message, + MediaFile? mediaFile, + BuildContext context, +) { + if (message.type == MessageType.restoreFlameCounter.name) { + return context.appColor(AppColor.messageRestore); + } + if (message.type == MessageType.text.name) { + return context.appColor(AppColor.messageText); + } + if (message.isWidgetMedia) { + return context.appColor(AppColor.messageWidget); + } + if (mediaFile == null) return context.color.onSurface; + if (mediaFile.requiresAuthentication) return context.color.primary; + return switch (mediaFile.type) { + MediaType.video => context.appColor(AppColor.messageVideo), + MediaType.audio => context.appColor(AppColor.messageAudio), + _ => context.appColor(AppColor.messageImage), + }; +} diff --git a/lib/src/visual/views/chats/chat_messages_components/message_context_menu.dart b/lib/src/visual/views/chats/chat_messages_components/message_context_menu.dart index 4af04851..818cb85a 100644 --- a/lib/src/visual/views/chats/chat_messages_components/message_context_menu.dart +++ b/lib/src/visual/views/chats/chat_messages_components/message_context_menu.dart @@ -116,7 +116,7 @@ class MessageContextMenu extends StatelessWidget { final layer = await showModalBottomSheet( context: navigator.context, - backgroundColor: Colors.black, + backgroundColor: context.color.surface, builder: (context) { return const EmojiPickerBottom(); }, diff --git a/lib/src/visual/views/chats/chat_messages_components/message_input.dart b/lib/src/visual/views/chats/chat_messages_components/message_input.dart index b6abe258..99656ff7 100644 --- a/lib/src/visual/views/chats/chat_messages_components/message_input.dart +++ b/lib/src/visual/views/chats/chat_messages_components/message_input.dart @@ -324,7 +324,7 @@ class _MessageInputState extends State // ignore: inference_failure_on_function_invocation await showModalBottomSheet( context: context, - backgroundColor: Colors.black, + backgroundColor: context.color.surface, builder: (context) { return ShareAdditionalView( group: widget.group, @@ -457,10 +457,12 @@ class _MessageInputState extends State ), child: FadeTransition( opacity: _recordingBlink, - child: const FaIcon( + child: FaIcon( FontAwesomeIcons.microphone, size: 20, - color: Colors.red, + color: context.appColor( + AppColor.recording, + ), ), ), ), @@ -470,9 +472,7 @@ class _MessageInputState extends State _currentDuration, ), style: TextStyle( - color: isDarkMode(context) - ? Colors.white - : Colors.black, + color: context.color.onSurface, fontSize: 12, ), ), @@ -491,8 +491,10 @@ class _MessageInputState extends State onTap: _cancelAudioRecording, child: Text( context.lang.voiceMessageCancel, - style: const TextStyle( - color: Colors.red, + style: TextStyle( + color: context.appColor( + AppColor.recording, + ), ), ), ), @@ -591,9 +593,7 @@ class _MessageInputState extends State borderRadius: BorderRadius.circular( 90, ), - color: isDarkMode(context) - ? Colors.black - : Colors.white, + color: context.color.surface, ), child: const Center( child: Column( @@ -622,7 +622,9 @@ class _MessageInputState extends State right: -20, child: Container( decoration: BoxDecoration( - color: Colors.red, + color: context.appColor( + AppColor.recording, + ), borderRadius: BorderRadius.circular(90), ), width: 60, @@ -644,7 +646,7 @@ class _MessageInputState extends State color: (_recordingState == RecordingState.recording) - ? Colors.white + ? context.color.onError : null, (_recordingState == RecordingState.none) ? FontAwesomeIcons.microphone @@ -712,11 +714,11 @@ class _MessageInputState extends State ), searchViewConfig: SearchViewConfig( backgroundColor: context.color.surfaceContainer, - buttonIconColor: Colors.white, + buttonIconColor: context.color.onSurface, ), categoryViewConfig: CategoryViewConfig( backgroundColor: context.color.surfaceContainer, - dividerColor: Colors.white, + dividerColor: context.color.outlineVariant, indicatorColor: context.color.primary, iconColorSelected: context.color.primary, iconColor: context.color.secondary, diff --git a/lib/src/visual/views/chats/chat_messages_components/message_send_state_icon.dart b/lib/src/visual/views/chats/chat_messages_components/message_send_state_icon.dart index 4f458031..c1454e02 100644 --- a/lib/src/visual/views/chats/chat_messages_components/message_send_state_icon.dart +++ b/lib/src/visual/views/chats/chat_messages_components/message_send_state_icon.dart @@ -11,9 +11,9 @@ import 'package:twonly/src/database/tables/messages.table.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/components/animate_icon.comp.dart'; -import 'package:twonly/src/visual/themes/colors.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/file_limit_reached.dialog.dart'; +import 'package:twonly/src/visual/views/chats/chat_messages_components/message_color.dart'; enum MessageSendState { received, @@ -310,7 +310,11 @@ class _MessageSendStateIconState extends State { widget.group!.draftMessage != null && widget.group!.draftMessage != '') { icons = [ - const FaIcon(FontAwesomeIcons.pen, size: 12, color: Colors.grey), + FaIcon( + FontAwesomeIcons.pen, + size: 12, + color: context.color.onSurfaceVariant, + ), ]; textWidget = Text( '${context.lang.draftMessage}: ${substringBy(widget.group!.draftMessage!, 10)}', diff --git a/lib/src/visual/views/chats/chat_messages_components/response_container.dart b/lib/src/visual/views/chats/chat_messages_components/response_container.dart index d793f8cf..367e1141 100644 --- a/lib/src/visual/views/chats/chat_messages_components/response_container.dart +++ b/lib/src/visual/views/chats/chat_messages_components/response_container.dart @@ -54,7 +54,7 @@ class ResponseContainer extends StatelessWidget { maxWidth: MediaQuery.sizeOf(context).width * 0.8, ), decoration: BoxDecoration( - color: getMessageColor(msg.senderId != null), + color: getMessageColor(context, msg.senderId != null), borderRadius: borderRadius, ), child: IntrinsicWidth( @@ -187,7 +187,7 @@ class _ResponsePreviewState extends State { @override Widget build(BuildContext context) { String? subtitle; - var color = const Color.fromARGB(233, 68, 137, 255); + var color = context.appColor(AppColor.messageText); if (_message != null) { if (_message!.type == MessageType.text.name) { @@ -238,7 +238,7 @@ class _ResponsePreviewState extends State { subtitle = _storyAlive ? context.lang.story : context.lang.storyExpired; } - color = getMessageColor(_message!.senderId != null); + color = getMessageColor(context, _message!.senderId != null); } final hasImage = diff --git a/lib/src/visual/views/chats/chat_messages_components/typing_indicator.dart b/lib/src/visual/views/chats/chat_messages_components/typing_indicator.dart index 925b5648..a939c2dc 100644 --- a/lib/src/visual/views/chats/chat_messages_components/typing_indicator.dart +++ b/lib/src/visual/views/chats/chat_messages_components/typing_indicator.dart @@ -6,6 +6,7 @@ import 'package:go_router/go_router.dart'; import 'package:twonly/locator.dart'; import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/components/avatar_icon.comp.dart'; import 'package:twonly/src/visual/views/chats/chat_messages.view.dart'; @@ -131,7 +132,7 @@ class _TypingIndicatorState extends State { vertical: 6, ), decoration: BoxDecoration( - color: getMessageColor(true), + color: getMessageColor(context, true), borderRadius: BorderRadius.circular(12), ), child: AnimatedTypingDots( @@ -246,8 +247,8 @@ class _AnimatedDot extends AnimatedWidget { child: Container( width: 8, height: 8, - decoration: const BoxDecoration( - color: Colors.white, + decoration: BoxDecoration( + color: context.appColor(AppColor.onChatBubbleReceived), shape: BoxShape.circle, ), ), diff --git a/lib/src/visual/views/chats/media_viewer.view.dart b/lib/src/visual/views/chats/media_viewer.view.dart index bcacc0d7..bfe6e2f1 100644 --- a/lib/src/visual/views/chats/media_viewer.view.dart +++ b/lib/src/visual/views/chats/media_viewer.view.dart @@ -870,10 +870,10 @@ class _MediaViewerViewState extends State { fontWeight: FontWeight.bold, color: showSendTextMessageInput ? null - : const Color.fromARGB(255, 126, 126, 126), - shadows: const [ + : context.appColor(AppColor.mediaForegroundMuted), + shadows: [ Shadow( - color: Color.fromARGB(122, 0, 0, 0), + color: context.appColor(AppColor.mediaScrim), blurRadius: 5, ), ], diff --git a/lib/src/visual/views/chats/media_viewer_components/media_content_renderer.comp.dart b/lib/src/visual/views/chats/media_viewer_components/media_content_renderer.comp.dart index 5734011a..c4719f22 100644 --- a/lib/src/visual/views/chats/media_viewer_components/media_content_renderer.comp.dart +++ b/lib/src/visual/views/chats/media_viewer_components/media_content_renderer.comp.dart @@ -3,6 +3,7 @@ import 'package:photo_view/photo_view.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart' show MediaType; import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:video_player/video_player.dart'; class MediaContentRenderer extends StatelessWidget { @@ -47,10 +48,10 @@ class MediaContentRenderer extends StatelessWidget { initialScale: PhotoViewComputedScale.contained, minScale: PhotoViewComputedScale.contained, errorBuilder: (context, error, stackTrace) { - return const Center( + return Center( child: Icon( Icons.broken_image_outlined, - color: Colors.white38, + color: context.appColor(AppColor.mediaForegroundMuted), size: 64, ), ); diff --git a/lib/src/visual/views/chats/media_viewer_components/own_story_bottom_bar.comp.dart b/lib/src/visual/views/chats/media_viewer_components/own_story_bottom_bar.comp.dart index 2a15aee9..f9b9fe3b 100644 --- a/lib/src/visual/views/chats/media_viewer_components/own_story_bottom_bar.comp.dart +++ b/lib/src/visual/views/chats/media_viewer_components/own_story_bottom_bar.comp.dart @@ -3,6 +3,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:twonly/locator.dart'; import 'package:twonly/src/database/daos/stories.dao.dart'; import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/elements/my_icon_button.element.dart'; import 'package:twonly/src/visual/views/chats/media_viewer_components/story_viewers.bottom_sheet.dart'; @@ -23,7 +24,7 @@ class OwnStoryBottomBar extends StatelessWidget { Future _showViewers(BuildContext context, MediaFile media) async { await showModalBottomSheet( context: context, - backgroundColor: Colors.black, + backgroundColor: context.appColor(AppColor.mediaBackground), builder: (context) => StoryViewersBottomSheet(mediaFile: media), ); } diff --git a/lib/src/visual/views/chats/media_viewer_components/reaction_buttons.comp.dart b/lib/src/visual/views/chats/media_viewer_components/reaction_buttons.comp.dart index 106e5b9f..4aaf8e8a 100644 --- a/lib/src/visual/views/chats/media_viewer_components/reaction_buttons.comp.dart +++ b/lib/src/visual/views/chats/media_viewer_components/reaction_buttons.comp.dart @@ -283,7 +283,10 @@ class EmojiFloatWidgetState extends State Widget build(BuildContext context) { return IgnorePointer( child: CustomPaint( - painter: _ParticlePainter(List<_Particle>.from(_particles)), + painter: _ParticlePainter( + List<_Particle>.from(_particles), + context.appColor(AppColor.mediaBackground), + ), size: Size.infinite, ), ); @@ -339,8 +342,9 @@ class _Particle { } class _ParticlePainter extends CustomPainter { - _ParticlePainter(this.particles); + _ParticlePainter(this.particles, this.particleColor); final List<_Particle> particles; + final Color particleColor; static final _textPainter = TextPainter(textDirection: TextDirection.ltr); @@ -351,7 +355,7 @@ class _ParticlePainter extends CustomPainter { text: p.emoji, style: TextStyle( fontSize: 24 * p.currentScale, - color: Colors.black.withValues(alpha: p.opacity), + color: particleColor.withValues(alpha: p.opacity), fontFamily: Platform.isAndroid ? 'sans-serif' : null, fontFamilyFallback: Platform.isAndroid ? const ['NotoColorEmoji'] diff --git a/lib/src/visual/views/chats/media_viewer_components/story_viewers.bottom_sheet.dart b/lib/src/visual/views/chats/media_viewer_components/story_viewers.bottom_sheet.dart index f375e8e5..eb5e6935 100644 --- a/lib/src/visual/views/chats/media_viewer_components/story_viewers.bottom_sheet.dart +++ b/lib/src/visual/views/chats/media_viewer_components/story_viewers.bottom_sheet.dart @@ -94,10 +94,10 @@ class _StoryViewersBottomSheetState extends State { topRight: Radius.circular(32), ), color: context.color.surface, - boxShadow: const [ + boxShadow: [ BoxShadow( blurRadius: 10.9, - color: Color.fromRGBO(0, 0, 0, 0.1), + color: context.color.shadow.withValues(alpha: 0.1), ), ], ), @@ -107,7 +107,7 @@ class _StoryViewersBottomSheetState extends State { margin: const EdgeInsets.only(top: 20, bottom: 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(32), - color: Colors.grey, + color: context.color.outline, ), height: 3, width: 60, diff --git a/lib/src/visual/views/chats/message_info.view.dart b/lib/src/visual/views/chats/message_info.view.dart index ffbc7338..3539eee9 100644 --- a/lib/src/visual/views/chats/message_info.view.dart +++ b/lib/src/visual/views/chats/message_info.view.dart @@ -238,7 +238,7 @@ class _MessageInfoViewState extends State { // ignore: inference_failure_on_function_invocation await showModalBottomSheet( context: context, - backgroundColor: Colors.black, + backgroundColor: context.color.surface, builder: (context) { return MessageHistoryView( message: widget.message, diff --git a/lib/src/visual/views/chats/start_new_chat.view.dart b/lib/src/visual/views/chats/start_new_chat.view.dart index 10c9cc90..fbdf8595 100644 --- a/lib/src/visual/views/chats/start_new_chat.view.dart +++ b/lib/src/visual/views/chats/start_new_chat.view.dart @@ -17,7 +17,7 @@ import 'package:twonly/src/visual/components/flame_counter.comp.dart'; import 'package:twonly/src/visual/components/verification_badge.comp.dart'; import 'package:twonly/src/visual/context_menu/group.context_menu.dart'; import 'package:twonly/src/visual/context_menu/user.context_menu.dart'; -import 'package:twonly/src/visual/decorations/input_text.decoration.dart'; +import 'package:twonly/src/visual/elements/my_input.element.dart'; import 'package:twonly/src/visual/views/chats/chat_messages.view.dart'; class StartNewChatView extends StatefulWidget { @@ -65,6 +65,7 @@ class _StartNewChatView extends State { void dispose() { allNonDirectGroupsSub.cancel(); contactSub.cancel(); + searchUserName.dispose(); super.dispose(); } @@ -142,22 +143,20 @@ class _StartNewChatView extends State { padding: const EdgeInsets.only( bottom: 40, left: 10, - top: 20, + top: 0, right: 10, ), child: Column( children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 10), - child: TextField( - onChanged: (_) async { - await filterUsers(); - }, + child: MyInput( controller: searchUserName, - decoration: getInputDecoration( - context, - context.lang.startNewChatSearchHint, - ), + dense: true, + fontWeight: FontWeight.normal, + hintText: context.lang.startNewChatSearchHint, + prefixIcon: const Icon(Icons.search, size: 20), + onChanged: (_) => unawaited(filterUsers()), ), ), const SizedBox(height: 10), @@ -171,10 +170,13 @@ class _StartNewChatView extends State { if (i == 0) { return ListTile( title: Text(context.lang.newGroup), - leading: const CircleAvatar( - child: FaIcon( - FontAwesomeIcons.userGroup, - size: 13, + leading: const SizedBox.square( + dimension: 26, + child: Center( + child: FaIcon( + FontAwesomeIcons.userGroup, + size: 13, + ), ), ), onTap: () => context.push( @@ -185,10 +187,13 @@ class _StartNewChatView extends State { if (i == 1) { return ListTile( title: Text(context.lang.startNewChatNewContact), - leading: const CircleAvatar( - child: FaIcon( - FontAwesomeIcons.userPlus, - size: 13, + leading: const SizedBox.square( + dimension: 26, + child: Center( + child: FaIcon( + FontAwesomeIcons.userPlus, + size: 13, + ), ), ), onTap: () => context.push(Routes.chatsAddNewUser), diff --git a/lib/src/visual/views/contact/add_new_contact_components/open_requests_list.comp.dart b/lib/src/visual/views/contact/add_new_contact_components/open_requests_list.comp.dart index be2778e0..9b9d1938 100644 --- a/lib/src/visual/views/contact/add_new_contact_components/open_requests_list.comp.dart +++ b/lib/src/visual/views/contact/add_new_contact_components/open_requests_list.comp.dart @@ -52,9 +52,9 @@ class OpenRequestsListComp extends StatelessWidget { ).merge(secondaryGreyButtonStyle(context)), child: Row( children: [ - const Icon( + Icon( Icons.person_off_rounded, - color: Color.fromARGB(164, 244, 67, 54), + color: context.color.error, ), Text( context.lang.contactActionBlock, @@ -89,7 +89,7 @@ class OpenRequestsListComp extends StatelessWidget { ).merge(secondaryGreyButtonStyle(context)), child: Row( children: [ - const Icon(Icons.check, color: Colors.green), + Icon(Icons.check, color: context.appColor(AppColor.success)), Text( context.lang.contactActionAccept, style: const TextStyle(fontSize: 10), diff --git a/lib/src/visual/views/contact/contact.view.dart b/lib/src/visual/views/contact/contact.view.dart index c0d64d43..7ae43b05 100644 --- a/lib/src/visual/views/contact/contact.view.dart +++ b/lib/src/visual/views/contact/contact.view.dart @@ -21,7 +21,6 @@ import 'package:twonly/src/visual/components/verification_badge.comp.dart'; import 'package:twonly/src/visual/elements/better_list_title.element.dart'; import 'package:twonly/src/visual/views/contact/contact_components/mutual_groups_expansion_tile.comp.dart'; import 'package:twonly/src/visual/views/contact/contact_components/restore_flame.comp.dart'; -import 'package:twonly/src/visual/views/contact/contact_components/user_discovery_contact_settings.comp.dart'; import 'package:twonly/src/visual/views/contact/contact_components/verification_expansion_tile.comp.dart'; import 'package:twonly/src/visual/views/contact/select_contact_groups.view.dart'; import 'package:twonly/src/visual/views/groups/group.view.dart'; @@ -301,9 +300,6 @@ class _ContactViewState extends State { MutualGroupsExpansionTileComp( contact: contact, ), - UserDiscoveryContactSettingsComp( - contact: contact, - ), const Divider(), BetterListTile( icon: FontAwesomeIcons.flag, @@ -331,7 +327,7 @@ class _ContactViewState extends State { BetterListTile( icon: FontAwesomeIcons.userMinus, iconSize: 16, - color: Colors.red, + color: context.color.error, text: context.lang.contactRemove, onTap: () => handleUserRemoveRequest(contact), ), @@ -382,12 +378,16 @@ class _ContactViewState extends State { SignalVersion.v1 => 'Signal Protocol (v1)', SignalVersion.v2 => 'PQXDH (v2)', }}', - style: const TextStyle(color: Colors.grey), + style: TextStyle( + color: context.color.onSurfaceVariant, + ), ), const SizedBox(height: 4), Text( 'Can send widgets: ${contact.widgetSharingAllowed ? 'Yes' : 'No'}', - style: const TextStyle(color: Colors.grey), + style: TextStyle( + color: context.color.onSurfaceVariant, + ), ), ], ), diff --git a/lib/src/visual/views/contact/contact_components/user_discovery_contact_settings.comp.dart b/lib/src/visual/views/contact/contact_components/user_discovery_contact_settings.comp.dart deleted file mode 100644 index 783f0c7c..00000000 --- a/lib/src/visual/views/contact/contact_components/user_discovery_contact_settings.comp.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:drift/drift.dart' hide Column; -import 'package:flutter/material.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:twonly/locator.dart'; -import 'package:twonly/src/database/daos/contacts.dao.dart'; -import 'package:twonly/src/database/twonly.db.dart'; -import 'package:twonly/src/services/user_discovery.service.dart'; -import 'package:twonly/src/utils/misc.dart'; -import 'package:twonly/src/visual/elements/better_list_title.element.dart'; -import 'package:twonly/src/visual/views/settings/privacy/user_discovery.view.dart'; - -class UserDiscoveryContactSettingsComp extends StatelessWidget { - const UserDiscoveryContactSettingsComp({ - required this.contact, - super.key, - }); - - final Contact contact; - - @override - Widget build(BuildContext context) { - if (!userService.currentUser.isUserDiscoveryEnabled) { - return const SizedBox.shrink(); - } - - if (userService.currentUser.userDiscoveryRequiresManualApproval && - contact.userDiscoveryManualApproved != true) { - return BetterListTile( - icon: FontAwesomeIcons.usersViewfinder, - text: context.lang.userDiscoverySettingsTitle, - subtitle: Text( - context.lang.contactUserDiscoveryManualApprovalPending, - style: const TextStyle(fontSize: 10), - ), - trailing: TextButton( - onPressed: () async { - await twonlyDB.contactsDao.updateContact( - contact.userId, - const ContactsCompanion( - userDiscoveryManualApproved: Value(true), - ), - ); - }, - child: Text( - context.lang.contactUserDiscoveryManualApprovalApprove, - ), - ), - ); - } - - return BetterListTile( - icon: FontAwesomeIcons.usersViewfinder, - text: context.lang.userDiscoverySettingsTitle, - onTap: () => context.navPush(const UserDiscoverySettingsView()), - subtitle: - !contact.userDiscoveryExcluded && - contact.mediaSendCounter < - userService.currentUser.requiredSendImages - ? Text( - context.lang.contactUserDiscoveryImagesLeft( - userService.currentUser.requiredSendImages - - contact.mediaSendCounter, - getContactDisplayName(contact), - ), - style: const TextStyle(fontSize: 9), - ) - : null, - trailing: Transform.scale( - scale: 0.8, - child: Switch.adaptive( - value: !contact.userDiscoveryExcluded, - onChanged: (a) async { - await UserDiscoveryService.changeExclusionForContact( - contact.userId, - !a, - ); - }, - ), - ), - ); - } -} diff --git a/lib/src/visual/views/contact/contact_group_settings.view.dart b/lib/src/visual/views/contact/contact_group_settings.view.dart index fa05b871..08c548dc 100644 --- a/lib/src/visual/views/contact/contact_group_settings.view.dart +++ b/lib/src/visual/views/contact/contact_group_settings.view.dart @@ -187,7 +187,7 @@ class _ContactGroupSettingsViewState extends State { Future _selectEmoji() async { final result = await showModalBottomSheet( context: context, - backgroundColor: Colors.black, + backgroundColor: context.color.surface, builder: (context) => const EmojiPickerBottom(), ); if (result is EmojiLayerData && mounted) { @@ -330,9 +330,7 @@ class _ContactGroupSettingsViewState extends State { color: color == noContactGroupBackgroundColor ? null : Color(color), shape: BoxShape.circle, border: Border.all( - color: selected - ? (isDarkMode(context) ? Colors.white : Colors.black) - : Theme.of(context).colorScheme.outline, + color: selected ? context.color.onSurface : context.color.outline, width: selected ? 3 : 1, ), ), @@ -431,10 +429,10 @@ class _ContactGroupSettingsViewState extends State { IconButton( tooltip: context.lang.deleteContactGroup, onPressed: _delete, - icon: const FaIcon( + icon: FaIcon( FontAwesomeIcons.trashCan, size: 18, - color: Colors.red, + color: context.color.error, ), ), ], diff --git a/lib/src/visual/views/critical_error.view.dart b/lib/src/visual/views/critical_error.view.dart index c847514f..ee45b7c6 100644 --- a/lib/src/visual/views/critical_error.view.dart +++ b/lib/src/visual/views/critical_error.view.dart @@ -17,10 +17,10 @@ class CriticalErrorView extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const FaIcon( + FaIcon( FontAwesomeIcons.triangleExclamation, size: 80, - color: Colors.redAccent, + color: context.color.error, ), const SizedBox(height: 24), Text( diff --git a/lib/src/visual/views/groups/group.view.dart b/lib/src/visual/views/groups/group.view.dart index 2b7bef4e..8ff743d7 100644 --- a/lib/src/visual/views/groups/group.view.dart +++ b/lib/src/visual/views/groups/group.view.dart @@ -322,7 +322,7 @@ class _GroupViewState extends State { if (!_group!.leftGroup) BetterListTile( icon: FontAwesomeIcons.rightFromBracket, - color: Colors.red, + color: context.color.error, text: context.lang.leaveGroup, onTap: _leaveGroup, ) diff --git a/lib/src/visual/views/memories/components/cloud_backup_promo.comp.dart b/lib/src/visual/views/memories/components/cloud_backup_promo.comp.dart index fbc50e7c..9bf3e506 100644 --- a/lib/src/visual/views/memories/components/cloud_backup_promo.comp.dart +++ b/lib/src/visual/views/memories/components/cloud_backup_promo.comp.dart @@ -42,7 +42,7 @@ class MemoriesCloudBackupPromoComp extends StatelessWidget { borderRadius: BorderRadius.circular(24), boxShadow: [ BoxShadow( - color: Colors.black.withValues(alpha: 0.05), + color: context.color.shadow.withValues(alpha: 0.05), blurRadius: 10, offset: const Offset(0, 4), ), diff --git a/lib/src/visual/views/memories/components/flashback_banner.comp.dart b/lib/src/visual/views/memories/components/flashback_banner.comp.dart index 2d5b8a54..f20ad409 100644 --- a/lib/src/visual/views/memories/components/flashback_banner.comp.dart +++ b/lib/src/visual/views/memories/components/flashback_banner.comp.dart @@ -51,11 +51,11 @@ class MemoriesFlashbackBannerComp extends StatelessWidget { width: 120, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), - boxShadow: const [ + boxShadow: [ BoxShadow( - color: Colors.black12, + color: context.color.shadow.withValues(alpha: 0.12), blurRadius: 6, - offset: Offset(0, 2), + offset: const Offset(0, 2), ), ], ), @@ -68,11 +68,15 @@ class MemoriesFlashbackBannerComp extends StatelessWidget { fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return ColoredBox( - color: Colors.grey.shade800, - child: const Center( + color: context.appColor( + AppColor.mediaBackground, + ), + child: Center( child: Icon( Icons.broken_image_outlined, - color: Colors.white30, + color: context.appColor( + AppColor.mediaForegroundMuted, + ), size: 32, ), ), @@ -86,7 +90,7 @@ class MemoriesFlashbackBannerComp extends StatelessWidget { begin: Alignment.bottomCenter, end: Alignment.center, colors: [ - Colors.black.withValues(alpha: 0.7), + context.appColor(AppColor.mediaScrim), Colors.transparent, ], ), @@ -100,8 +104,10 @@ class MemoriesFlashbackBannerComp extends StatelessWidget { child: Text( text, textAlign: TextAlign.center, - style: const TextStyle( - color: Colors.white, + style: TextStyle( + color: context.appColor( + AppColor.mediaForeground, + ), fontWeight: FontWeight.bold, fontSize: 14, ), diff --git a/lib/src/visual/views/memories/components/memory_thumbnail.comp.dart b/lib/src/visual/views/memories/components/memory_thumbnail.comp.dart index c703ba40..86c39561 100644 --- a/lib/src/visual/views/memories/components/memory_thumbnail.comp.dart +++ b/lib/src/visual/views/memories/components/memory_thumbnail.comp.dart @@ -8,6 +8,7 @@ import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/model/memory_item.model.dart'; import 'package:twonly/src/services/memories/memories_cloud.service.dart'; import 'package:twonly/src/utils/log.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/components/selectable_thumbnail.comp.dart'; import 'package:twonly/src/visual/views/memories/components/memory_transition_painter.dart'; @@ -234,11 +235,11 @@ class _MemoriesThumbnailCompState extends State { } Log.warn(error); return ColoredBox( - color: Colors.grey.shade200, - child: const Center( + color: context.color.surfaceContainerHigh, + child: Center( child: FaIcon( FontAwesomeIcons.image, - color: Colors.black26, + color: context.color.onSurfaceVariant, ), ), ); @@ -251,37 +252,43 @@ class _MemoriesThumbnailCompState extends State { ) else ColoredBox( - color: Colors.grey.shade200, - child: const Center( + color: context.color.surfaceContainerHigh, + child: Center( child: FaIcon( FontAwesomeIcons.image, - color: Colors.black26, + color: context.color.onSurfaceVariant, ), ), ), if (isVideo) - const Positioned.fill( + Positioned.fill( child: Center( child: FaIcon( FontAwesomeIcons.circlePlay, - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), size: 32, shadows: [ - Shadow(color: Colors.black54, blurRadius: 6), + Shadow( + color: context.appColor(AppColor.mediaScrim), + blurRadius: 6, + ), ], ), ), ), if (media.mediaFile.isFavorite) - const Positioned( + Positioned( bottom: 6, left: 6, child: Icon( Icons.favorite, - color: Colors.redAccent, + color: context.color.error, size: 16, shadows: [ - Shadow(color: Colors.black54, blurRadius: 4), + Shadow( + color: context.appColor(AppColor.mediaScrim), + blurRadius: 4, + ), ], ), ), @@ -294,17 +301,17 @@ class _MemoriesThumbnailCompState extends State { switch (media.mediaFile.cloudState) { case CloudState.none: iconData = Icons.cloud_off_outlined; - color = Colors.white54; + color = context.appColor(AppColor.mediaForegroundMuted); case CloudState.pending: iconData = Icons.cloud_upload_outlined; - color = Colors.white70; + color = context.appColor(AppColor.mediaForegroundMuted); case CloudState.uploaded: if (hasStored) { iconData = Icons.cloud_done_outlined; - color = Colors.white; + color = context.appColor(AppColor.mediaForeground); } else { iconData = Icons.cloud_outlined; - color = Colors.white; + color = context.appColor(AppColor.mediaForeground); } } @@ -315,8 +322,11 @@ class _MemoriesThumbnailCompState extends State { iconData, color: color, size: 16, - shadows: const [ - Shadow(color: Colors.black54, blurRadius: 4), + shadows: [ + Shadow( + color: context.appColor(AppColor.mediaScrim), + blurRadius: 4, + ), ], ), ); diff --git a/lib/src/visual/views/memories/components/selection_menu.comp.dart b/lib/src/visual/views/memories/components/selection_menu.comp.dart index 4433f90c..754d4421 100644 --- a/lib/src/visual/views/memories/components/selection_menu.comp.dart +++ b/lib/src/visual/views/memories/components/selection_menu.comp.dart @@ -85,15 +85,15 @@ class MemoriesSelectionMenuComp extends StatelessWidget { value: 'delete', child: Row( children: [ - const Icon( + Icon( Icons.delete_forever_outlined, size: 20, - color: Colors.redAccent, + color: context.color.error, ), const SizedBox(width: 12), Text( context.lang.memoriesMenuDelete, - style: const TextStyle(color: Colors.redAccent), + style: TextStyle(color: context.color.error), ), ], ), diff --git a/lib/src/visual/views/memories/components/synchronized_viewer_actions_toolbar.comp.dart b/lib/src/visual/views/memories/components/synchronized_viewer_actions_toolbar.comp.dart index 370e093a..da53095b 100644 --- a/lib/src/visual/views/memories/components/synchronized_viewer_actions_toolbar.comp.dart +++ b/lib/src/visual/views/memories/components/synchronized_viewer_actions_toolbar.comp.dart @@ -30,7 +30,7 @@ class SynchronizedViewerActionsToolbarComp extends StatelessWidget { padding: const EdgeInsets.all(12), child: Container( decoration: BoxDecoration( - color: Colors.black.withValues(alpha: 0.50), + color: context.appColor(AppColor.mediaScrim), borderRadius: BorderRadius.circular(20), ), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), @@ -40,26 +40,28 @@ class SynchronizedViewerActionsToolbarComp extends StatelessWidget { if (showStoreButton) _ToolbarAction( icon: isImageSaving - ? const SizedBox( + ? SizedBox( width: 16, height: 16, child: CircularProgressIndicator.adaptive( strokeWidth: 2, - valueColor: AlwaysStoppedAnimation(Colors.white), + valueColor: AlwaysStoppedAnimation( + context.appColor(AppColor.mediaForeground), + ), ), ) - : const FaIcon( + : FaIcon( FontAwesomeIcons.floppyDisk, - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), size: 18, ), label: context.lang.galleryActionSave, onTap: isImageSaving ? null : onStore, ), _ToolbarAction( - icon: const FaIcon( + icon: FaIcon( FontAwesomeIcons.download, - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), size: 19, ), label: context.lang.galleryActionExport, @@ -68,7 +70,9 @@ class SynchronizedViewerActionsToolbarComp extends StatelessWidget { _ToolbarAction( icon: Icon( isFavorite ? Icons.favorite : Icons.favorite_border, - color: isFavorite ? Colors.redAccent : Colors.white, + color: isFavorite + ? context.color.error + : context.appColor(AppColor.mediaForeground), size: 22, ), label: isFavorite @@ -77,18 +81,18 @@ class SynchronizedViewerActionsToolbarComp extends StatelessWidget { onTap: onToggleFavorite, ), _ToolbarAction( - icon: const Icon( + icon: Icon( Icons.delete, - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), size: 22, ), label: context.lang.galleryActionDelete, onTap: onDelete, ), _ToolbarAction( - icon: const FaIcon( + icon: FaIcon( FontAwesomeIcons.shareNodes, - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), size: 19, ), label: context.lang.galleryActionShare, @@ -135,7 +139,7 @@ class _ToolbarAction extends StatelessWidget { maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( - color: Colors.white.withValues(alpha: 0.9), + color: context.appColor(AppColor.mediaForegroundMuted), fontSize: 10, fontWeight: FontWeight.w600, letterSpacing: 0.1, diff --git a/lib/src/visual/views/memories/components/synchronized_viewer_item.comp.dart b/lib/src/visual/views/memories/components/synchronized_viewer_item.comp.dart index 083d3ca4..da9c47ec 100644 --- a/lib/src/visual/views/memories/components/synchronized_viewer_item.comp.dart +++ b/lib/src/visual/views/memories/components/synchronized_viewer_item.comp.dart @@ -5,6 +5,7 @@ import 'package:photo_view/photo_view.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/model/memory_item.model.dart'; import 'package:twonly/src/services/memories/memories_cloud.service.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/helpers/video_player_file.helper.dart'; class SynchronizedViewerItemComp extends StatefulWidget { @@ -124,10 +125,10 @@ class _SynchronizedViewerItemCompState hash: item.mediaService.mediaFile.blurhash!, optimizationMode: BlurHashOptimizationMode.approximation, ) - : const Center( + : Center( child: Icon( Icons.broken_image_outlined, - color: Colors.white38, + color: context.appColor(AppColor.mediaForegroundMuted), size: 64, ), ) @@ -143,10 +144,10 @@ class _SynchronizedViewerItemCompState color: Colors.transparent, ), errorBuilder: (context, error, stackTrace) { - return const Center( + return Center( child: Icon( Icons.broken_image_outlined, - color: Colors.white38, + color: context.appColor(AppColor.mediaForegroundMuted), size: 64, ), ); diff --git a/lib/src/visual/views/memories/memories.view.dart b/lib/src/visual/views/memories/memories.view.dart index 657af338..386dae04 100644 --- a/lib/src/visual/views/memories/memories.view.dart +++ b/lib/src/visual/views/memories/memories.view.dart @@ -441,14 +441,14 @@ class MemoriesViewState extends State Icon( Icons.photo_library_outlined, size: 64, - color: Colors.grey.shade400, + color: context.color.onSurfaceVariant, ), const SizedBox(height: 16), Text( context.lang.memoriesEmpty, textAlign: TextAlign.center, - style: const TextStyle( - color: Colors.grey, + style: TextStyle( + color: context.color.onSurfaceVariant, ), ), ], @@ -489,7 +489,7 @@ class MemoriesViewState extends State children: [ if (_isUsageLimitReached) Container( - color: Colors.redAccent, + color: context.color.error, width: double.infinity, padding: const EdgeInsets.symmetric( vertical: 8, @@ -497,8 +497,8 @@ class MemoriesViewState extends State ), child: Text( context.lang.memoriesBackupLimitReached, - style: const TextStyle( - color: Colors.white, + style: TextStyle( + color: context.color.onError, fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, @@ -656,7 +656,7 @@ class MemoriesViewState extends State ? Icons.favorite : Icons.favorite_border, color: _filterFavoritesOnly - ? Colors.redAccent + ? context.color.error : null, ), onPressed: () { diff --git a/lib/src/visual/views/memories/synchronized_viewer.view.dart b/lib/src/visual/views/memories/synchronized_viewer.view.dart index 473dde2c..76c23189 100644 --- a/lib/src/visual/views/memories/synchronized_viewer.view.dart +++ b/lib/src/visual/views/memories/synchronized_viewer.view.dart @@ -287,7 +287,11 @@ class _SynchronizedImageViewerScreenState valueListenable: _backdropOpacityNotifier, builder: (context, opacity, child) { return ColoredBox( - color: Colors.black.withValues(alpha: opacity), + color: context + .appColor(AppColor.mediaBackground) + .withValues( + alpha: opacity, + ), child: child, ); }, diff --git a/lib/src/visual/views/onboarding/components/link_logo_animation.dart b/lib/src/visual/views/onboarding/components/link_logo_animation.dart index 3a9dd31f..d4def906 100644 --- a/lib/src/visual/views/onboarding/components/link_logo_animation.dart +++ b/lib/src/visual/views/onboarding/components/link_logo_animation.dart @@ -4,9 +4,9 @@ import 'package:flutter_svg/flutter_svg.dart'; class LinkLogoAnimation extends StatefulWidget { const LinkLogoAnimation({ + required this.color, super.key, this.size = 130, - this.color = Colors.white, }); final double size; diff --git a/lib/src/visual/views/onboarding/recover_password.view.dart b/lib/src/visual/views/onboarding/recover_password.view.dart index 0b1b0f99..6da42bc1 100644 --- a/lib/src/visual/views/onboarding/recover_password.view.dart +++ b/lib/src/visual/views/onboarding/recover_password.view.dart @@ -58,9 +58,8 @@ class _BackupRecoveryViewState extends State { @override Widget build(BuildContext context) { - final isDark = isDarkMode(context); - final titleColor = isDark ? Colors.white : Colors.black87; - final iconColor = isDark ? Colors.white70 : Colors.black54; + final titleColor = context.color.onSurface; + final iconColor = context.color.onSurfaceVariant; return GestureDetector( onTap: () => FocusManager.instance.primaryFocus?.unfocus(), @@ -104,7 +103,7 @@ class _BackupRecoveryViewState extends State { child: Padding( padding: const EdgeInsets.all(20), child: LinkLogoAnimation( - color: isDark ? Colors.white : Colors.black, + color: context.color.onSurface, ), ), ), diff --git a/lib/src/visual/views/onboarding/recover_passwordless.view.dart b/lib/src/visual/views/onboarding/recover_passwordless.view.dart index 3f5b4337..ab413880 100644 --- a/lib/src/visual/views/onboarding/recover_passwordless.view.dart +++ b/lib/src/visual/views/onboarding/recover_passwordless.view.dart @@ -477,7 +477,6 @@ class _RecoverPasswordlessState extends State { Widget _buildProgressSection( BuildContext context, - bool isDark, List shares, ) { final first = shares.first; @@ -532,11 +531,11 @@ class _RecoverPasswordlessState extends State { child: LinearProgressIndicator( value: shares.length / threshold, minHeight: 12, - backgroundColor: isDark - ? Colors.white.withValues(alpha: 0.1) - : Colors.black.withValues(alpha: 0.08), + backgroundColor: context.color.surfaceContainerHighest, valueColor: AlwaysStoppedAnimation( - thresholdReached ? Colors.green : context.color.primary, + thresholdReached + ? context.appColor(AppColor.success) + : context.color.primary, ), ), ), @@ -568,7 +567,7 @@ class _RecoverPasswordlessState extends State { padding: const EdgeInsets.symmetric(vertical: 16), child: Text( 'Reconstruction failed: $_reconstructionError', - style: const TextStyle(color: Colors.red), + style: TextStyle(color: context.color.error), textAlign: TextAlign.center, ), ) @@ -603,12 +602,12 @@ class _RecoverPasswordlessState extends State { MyButton( onPressed: _isRecovering ? null : () => _recoverNow(shares), child: _isRecovering - ? const SizedBox( + ? SizedBox( height: 24, width: 24, child: CircularProgressIndicator( strokeWidth: 2.5, - color: Colors.black87, + color: context.color.onPrimary, ), ) : Text( @@ -651,7 +650,6 @@ class _RecoverPasswordlessState extends State { @override Widget build(BuildContext context) { - final isDark = isDarkMode(context); final shares = _onboardingState?.receivedShares ?? []; final hasShares = shares.isNotEmpty; final threshold = hasShares ? shares.first.threshold : 0; @@ -663,7 +661,7 @@ class _RecoverPasswordlessState extends State { title: Text(context.lang.passwordlessRecoveryRecoverBtn), leading: IconButton( icon: const Icon(Icons.arrow_back_ios_new_rounded), - color: isDark ? Colors.white70 : Colors.black54, + color: context.color.onSurfaceVariant, iconSize: 20, onPressed: () => Navigator.of(context).pop(), ), @@ -679,7 +677,7 @@ class _RecoverPasswordlessState extends State { title: Text(context.lang.passwordlessRecoveryRecoverBtn), leading: IconButton( icon: const Icon(Icons.arrow_back_ios_new_rounded), - color: isDark ? Colors.white70 : Colors.black54, + color: context.color.onSurfaceVariant, iconSize: 20, onPressed: () => Navigator.of(context).pop(), ), @@ -699,19 +697,15 @@ class _RecoverPasswordlessState extends State { const SizedBox(height: 24), ], - if (hasShares) _buildProgressSection(context, isDark, shares), + if (hasShares) _buildProgressSection(context, shares), if (!_notificationsEnabled && !hasShares) ...[ Container( decoration: BoxDecoration( - color: isDark - ? Colors.white.withValues(alpha: 0.05) - : Colors.black.withValues(alpha: 0.04), + color: context.color.surfaceContainerLow, borderRadius: BorderRadius.circular(16), border: Border.all( - color: isDark - ? Colors.white.withValues(alpha: 0.08) - : Colors.black.withValues(alpha: 0.06), + color: context.color.outlineVariant, ), ), padding: const EdgeInsets.all(16), @@ -763,14 +757,10 @@ class _RecoverPasswordlessState extends State { if (!hasShares) Container( decoration: BoxDecoration( - color: isDark - ? Colors.white.withValues(alpha: 0.05) - : Colors.black.withValues(alpha: 0.04), + color: context.color.surfaceContainerLow, borderRadius: BorderRadius.circular(16), border: Border.all( - color: isDark - ? Colors.white.withValues(alpha: 0.08) - : Colors.black.withValues(alpha: 0.06), + color: context.color.outlineVariant, ), ), padding: const EdgeInsets.all(16), @@ -778,7 +768,7 @@ class _RecoverPasswordlessState extends State { children: [ Icon( Icons.info_outline_rounded, - color: isDark ? Colors.white70 : Colors.black54, + color: context.color.onSurfaceVariant, size: 22, ), const SizedBox(width: 12), @@ -798,13 +788,11 @@ class _RecoverPasswordlessState extends State { child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( - color: Colors.white, + color: context.appColor(AppColor.qrBackground), borderRadius: BorderRadius.circular(24), boxShadow: [ BoxShadow( - color: Colors.black.withValues( - alpha: isDark ? 0.3 : 0.08, - ), + color: context.color.shadow.withValues(alpha: 0.2), blurRadius: 20, offset: const Offset(0, 10), ), @@ -815,12 +803,12 @@ class _RecoverPasswordlessState extends State { data: _shareUrl, errorCorrectLevel: QrErrorCorrectLevel.M, ), - eyeStyle: const QrEyeStyle( - color: Colors.black, + eyeStyle: QrEyeStyle( + color: context.appColor(AppColor.qrForeground), borderRadius: 4, ), - dataModuleStyle: const QrDataModuleStyle( - color: Colors.black, + dataModuleStyle: QrDataModuleStyle( + color: context.appColor(AppColor.qrForeground), borderRadius: 4, ), gapless: false, diff --git a/lib/src/visual/views/onboarding/recovery_progress.view.dart b/lib/src/visual/views/onboarding/recovery_progress.view.dart index 814e732a..c388bc40 100644 --- a/lib/src/visual/views/onboarding/recovery_progress.view.dart +++ b/lib/src/visual/views/onboarding/recovery_progress.view.dart @@ -144,12 +144,11 @@ class _RecoveryProgressViewState extends State { } Widget _buildStatusIcon(BuildContext context, _StepStatus status) { - final isDark = isDarkMode(context); switch (status) { case _StepStatus.done: - return const Icon( + return Icon( Icons.check_circle_rounded, - color: Colors.green, + color: context.appColor(AppColor.success), size: 24, ); case _StepStatus.active: @@ -165,43 +164,41 @@ class _RecoveryProgressViewState extends State { ), ); case _StepStatus.error: - return const Icon( + return Icon( Icons.error_rounded, - color: Colors.redAccent, + color: context.color.error, size: 24, ); case _StepStatus.pending: return Icon( Icons.circle_outlined, size: 24, - color: isDark ? Colors.white24 : Colors.black26, + color: context.color.outlineVariant, ); } } TextStyle _labelStyle(BuildContext context, _StepStatus status) { - final isDark = isDarkMode(context); const base = TextStyle(fontSize: 15, height: 1.3); switch (status) { case _StepStatus.active: return base.copyWith( fontWeight: FontWeight.w600, - color: isDark ? Colors.white : Colors.black87, + color: context.color.onSurface, ); case _StepStatus.error: return base.copyWith( fontWeight: FontWeight.w600, - color: Colors.redAccent, + color: context.color.error, ); case _StepStatus.done: - return base.copyWith(color: isDark ? Colors.white60 : Colors.black54); + return base.copyWith(color: context.color.onSurfaceVariant); case _StepStatus.pending: - return base.copyWith(color: isDark ? Colors.white30 : Colors.black38); + return base.copyWith(color: context.color.outline); } } Widget _buildStepRow(BuildContext context, int index) { - final isDark = isDarkMode(context); final isLast = index == _stepCount - 1; final status = _statusFor(index); @@ -218,8 +215,10 @@ class _RecoveryProgressViewState extends State { width: 2, margin: const EdgeInsets.symmetric(vertical: 4), color: status == _StepStatus.done - ? Colors.green.withValues(alpha: 0.4) - : (isDark ? Colors.white12 : Colors.black12), + ? context + .appColor(AppColor.success) + .withValues(alpha: 0.4) + : context.color.outlineVariant, ), ), ], @@ -257,8 +256,7 @@ class _RecoveryProgressViewState extends State { @override Widget build(BuildContext context) { - final isDark = isDarkMode(context); - final titleColor = isDark ? Colors.white : Colors.black87; + final titleColor = context.color.onSurface; final hasFailed = _error != null; return PopScope( @@ -280,7 +278,7 @@ class _RecoveryProgressViewState extends State { ? () => Navigator.of(context).pop() : null, icon: const Icon(Icons.arrow_back_ios_new_rounded), - color: isDark ? Colors.white70 : Colors.black54, + color: context.color.onSurfaceVariant, iconSize: 20, ), ], @@ -291,7 +289,7 @@ class _RecoveryProgressViewState extends State { padding: const EdgeInsets.all(12), child: LinkLogoAnimation( size: 90, - color: isDark ? Colors.white : Colors.black, + color: context.color.onSurface, ), ), ), @@ -312,20 +310,16 @@ class _RecoveryProgressViewState extends State { textAlign: TextAlign.center, style: TextStyle( fontSize: 13, - color: isDark ? Colors.white54 : Colors.black45, + color: context.color.onSurfaceVariant, ), ), const SizedBox(height: 32), Container( decoration: BoxDecoration( - color: isDark - ? Colors.white.withValues(alpha: 0.05) - : Colors.black.withValues(alpha: 0.04), + color: context.color.surfaceContainerLow, borderRadius: BorderRadius.circular(16), border: Border.all( - color: isDark - ? Colors.white.withValues(alpha: 0.08) - : Colors.black.withValues(alpha: 0.06), + color: context.color.outlineVariant, ), ), padding: const EdgeInsets.fromLTRB(16, 20, 16, 20), @@ -341,8 +335,8 @@ class _RecoveryProgressViewState extends State { Text( _error!.toLocalizedString(context), textAlign: TextAlign.center, - style: const TextStyle( - color: Colors.redAccent, + style: TextStyle( + color: context.color.error, fontSize: 14, ), ), diff --git a/lib/src/visual/views/onboarding/register.view.dart b/lib/src/visual/views/onboarding/register.view.dart index e6298e2b..afa83951 100644 --- a/lib/src/visual/views/onboarding/register.view.dart +++ b/lib/src/visual/views/onboarding/register.view.dart @@ -198,8 +198,6 @@ class _RegisterViewState extends State { @override Widget build(BuildContext context) { - final isDark = isDarkMode(context); - return GestureDetector( onTap: () => FocusManager.instance.primaryFocus?.unfocus(), behavior: HitTestBehavior.opaque, @@ -223,7 +221,7 @@ class _RegisterViewState extends State { child: Container( padding: const EdgeInsets.all(10), child: LinkLogoAnimation( - color: isDark ? Colors.white : Colors.black, + color: context.color.onSurface, ), ), ), @@ -238,9 +236,7 @@ class _RegisterViewState extends State { color: Theme.of(context).textTheme.bodyMedium?.color ?.withValues(alpha: 0.7) ?? - (isDark - ? Colors.white.withValues(alpha: 0.7) - : Colors.black.withValues(alpha: 0.7)), + context.color.onSurfaceVariant, fontWeight: FontWeight.w500, ), ), @@ -250,9 +246,9 @@ class _RegisterViewState extends State { Text( context.lang.registrationClosed, textAlign: TextAlign.center, - style: const TextStyle( + style: TextStyle( fontSize: 16, - color: Colors.redAccent, + color: context.color.error, fontWeight: FontWeight.bold, ), ), @@ -263,7 +259,7 @@ class _RegisterViewState extends State { textAlign: TextAlign.center, style: TextStyle( fontSize: 22, - color: isDark ? Colors.white : Colors.black87, + color: context.color.onSurface, fontWeight: FontWeight.bold, letterSpacing: -0.5, ), @@ -299,8 +295,8 @@ class _RegisterViewState extends State { const SizedBox(height: 10), Text( context.lang.registerProofOfWorkFailed, - style: const TextStyle( - color: Colors.redAccent, + style: TextStyle( + color: context.color.error, fontSize: 14, fontWeight: FontWeight.w500, ), @@ -313,12 +309,12 @@ class _RegisterViewState extends State { ? null : createNewUser, child: _isTryingToRegister - ? const SizedBox( + ? SizedBox( width: 24, height: 24, child: CircularProgressIndicator.adaptive( valueColor: AlwaysStoppedAnimation( - Colors.white, + context.color.onPrimary, ), strokeWidth: 3, ), diff --git a/lib/src/visual/views/onboarding/setup/backup.setup.dart b/lib/src/visual/views/onboarding/setup/backup.setup.dart index 1a2f0cd1..a9227d0d 100644 --- a/lib/src/visual/views/onboarding/setup/backup.setup.dart +++ b/lib/src/visual/views/onboarding/setup/backup.setup.dart @@ -112,16 +112,19 @@ class _BackupSetupPageState extends State { const SizedBox(height: 10), Row( children: [ - const FaIcon( + FaIcon( FontAwesomeIcons.circleInfo, size: 14, - color: Colors.grey, + color: context.color.onSurfaceVariant, ), const SizedBox(width: 8), Expanded( child: Text( context.lang.backupNoPasswordRecovery, - style: const TextStyle(fontSize: 12, color: Colors.grey), + style: TextStyle( + fontSize: 12, + color: context.color.onSurfaceVariant, + ), ), ), ], diff --git a/lib/src/visual/views/onboarding/setup/components/mock_contact_request_actions.comp.dart b/lib/src/visual/views/onboarding/setup/components/mock_contact_request_actions.comp.dart index 80183cfd..580a6c1d 100644 --- a/lib/src/visual/views/onboarding/setup/components/mock_contact_request_actions.comp.dart +++ b/lib/src/visual/views/onboarding/setup/components/mock_contact_request_actions.comp.dart @@ -30,9 +30,9 @@ class MockContactRequestActionsComp extends StatelessWidget { child: Row( mainAxisSize: MainAxisSize.min, children: [ - const Icon( + Icon( Icons.person_off_rounded, - color: Color.fromARGB(164, 244, 67, 54), + color: context.color.error, size: 12, ), Text( @@ -56,7 +56,11 @@ class MockContactRequestActionsComp extends StatelessWidget { child: Row( mainAxisSize: MainAxisSize.min, children: [ - const Icon(Icons.check, color: Colors.green, size: 12), + Icon( + Icons.check, + color: context.appColor(AppColor.success), + size: 12, + ), Text( context.lang.contactActionAccept, style: const TextStyle(fontSize: 8), diff --git a/lib/src/visual/views/onboarding/setup/components/next_button.comp.dart b/lib/src/visual/views/onboarding/setup/components/next_button.comp.dart index 915b9a12..e530d5c9 100644 --- a/lib/src/visual/views/onboarding/setup/components/next_button.comp.dart +++ b/lib/src/visual/views/onboarding/setup/components/next_button.comp.dart @@ -38,12 +38,14 @@ class NextButtonComp extends StatelessWidget { } : null, child: isLoading - ? const SizedBox( + ? SizedBox( height: 24, width: 24, child: CircularProgressIndicator.adaptive( strokeWidth: 2, - valueColor: AlwaysStoppedAnimation(Colors.white), + valueColor: AlwaysStoppedAnimation( + context.color.onPrimary, + ), ), ) : Text( diff --git a/lib/src/visual/views/recovery_from_secure_storage.view.dart b/lib/src/visual/views/recovery_from_secure_storage.view.dart index fa0d4de2..293e3ffd 100644 --- a/lib/src/visual/views/recovery_from_secure_storage.view.dart +++ b/lib/src/visual/views/recovery_from_secure_storage.view.dart @@ -133,7 +133,8 @@ class _RecoveryViewState extends State { onPressed: _registerNewAccount, icon: const Icon(Icons.person_add_rounded), style: FilledButton.styleFrom( - backgroundColor: Colors.redAccent, + backgroundColor: context.color.error, + foregroundColor: context.color.onError, padding: const EdgeInsets.symmetric( horizontal: 32, vertical: 12, diff --git a/lib/src/visual/views/settings/account.view.dart b/lib/src/visual/views/settings/account.view.dart index 36e859e3..3fd19faf 100644 --- a/lib/src/visual/views/settings/account.view.dart +++ b/lib/src/visual/views/settings/account.view.dart @@ -32,12 +32,12 @@ class AccountView extends StatelessWidget { Container( alignment: Alignment.center, padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 20), - child: const Text( + child: Text( 'Danger Zone', textAlign: TextAlign.center, style: TextStyle( fontSize: 20, - color: Colors.red, + color: context.color.error, fontWeight: FontWeight.bold, ), ), @@ -45,7 +45,7 @@ class AccountView extends StatelessWidget { ListTile( title: Text( context.lang.settingsAccountDeleteAccount, - style: const TextStyle(color: Colors.red), + style: TextStyle(color: context.color.error), ), subtitle: Text(context.lang.settingsAccountDeleteAccountNoBallance), onTap: () async { diff --git a/lib/src/visual/views/settings/appearance.view.dart b/lib/src/visual/views/settings/appearance.view.dart index e42b6cc0..60bb6d53 100644 --- a/lib/src/visual/views/settings/appearance.view.dart +++ b/lib/src/visual/views/settings/appearance.view.dart @@ -175,14 +175,16 @@ class _AppearanceViewState extends State { ), ], border: Border.all( - color: isSelected ? Colors.white : Colors.transparent, + color: isSelected + ? context.appColor(AppColor.mediaForeground) + : Colors.transparent, width: 3, ), ), child: isSelected - ? const Icon( + ? Icon( Icons.check, - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), size: 24, ) : null, @@ -209,7 +211,10 @@ class _AppearanceViewState extends State { Colors.red, ], ), - border: Border.all(color: Colors.white24, width: 2), + border: Border.all( + color: context.color.outlineVariant, + width: 2, + ), ), ), title: Text(context.lang.customColor), @@ -298,7 +303,7 @@ class _AppearanceViewState extends State { title: Text(context.lang.settingsAppearanceTheme), subtitle: Text( _themeModeLabel(context, selectedTheme), - style: const TextStyle(color: Colors.grey), + style: TextStyle(color: context.color.onSurfaceVariant), ), onTap: () async { await _showSelectThemeMode(context); @@ -310,7 +315,7 @@ class _AppearanceViewState extends State { selectedLocale == null ? context.lang.themeSystemDefault : _languageName(selectedLocale), - style: const TextStyle(color: Colors.grey), + style: TextStyle(color: context.color.onSurfaceVariant), ), onTap: () async { await _showSelectLanguage(context); @@ -324,7 +329,10 @@ class _AppearanceViewState extends State { decoration: BoxDecoration( color: primaryColor, shape: BoxShape.circle, - border: Border.all(color: Colors.white, width: 1.5), + border: Border.all( + color: context.color.outlineVariant, + width: 1.5, + ), ), ), onTap: () async { diff --git a/lib/src/visual/views/settings/backup/components/backup_setup.comp.dart b/lib/src/visual/views/settings/backup/components/backup_setup.comp.dart index 9dd395fa..89f04bad 100644 --- a/lib/src/visual/views/settings/backup/components/backup_setup.comp.dart +++ b/lib/src/visual/views/settings/backup/components/backup_setup.comp.dart @@ -87,7 +87,7 @@ class PasswordRequirementText extends StatelessWidget { text, style: TextStyle( fontSize: 12, - color: showError ? Colors.red : Colors.transparent, + color: showError ? context.color.error : Colors.transparent, ), ), ); @@ -95,10 +95,9 @@ class PasswordRequirementText extends StatelessWidget { } void showBackupExplanation(BuildContext context) { - final isDark = isDarkMode(context); final backgroundColor = Theme.of(context).scaffoldBackgroundColor; - final textColor = isDark ? Colors.white : Colors.black87; - final subtitleColor = isDark ? Colors.white70 : Colors.black54; + final textColor = context.color.onSurface; + final subtitleColor = context.color.onSurfaceVariant; showModalBottomSheet( context: context, @@ -122,7 +121,7 @@ void showBackupExplanation(BuildContext context) { width: 40, height: 5, decoration: BoxDecoration( - color: isDark ? Colors.white24 : Colors.black12, + color: context.color.outlineVariant, borderRadius: BorderRadius.circular(2.5), ), ), diff --git a/lib/src/visual/views/settings/backup/components/recovery_card.comp.dart b/lib/src/visual/views/settings/backup/components/recovery_card.comp.dart index cd53efad..d2a67505 100644 --- a/lib/src/visual/views/settings/backup/components/recovery_card.comp.dart +++ b/lib/src/visual/views/settings/backup/components/recovery_card.comp.dart @@ -25,7 +25,7 @@ class RecoveryCard extends StatelessWidget { title: title, subtitle: subtitle, onTap: onTap, - accentColor: isEnabled ? context.color.primary : Colors.red, + accentColor: isEnabled ? context.color.primary : context.color.error, ); } } diff --git a/lib/src/visual/views/settings/backup/memories_backup_detail.view.dart b/lib/src/visual/views/settings/backup/memories_backup_detail.view.dart index 39688d5d..dc7ddab8 100644 --- a/lib/src/visual/views/settings/backup/memories_backup_detail.view.dart +++ b/lib/src/visual/views/settings/backup/memories_backup_detail.view.dart @@ -71,7 +71,7 @@ class _MemoriesBackupDetailViewState extends State { height: 24, width: double.infinity, decoration: BoxDecoration( - color: Colors.grey.withValues(alpha: 0.2), + color: context.color.surfaceContainerHighest, borderRadius: BorderRadius.circular(12), ), child: ClipRRect( @@ -94,7 +94,7 @@ class _MemoriesBackupDetailViewState extends State { if (usageWidth > 0) Container( width: usageWidth, - color: Colors.blue, + color: context.appColor(AppColor.info), ), ], ); diff --git a/lib/src/visual/views/settings/backup/passwordless_recovery/components/second_factor_picker.comp.dart b/lib/src/visual/views/settings/backup/passwordless_recovery/components/second_factor_picker.comp.dart index 6e870db3..90bca1b2 100644 --- a/lib/src/visual/views/settings/backup/passwordless_recovery/components/second_factor_picker.comp.dart +++ b/lib/src/visual/views/settings/backup/passwordless_recovery/components/second_factor_picker.comp.dart @@ -122,7 +122,7 @@ class SecondFactorPicker extends StatelessWidget { Icon( option.icon, color: isSelected - ? Colors.black87 + ? context.color.onPrimary : context.color.onSurfaceVariant, ), const SizedBox(height: 6), @@ -131,7 +131,7 @@ class SecondFactorPicker extends StatelessWidget { style: TextStyle( fontWeight: FontWeight.bold, color: isSelected - ? Colors.black87 + ? context.color.onPrimary : context.color.onSurfaceVariant, ), ), diff --git a/lib/src/visual/views/settings/backup/passwordless_recovery/components/threshold_picker.comp.dart b/lib/src/visual/views/settings/backup/passwordless_recovery/components/threshold_picker.comp.dart index cdb02684..98513301 100644 --- a/lib/src/visual/views/settings/backup/passwordless_recovery/components/threshold_picker.comp.dart +++ b/lib/src/visual/views/settings/backup/passwordless_recovery/components/threshold_picker.comp.dart @@ -113,7 +113,7 @@ class ThresholdPicker extends StatelessWidget { style: TextStyle( fontWeight: FontWeight.bold, color: isSelected - ? Colors.black87 + ? context.color.onPrimary : context.color.onSurfaceVariant, ), ), diff --git a/lib/src/visual/views/settings/backup/passwordless_recovery/help_a_friend.passwordless_recovery.view.dart b/lib/src/visual/views/settings/backup/passwordless_recovery/help_a_friend.passwordless_recovery.view.dart index bf0a7ac3..6c1feda7 100644 --- a/lib/src/visual/views/settings/backup/passwordless_recovery/help_a_friend.passwordless_recovery.view.dart +++ b/lib/src/visual/views/settings/backup/passwordless_recovery/help_a_friend.passwordless_recovery.view.dart @@ -117,7 +117,6 @@ class _HelpAFriendPasswordlessRecoveryViewState @override Widget build(BuildContext context) { - final isDark = isDarkMode(context); final query = _searchController.text.trim().toLowerCase(); final filteredContacts = @@ -145,14 +144,10 @@ class _HelpAFriendPasswordlessRecoveryViewState children: [ Container( decoration: BoxDecoration( - color: isDark - ? Colors.white.withValues(alpha: 0.05) - : Colors.black.withValues(alpha: 0.04), + color: context.color.surfaceContainerLow, borderRadius: BorderRadius.circular(16), border: Border.all( - color: isDark - ? Colors.white.withValues(alpha: 0.08) - : Colors.black.withValues(alpha: 0.06), + color: context.color.outlineVariant, ), ), padding: const EdgeInsets.all(16), @@ -228,8 +223,10 @@ class _HelpAFriendPasswordlessRecoveryViewState context .lang .passwordlessRecoveryCantHelpHim, - style: const TextStyle( - color: Colors.grey, + style: TextStyle( + color: context + .color + .onSurfaceVariant, ), ), onTap: () => _onContactSelected(contact), diff --git a/lib/src/visual/views/settings/backup/passwordless_recovery/setup.passwordless_recovery.view.dart b/lib/src/visual/views/settings/backup/passwordless_recovery/setup.passwordless_recovery.view.dart index 22156a8f..d8303a0d 100644 --- a/lib/src/visual/views/settings/backup/passwordless_recovery/setup.passwordless_recovery.view.dart +++ b/lib/src/visual/views/settings/backup/passwordless_recovery/setup.passwordless_recovery.view.dart @@ -263,13 +263,13 @@ class _PasswordLessRecoverySetupState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ if (_isLoading) - const SizedBox( + SizedBox( height: 16, width: 16, child: CircularProgressIndicator.adaptive( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( - Colors.black87, + context.color.onPrimary, ), ), ) diff --git a/lib/src/visual/views/settings/data_and_storage.view.dart b/lib/src/visual/views/settings/data_and_storage.view.dart index d1e4aa51..4941e2c7 100644 --- a/lib/src/visual/views/settings/data_and_storage.view.dart +++ b/lib/src/visual/views/settings/data_and_storage.view.dart @@ -65,7 +65,7 @@ class _DataAndStorageViewState extends State { onPressed: () => Navigator.of(context).pop(true), child: Text( context.lang.galleryDisableWarningConfirm, - style: const TextStyle(color: Colors.red), + style: TextStyle(color: context.color.error), ), ), ], @@ -216,7 +216,7 @@ class _DataAndStorageViewState extends State { autoDownloadOptions[ConnectivityResult.mobile.name]! .where((e) => e != 'audio') .join(', '), - style: const TextStyle(color: Colors.grey), + style: TextStyle(color: context.color.onSurfaceVariant), ), onTap: () async { await showAutoDownloadOptions( @@ -231,7 +231,7 @@ class _DataAndStorageViewState extends State { autoDownloadOptions[ConnectivityResult.wifi.name]! .where((e) => e != 'audio') .join(', '), - style: const TextStyle(color: Colors.grey), + style: TextStyle(color: context.color.onSurfaceVariant), ), onTap: () async { await showAutoDownloadOptions( diff --git a/lib/src/visual/views/settings/data_and_storage/chat_storage_overview.view.dart b/lib/src/visual/views/settings/data_and_storage/chat_storage_overview.view.dart index 8dd2d4b2..e5c323b6 100644 --- a/lib/src/visual/views/settings/data_and_storage/chat_storage_overview.view.dart +++ b/lib/src/visual/views/settings/data_and_storage/chat_storage_overview.view.dart @@ -66,9 +66,12 @@ class _ChatStorageOverviewViewState extends State { child: Text( context.lang.settingsStorageNoChats, textAlign: TextAlign.center, - style: Theme.of( - context, - ).textTheme.bodyLarge?.copyWith(color: Colors.grey), + style: + Theme.of( + context, + ).textTheme.bodyLarge?.copyWith( + color: context.color.onSurfaceVariant, + ), ), ) else diff --git a/lib/src/visual/views/settings/data_and_storage/import_from_gallery.view.dart b/lib/src/visual/views/settings/data_and_storage/import_from_gallery.view.dart index 69d38c6f..f0dbb300 100644 --- a/lib/src/visual/views/settings/data_and_storage/import_from_gallery.view.dart +++ b/lib/src/visual/views/settings/data_and_storage/import_from_gallery.view.dart @@ -14,7 +14,8 @@ import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/services/android_photo_picker.service.dart'; import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; import 'package:twonly/src/utils/log.dart'; -import 'package:twonly/src/utils/misc.dart' show ShortCutsExtension, sha256File; +import 'package:twonly/src/utils/misc.dart' + show AppColor, ShortCutsExtension, sha256File; import 'package:twonly/src/visual/components/selectable_thumbnail.comp.dart'; import 'package:twonly/src/visual/components/snackbar.dart'; import 'package:twonly/src/visual/themes/light.dart'; @@ -570,10 +571,10 @@ class _ImportFromGalleryViewState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( + Icon( Icons.photo_library_outlined, size: 64, - color: Colors.grey, + color: context.color.onSurfaceVariant, ), const SizedBox(height: 16), Text( @@ -604,7 +605,11 @@ class _ImportFromGalleryViewState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon(Icons.error_outline, size: 64, color: Colors.red), + Icon( + Icons.error_outline, + size: 64, + color: context.color.error, + ), const SizedBox(height: 16), Text( _errorMessage!, @@ -629,10 +634,10 @@ class _ImportFromGalleryViewState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( + Icon( Icons.photo_album_outlined, size: 64, - color: Colors.grey, + color: context.color.onSurfaceVariant, ), const SizedBox(height: 16), Text( @@ -650,7 +655,7 @@ class _ImportFromGalleryViewState extends State { ? context.lang.importGalleryNoImagesFoundDesc : context.lang.importGalleryAlbumNotFoundDesc, textAlign: TextAlign.center, - style: const TextStyle(color: Colors.grey), + style: TextStyle(color: context.color.onSurfaceVariant), ), const SizedBox(height: 24), ElevatedButton( @@ -699,7 +704,7 @@ class _ImportFromGalleryViewState extends State { Widget _buildImportingOverlay() { return ColoredBox( - color: Colors.black54, + color: context.appColor(AppColor.mediaScrim), child: Center( child: Container( margin: const EdgeInsets.symmetric(horizontal: 32), @@ -776,7 +781,7 @@ class _GalleryThumbnailWidgetState extends State { ); } return ColoredBox( - color: Colors.grey.withValues(alpha: 0.1), + color: context.color.surfaceContainerHigh, child: const Center( child: SizedBox( width: 24, @@ -788,14 +793,17 @@ class _GalleryThumbnailWidgetState extends State { }, ), if (widget.asset.type == AssetType.video) - const Positioned.fill( + Positioned.fill( child: Center( child: Icon( Icons.play_circle_outline, - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), size: 32, shadows: [ - Shadow(color: Colors.black54, blurRadius: 6), + Shadow( + color: context.appColor(AppColor.mediaScrim), + blurRadius: 6, + ), ], ), ), diff --git a/lib/src/visual/views/settings/data_and_storage/manage_storage.view.dart b/lib/src/visual/views/settings/data_and_storage/manage_storage.view.dart index c2166acc..9ef7d649 100644 --- a/lib/src/visual/views/settings/data_and_storage/manage_storage.view.dart +++ b/lib/src/visual/views/settings/data_and_storage/manage_storage.view.dart @@ -128,7 +128,7 @@ class _ManageStorageViewState extends State { height: 24, width: double.infinity, decoration: BoxDecoration( - color: Colors.grey.withValues(alpha: 0.2), + color: context.color.surfaceContainerHighest, borderRadius: BorderRadius.circular(12), ), child: ClipRRect( @@ -145,11 +145,20 @@ class _ManageStorageViewState extends State { return Row( children: [ if (imageBytes > 0) - Container(width: imageWidth, color: Colors.blue), + Container( + width: imageWidth, + color: context.appColor(AppColor.info), + ), if (videoBytes > 0) - Container(width: videoWidth, color: Colors.green), + Container( + width: videoWidth, + color: context.appColor(AppColor.success), + ), if (gifBytes > 0) - Container(width: gifWidth, color: Colors.orange), + Container( + width: gifWidth, + color: context.appColor(AppColor.warning), + ), ], ); }, @@ -160,17 +169,17 @@ class _ManageStorageViewState extends State { _StorageCategoryTile( title: context.lang.settingsStorageImages, size: formatBytes(imageBytes), - color: Colors.blue, + color: context.appColor(AppColor.info), ), _StorageCategoryTile( title: context.lang.settingsStorageVideos, size: formatBytes(videoBytes), - color: Colors.green, + color: context.appColor(AppColor.success), ), _StorageCategoryTile( title: context.lang.settingsStorageGifs, size: formatBytes(gifBytes), - color: Colors.orange, + color: context.appColor(AppColor.warning), ), const SizedBox(height: 32), Align( @@ -229,9 +238,9 @@ class _StorageCategoryTile extends StatelessWidget { ), Text( size, - style: const TextStyle( + style: TextStyle( fontSize: 16, - color: Colors.grey, + color: context.color.onSurfaceVariant, ), ), ], diff --git a/lib/src/visual/views/settings/data_and_storage/storage_contents.view.dart b/lib/src/visual/views/settings/data_and_storage/storage_contents.view.dart index 07e0d6f2..65268fd7 100644 --- a/lib/src/visual/views/settings/data_and_storage/storage_contents.view.dart +++ b/lib/src/visual/views/settings/data_and_storage/storage_contents.view.dart @@ -104,9 +104,12 @@ class _StorageContentsViewState extends State { ? Center( child: Text( context.lang.settingsStorageNoContents, - style: Theme.of( - context, - ).textTheme.bodyLarge?.copyWith(color: Colors.grey), + style: + Theme.of( + context, + ).textTheme.bodyLarge?.copyWith( + color: context.color.onSurfaceVariant, + ), ), ) : Column( @@ -236,21 +239,31 @@ class _StorageContentsViewState extends State { children: [ Text( file.type.name.toUpperCase(), - style: const TextStyle(color: Colors.grey, fontSize: 12), + style: TextStyle( + color: context.color.onSurfaceVariant, + fontSize: 12, + ), ), const SizedBox(width: 8), - Icon(statusIcon, size: 14, color: Colors.grey), + Icon( + statusIcon, + size: 14, + color: context.color.onSurfaceVariant, + ), const SizedBox(width: 4), Text( statusText, - style: const TextStyle(color: Colors.grey, fontSize: 12), + style: TextStyle( + color: context.color.onSurfaceVariant, + fontSize: 12, + ), ), ], ), trailing: isSelecting ? null : IconButton( - icon: const Icon(Icons.delete_outline, color: Colors.redAccent), + icon: Icon(Icons.delete_outline, color: context.color.error), onPressed: () => _deleteMemory(context, file), ), ); @@ -276,7 +289,7 @@ class _StorageContentsViewState extends State { child: Container( width: 60, height: 60, - color: Colors.grey.withValues(alpha: 0.1), + color: context.color.surfaceContainerHigh, child: Stack( alignment: Alignment.center, children: [ @@ -285,10 +298,10 @@ class _StorageContentsViewState extends State { ? Image.file( imgFile, fit: BoxFit.cover, - errorBuilder: (context, error, stackTrace) => const Icon( + errorBuilder: (context, error, stackTrace) => Icon( Icons.broken_image_outlined, size: 24, - color: Colors.grey, + color: context.color.onSurfaceVariant, ), ) : file.blurhash != null @@ -296,23 +309,23 @@ class _StorageContentsViewState extends State { hash: file.blurhash!, optimizationMode: BlurHashOptimizationMode.approximation, ) - : const Icon( + : Icon( Icons.image_outlined, size: 24, - color: Colors.grey, + color: context.color.onSurfaceVariant, ), ), if (isVideo) Container( decoration: BoxDecoration( - color: Colors.black.withValues(alpha: 0.5), + color: context.appColor(AppColor.mediaScrim), shape: BoxShape.circle, ), padding: const EdgeInsets.all(4), - child: const Icon( + child: Icon( Icons.play_arrow, size: 16, - color: Colors.white, + color: context.appColor(AppColor.mediaForeground), ), ), ], diff --git a/lib/src/visual/views/settings/developer/developer.view.dart b/lib/src/visual/views/settings/developer/developer.view.dart index 8a3d96ce..c87f1e09 100644 --- a/lib/src/visual/views/settings/developer/developer.view.dart +++ b/lib/src/visual/views/settings/developer/developer.view.dart @@ -527,9 +527,9 @@ class _DeveloperSettingsViewState extends State { }, ), ListTile( - title: const Text( + title: Text( 'Delete all app data', - style: TextStyle(color: Colors.red), + style: TextStyle(color: context.color.error), ), onTap: () async { final ok = await showAlertDialog( diff --git a/lib/src/visual/views/settings/developer/user_discovery_developer.view.dart b/lib/src/visual/views/settings/developer/user_discovery_developer.view.dart index 0b3af71b..b80756cb 100644 --- a/lib/src/visual/views/settings/developer/user_discovery_developer.view.dart +++ b/lib/src/visual/views/settings/developer/user_discovery_developer.view.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:twonly/locator.dart'; import 'package:twonly/src/database/daos/user_discovery.dao.dart'; import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/utils/misc.dart'; class UserDiscoveryDeveloperView extends StatefulWidget { const UserDiscoveryDeveloperView({super.key}); @@ -121,10 +122,10 @@ class _TableExpansionTile extends StatelessWidget { children: data.isNotEmpty ? data.map((item) => itemBuilder(context, item)).toList() : [ - const ListTile( + ListTile( title: Text( 'No entries', - style: TextStyle(color: Colors.grey), + style: TextStyle(color: context.color.onSurfaceVariant), ), ), ], diff --git a/lib/src/visual/views/settings/help/contact_us.view.dart b/lib/src/visual/views/settings/help/contact_us.view.dart index c673c82e..eaa8d5c2 100644 --- a/lib/src/visual/views/settings/help/contact_us.view.dart +++ b/lib/src/visual/views/settings/help/contact_us.view.dart @@ -260,8 +260,8 @@ $debugLogToken onTap: () => context.push(Routes.settingsHelpFaq), child: Text( context.lang.contactUsFaq, - style: const TextStyle( - color: Colors.blue, + style: TextStyle( + color: context.appColor(AppColor.info), ), ), ), @@ -285,12 +285,14 @@ $debugLogToken mainAxisSize: MainAxisSize.min, children: [ if (isLoading) - const SizedBox( + SizedBox( height: 12, width: 12, child: CircularProgressIndicator.adaptive( strokeWidth: 2, - valueColor: AlwaysStoppedAnimation(Colors.black87), + valueColor: AlwaysStoppedAnimation( + context.color.onPrimary, + ), ), ) else @@ -345,8 +347,8 @@ class _IncludeDebugLogState extends State { ), child: Text( context.lang.contactUsWhatsThat, - style: const TextStyle( - color: Colors.blue, + style: TextStyle( + color: context.appColor(AppColor.info), ), ), ), @@ -368,11 +370,11 @@ class FeedbackEmojiRow extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - _buildEmojiButton(5, Icons.sentiment_very_satisfied), - _buildEmojiButton(4, Icons.sentiment_satisfied), - _buildEmojiButton(3, Icons.sentiment_neutral), - _buildEmojiButton(2, Icons.sentiment_dissatisfied), - _buildEmojiButton(1, Icons.sentiment_very_dissatisfied), + _buildEmojiButton(context, 5, Icons.sentiment_very_satisfied), + _buildEmojiButton(context, 4, Icons.sentiment_satisfied), + _buildEmojiButton(context, 3, Icons.sentiment_neutral), + _buildEmojiButton(context, 2, Icons.sentiment_dissatisfied), + _buildEmojiButton(context, 1, Icons.sentiment_very_dissatisfied), ], ); } @@ -395,7 +397,7 @@ class FeedbackEmojiRow extends StatelessWidget { } } - Widget _buildEmojiButton(int value, IconData icon) { + Widget _buildEmojiButton(BuildContext context, int value, IconData icon) { final isSelected = selectedFeedback == value; return GestureDetector( @@ -405,22 +407,22 @@ class FeedbackEmojiRow extends StatelessWidget { child: Icon( icon, size: 40, - color: _getColorForValue(value, isSelected), + color: _getColorForValue(context, value, isSelected), ), ); } - Color _getColorForValue(int value, bool isSelected) { + Color _getColorForValue(BuildContext context, int value, bool isSelected) { if (isSelected) { if (value == 5) { - return Colors.greenAccent; + return context.appColor(AppColor.success); } else if (value > 1) { - return Colors.yellow; + return context.appColor(AppColor.warning); } else { - return Colors.red; + return context.color.error; } } else { - return const Color.fromARGB(155, 134, 134, 134); + return context.color.onSurfaceVariant; } } } diff --git a/lib/src/visual/views/settings/help/diagnostics.view.dart b/lib/src/visual/views/settings/help/diagnostics.view.dart index 44645598..e3283c81 100644 --- a/lib/src/visual/views/settings/help/diagnostics.view.dart +++ b/lib/src/visual/views/settings/help/diagnostics.view.dart @@ -109,13 +109,13 @@ class _LogViewerWidgetState extends State { Color _colorForLevel(String? level) { switch (level) { case 'WARNING': - return Colors.orange.shade700; + return context.appColor(AppColor.warning); case 'SHOUT': - return Colors.red.shade600; + return context.color.error; case 'FINE': - return Colors.blueGrey.shade600; + return context.appColor(AppColor.info); default: - return Colors.grey.shade700; + return context.color.onSurfaceVariant; } } @@ -145,18 +145,18 @@ class _LogViewerWidgetState extends State { TextSpan _formatLineSpan(_LogEntry e) { final tsStyle = TextStyle( - color: isDarkMode(context) ? Colors.white : Colors.black, + color: context.color.onSurface, fontFamily: 'monospace', fontSize: 12, ); final fileNameStyle = TextStyle( - color: Colors.blueGrey.shade400, + color: context.appColor(AppColor.info), fontWeight: FontWeight.bold, fontFamily: 'monospace', fontSize: 11, ); final msgStyle = TextStyle( - color: isDarkMode(context) ? Colors.white : Colors.black, + color: context.color.onSurface, fontFamily: 'monospace', fontSize: 13, ); @@ -175,7 +175,7 @@ class _LogViewerWidgetState extends State { child: FaIcon( e.isBackground ? FontAwesomeIcons.clock : FontAwesomeIcons.mobile, size: 12, - color: Colors.grey, + color: context.color.onSurfaceVariant, ), ), ), @@ -214,7 +214,7 @@ class _LogViewerWidgetState extends State { const Spacer(), Text( '${_filtered.length} lines', - style: TextStyle(color: Colors.grey.shade600), + style: TextStyle(color: context.color.onSurfaceVariant), ), const SizedBox(width: 8), ], diff --git a/lib/src/visual/views/settings/help/help.view.dart b/lib/src/visual/views/settings/help/help.view.dart index 0d120ab0..e5246a53 100644 --- a/lib/src/visual/views/settings/help/help.view.dart +++ b/lib/src/visual/views/settings/help/help.view.dart @@ -129,9 +129,12 @@ class _HelpViewState extends State { await UserService.update((u) => u.isDeveloper = true); } }, - title: const Text( + title: Text( 'Copyright twonly', - style: TextStyle(color: Colors.grey, fontSize: 13), + style: TextStyle( + color: context.color.onSurfaceVariant, + fontSize: 13, + ), ), ), ], diff --git a/lib/src/visual/views/settings/help/news.view.dart b/lib/src/visual/views/settings/help/news.view.dart index f15e33ff..9d9f6ba5 100644 --- a/lib/src/visual/views/settings/help/news.view.dart +++ b/lib/src/visual/views/settings/help/news.view.dart @@ -49,7 +49,7 @@ class _NewsViewState extends State { child: Text( 'No news articles found.', style: Theme.of(context).textTheme.bodyLarge?.copyWith( - color: Colors.grey, + color: context.color.onSurfaceVariant, ), ), ), @@ -60,15 +60,13 @@ class _NewsViewState extends State { itemCount: entries.length, itemBuilder: (context, index) { final entry = entries[index]; - final isDark = isDarkMode(context); - return ReactiveTapFeedback( onTap: () => launchUrl( Uri.parse(entry.link), mode: LaunchMode.externalApplication, ), child: Card( - color: isDark ? Colors.grey[800] : Colors.grey[200], + color: context.color.surfaceContainerHigh, clipBehavior: Clip.antiAlias, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), @@ -88,15 +86,15 @@ class _NewsViewState extends State { fit: BoxFit.cover, placeholder: (context, url) => Container( height: 180, - color: Colors.grey.withValues(alpha: 0.1), + color: context.color.surfaceContainerHighest, ), errorWidget: (context, url, error) => Container( height: 180, - color: Colors.grey.withValues(alpha: 0.1), - child: const Icon( + color: context.color.surfaceContainerHighest, + child: Icon( Icons.broken_image, size: 50, - color: Colors.grey, + color: context.color.onSurfaceVariant, ), ), ), @@ -114,7 +112,7 @@ class _NewsViewState extends State { ).format(entry.pubDate!), style: Theme.of(context).textTheme.bodySmall ?.copyWith( - color: Colors.grey, + color: context.color.onSurfaceVariant, ), ), const SizedBox(height: 8), diff --git a/lib/src/visual/views/settings/privacy.view.dart b/lib/src/visual/views/settings/privacy.view.dart index 252dc3b7..e2514595 100644 --- a/lib/src/visual/views/settings/privacy.view.dart +++ b/lib/src/visual/views/settings/privacy.view.dart @@ -4,7 +4,6 @@ import 'package:twonly/locator.dart'; import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/services/user.service.dart'; import 'package:twonly/src/utils/misc.dart'; -import 'package:twonly/src/visual/components/verification_badge_info.comp.dart'; import 'package:twonly/src/visual/elements/svg_icon.element.dart'; class PrivacyView extends StatefulWidget { @@ -113,12 +112,12 @@ class _VerificationBadgeTriangle extends StatelessWidget { @override Widget build(BuildContext context) { - return const SizedBox( + return SizedBox( width: 30, height: 30, child: Stack( children: [ - Positioned( + const Positioned( top: 0, left: 9, child: SvgIcon( @@ -132,10 +131,10 @@ class _VerificationBadgeTriangle extends StatelessWidget { child: SvgIcon( assetPath: SvgIcons.verifiedGreen, size: 14, - color: colorVerificationBadgeYellow, + color: context.appColor(AppColor.verificationPending), ), ), - Positioned( + const Positioned( bottom: 0, right: 0, child: SvgIcon( diff --git a/lib/src/visual/views/settings/privacy/user_discovery/components/user_discovery_enabled.comp.dart b/lib/src/visual/views/settings/privacy/user_discovery/components/user_discovery_enabled.comp.dart index e8e93092..9eb5453b 100644 --- a/lib/src/visual/views/settings/privacy/user_discovery/components/user_discovery_enabled.comp.dart +++ b/lib/src/visual/views/settings/privacy/user_discovery/components/user_discovery_enabled.comp.dart @@ -173,7 +173,10 @@ class _UserDiscoveryEnabledCompState extends State { context.lang.userDiscoveryEnabledYourVersion( '${_version!.announcement}.${_version!.promotion}', ), - style: const TextStyle(color: Colors.grey, fontSize: 13), + style: TextStyle( + color: context.color.onSurfaceVariant, + fontSize: 13, + ), ), ), ], diff --git a/lib/src/visual/views/settings/profile/modify_avatar.view.dart b/lib/src/visual/views/settings/profile/modify_avatar.view.dart index c75a476e..de4c28e9 100644 --- a/lib/src/visual/views/settings/profile/modify_avatar.view.dart +++ b/lib/src/visual/views/settings/profile/modify_avatar.view.dart @@ -41,14 +41,13 @@ class _ModifyAvatarViewState extends State { AvatarMakerThemeData getAvatarMakerTheme(BuildContext context) { final colors = context.color; - final isDark = isDarkMode(context); return AvatarMakerThemeData( boxDecoration: BoxDecoration( color: colors.surface, borderRadius: const BorderRadius.vertical(top: Radius.circular(24)), boxShadow: [ BoxShadow( - color: Colors.black.withValues(alpha: isDark ? 0.2 : 0.05), + color: colors.shadow.withValues(alpha: 0.12), blurRadius: 10, offset: const Offset(0, -5), ), diff --git a/lib/src/visual/views/settings/subscription/subscription.view.dart b/lib/src/visual/views/settings/subscription/subscription.view.dart index 8de8655b..969f24eb 100644 --- a/lib/src/visual/views/settings/subscription/subscription.view.dart +++ b/lib/src/visual/views/settings/subscription/subscription.view.dart @@ -119,7 +119,7 @@ class _SubscriptionViewState extends State { style: TextStyle( fontSize: 32, fontWeight: FontWeight.bold, - color: isDarkMode(context) ? Colors.black : Colors.white, + color: context.color.onPrimary, ), ), ), @@ -295,7 +295,7 @@ class CurrentPlanBadge extends StatelessWidget { style: TextStyle( fontSize: 11, fontWeight: FontWeight.bold, - color: isDarkMode(context) ? Colors.black : Colors.white, + color: context.color.onPrimary, ), ), ); @@ -580,9 +580,9 @@ class _MissionRow extends StatelessWidget { Text( desc, textAlign: TextAlign.center, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: Colors.grey, + color: context.color.onSurfaceVariant, height: 1.3, ), ), diff --git a/lib/src/visual/views/unlock_twonly.view.dart b/lib/src/visual/views/unlock_twonly.view.dart index bab7e1d9..4efa9bde 100644 --- a/lib/src/visual/views/unlock_twonly.view.dart +++ b/lib/src/visual/views/unlock_twonly.view.dart @@ -57,9 +57,9 @@ class _UnlockTwonlyViewState extends State { child: Text( context.lang.unlockTwonlyDesc, textAlign: TextAlign.center, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: Colors.grey, + color: context.color.onSurfaceVariant, ), ), ), diff --git a/scripts/fix_adb_connection.sh b/scripts/fix_adb_connection.sh index 3f3682d0..aa9d356e 100755 --- a/scripts/fix_adb_connection.sh +++ b/scripts/fix_adb_connection.sh @@ -1,7 +1,8 @@ #!/usr/bin/env bash # -# Recover an Android wireless-debugging connection when `adb connect` reports -# "No route to host" even though the device's TCP port is reachable. +# Recover an Android wireless-debugging connection when `adb connect` fails +# even though the device's TCP port is reachable. If Android has forgotten the +# host certificate, the script guides you through pairing again. # # Usage: # scripts/fix_adb_connection.sh [PORT] @@ -60,12 +61,28 @@ if [ "$PORT" -lt 1 ] || [ "$PORT" -gt 65535 ]; then exit 2 fi -for bin in adb nc; do - command -v "$bin" >/dev/null || { - echo "missing required tool: $bin" >&2 - exit 1 - } -done +command -v nc >/dev/null || { + echo "missing required tool: nc" >&2 + exit 1 +} + +# Prefer the platform-tools installation used by Flutter/Android Studio. Using +# a Homebrew adb client against an Android SDK adb daemon (or vice versa) makes +# daemon ownership and diagnostics unnecessarily unpredictable. +if [ -n "${ANDROID_SDK_ROOT:-}" ] && [ -x "$ANDROID_SDK_ROOT/platform-tools/adb" ]; then + ADB="$ANDROID_SDK_ROOT/platform-tools/adb" +elif [ -n "${ANDROID_HOME:-}" ] && [ -x "$ANDROID_HOME/platform-tools/adb" ]; then + ADB="$ANDROID_HOME/platform-tools/adb" +elif [ -x "$HOME/Library/Android/sdk/platform-tools/adb" ]; then + ADB="$HOME/Library/Android/sdk/platform-tools/adb" +else + ADB="$(command -v adb || true)" +fi + +if [ -z "$ADB" ]; then + echo "missing required tool: adb" >&2 + exit 1 +fi ENDPOINT="$HOST:$PORT" @@ -88,6 +105,8 @@ echo "TCP port is reachable." export ADB_MDNS_AUTO_CONNECT=0 LAST_OUTPUT="" +PAIRING_REQUIRED=0 +ADB_LOG="${TMPDIR:-/tmp}/adb.$(id -u).log" attempt=1 while [ "$attempt" -le "$MAX_ATTEMPTS" ]; do echo "ADB reconnect attempt $attempt/$MAX_ATTEMPTS..." @@ -95,27 +114,89 @@ while [ "$attempt" -le "$MAX_ATTEMPTS" ]; do # `adb kill-server` sometimes leaves the macOS daemon alive. Killing only # this user's exact `adb` processes clears that stale network-permission # state without requiring sudo or affecting other users. - adb kill-server >/dev/null 2>&1 || true + "$ADB" kill-server >/dev/null 2>&1 || true pkill -x -u "$(id -u)" adb >/dev/null 2>&1 || true - adb start-server >/dev/null 2>&1 - adb disconnect "$ENDPOINT" >/dev/null 2>&1 || true + "$ADB" start-server >/dev/null 2>&1 + "$ADB" disconnect "$ENDPOINT" >/dev/null 2>&1 || true - LAST_OUTPUT="$(adb connect "$ENDPOINT" 2>&1)" + LAST_OUTPUT="$("$ADB" connect "$ENDPOINT" 2>&1)" echo "$LAST_OUTPUT" - if adb -s "$ENDPOINT" get-state 2>/dev/null | grep -qx device; then + if "$ADB" -s "$ENDPOINT" get-state 2>/dev/null | grep -qx device; then echo echo "Connected successfully: $ENDPOINT" - adb -s "$ENDPOINT" shell getprop ro.product.model 2>/dev/null \ + "$ADB" -s "$ENDPOINT" shell getprop ro.product.model 2>/dev/null \ | sed 's/^/Device: /' exit 0 fi + if [ -f "$ADB_LOG" ] \ + && tail -80 "$ADB_LOG" | grep -qE 'CERTIFICATE_UNKNOWN|certificate unknown'; then + PAIRING_REQUIRED=1 + break + fi + attempt=$((attempt + 1)) [ "$attempt" -le "$MAX_ATTEMPTS" ] && sleep 1 done +if [ "$PAIRING_REQUIRED" -eq 1 ]; then + echo + echo "Android rejected this computer's ADB certificate. Pairing is required." + echo "On the phone, open:" + echo " Settings > System > Developer options > Wireless debugging" + echo " Pair device with pairing code" + + if [ ! -t 0 ]; then + echo "Run this script in an interactive terminal to enter the pairing port and code." >&2 + exit 1 + fi + + printf "Pairing port: " + IFS= read -r PAIRING_PORT + case "$PAIRING_PORT" in + ''|*[!0-9]*) + echo "invalid pairing port: $PAIRING_PORT" >&2 + exit 2 + ;; + esac + if [ "$PAIRING_PORT" -lt 1 ] || [ "$PAIRING_PORT" -gt 65535 ]; then + echo "pairing port must be between 1 and 65535" >&2 + exit 2 + fi + + printf "Pairing code: " + IFS= read -r PAIRING_CODE + case "$PAIRING_CODE" in + ''|*[!0-9]*) + echo "invalid pairing code" >&2 + exit 2 + ;; + esac + + PAIR_OUTPUT="$(printf '%s\n' "$PAIRING_CODE" | "$ADB" pair "$HOST:$PAIRING_PORT" 2>&1)" + PAIRING_CODE="" + echo "$PAIR_OUTPUT" + if ! printf '%s' "$PAIR_OUTPUT" | grep -q "Successfully paired"; then + echo "Pairing failed. Reopen the pairing dialog for a fresh port and code." >&2 + exit 1 + fi + + LAST_OUTPUT="$("$ADB" connect "$ENDPOINT" 2>&1)" + echo "$LAST_OUTPUT" + if "$ADB" -s "$ENDPOINT" get-state 2>/dev/null | grep -qx device; then + echo + echo "Paired and connected successfully: $ENDPOINT" + "$ADB" -s "$ENDPOINT" shell getprop ro.product.model 2>/dev/null \ + | sed 's/^/Device: /' + exit 0 + fi + + echo "Pairing succeeded, but the connection port failed. Check that $PORT is still current." >&2 + exit 1 +fi + echo >&2 echo "ADB could not connect after $MAX_ATTEMPTS clean restarts." >&2 if printf '%s' "$LAST_OUTPUT" | grep -q "No route to host"; then diff --git a/test/visual/chat_contacts_entry_test.dart b/test/visual/chat_contacts_entry_test.dart index b4b812c7..5c74136d 100644 --- a/test/visual/chat_contacts_entry_test.dart +++ b/test/visual/chat_contacts_entry_test.dart @@ -11,6 +11,7 @@ import 'package:twonly/src/localization/generated/app_localizations.dart'; import 'package:twonly/src/model/protobuf/client/generated/data.pb.dart'; import 'package:twonly/src/providers/settings.provider.dart'; import 'package:twonly/src/services/user.service.dart'; +import 'package:twonly/src/visual/themes/light.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/chat_contacts.entry.dart'; import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/common.dart'; @@ -70,6 +71,7 @@ void main() { ChangeNotifierProvider( create: (_) => SettingsChangeProvider()..loadSettings(), child: MaterialApp( + theme: lightTheme, localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: Scaffold( diff --git a/test/visual/theme_colors_test.dart b/test/visual/theme_colors_test.dart new file mode 100644 index 00000000..6f84500a --- /dev/null +++ b/test/visual/theme_colors_test.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:twonly/src/visual/themes/colors.dart'; +import 'package:twonly/src/visual/themes/dark.dart'; +import 'package:twonly/src/visual/themes/light.dart'; + +void main() { + test('light theme exposes the app palette', () { + final appColors = lightTheme.extension(); + + expect(appColors, isNotNull); + expect( + appColors![AppColor.chatBubbleReceived], + const Color(0xFFF3F4F3), + ); + expect(appColors[AppColor.chatBubbleSent], const Color(0xFFE8EBE9)); + expect(lightTheme.colorScheme.onPrimary, const Color(0xFF082117)); + expect( + lightTheme.scaffoldBackgroundColor, + lightTheme.colorScheme.surface, + ); + }); + + test('dark theme exposes the app palette', () { + final appColors = darkTheme.extension(); + + expect(appColors, isNotNull); + expect( + appColors![AppColor.chatBubbleReceived], + const Color(0xFF1D201E), + ); + expect(appColors[AppColor.chatBubbleSent], const Color(0xFF292C2A)); + expect(darkTheme.colorScheme.onPrimary, const Color(0xFF082117)); + expect(darkTheme.scaffoldBackgroundColor, darkTheme.colorScheme.surface); + }); + + test('custom dark accents receive a contrasting foreground', () { + final theme = getDarkTheme(const Color(0xFF3A76F0)); + + expect(theme.colorScheme.onPrimary, const Color(0xFFFFFFFF)); + }); + + test('every app color automatically participates in theme transitions', () { + final middle = const AppColors.light().lerp(const AppColors.dark(), 0.5); + + for (final color in AppColor.values) { + expect(middle[color], Color.lerp(color.light, color.dark, 0.5)); + } + }); +}