refactor: enhance error handling and theming in EnhancedErrorService

- Updated the EnhancedErrorService to utilize the dynamic theme context for improved color handling in SnackBars and AlertDialogs.
- Replaced hardcoded color values with theme-based colors to ensure better accessibility and visual consistency across the application.
- Added context parameter to various components to facilitate theme integration, enhancing the overall user experience.
- Improved the readability of error messages by applying appropriate text colors based on the current theme.
This commit is contained in:
cogwheel0
2025-10-02 11:39:17 +05:30
parent 1fa8412e0a
commit a85655d639
5 changed files with 48 additions and 19 deletions

View File

@@ -99,6 +99,8 @@ class EnhancedErrorService {
final message = getUserMessage(error); final message = getUserMessage(error);
final isRetryableError = isRetryable(error); final isRetryableError = isRetryable(error);
final retryDelay = getRetryDelay(error); final retryDelay = getRetryDelay(error);
final theme = context.conduitTheme;
final inverseColor = theme.textInverse;
final snackBar = SnackBar( final snackBar = SnackBar(
content: Column( content: Column(
@@ -109,15 +111,12 @@ class EnhancedErrorService {
children: [ children: [
Icon( Icon(
_getErrorIcon(error), _getErrorIcon(error),
color: AppTheme.neutral50, color: inverseColor,
size: IconSize.md, size: IconSize.md,
), ),
const SizedBox(width: Spacing.sm), const SizedBox(width: Spacing.sm),
Expanded( Expanded(
child: Text( child: Text(message, style: TextStyle(color: inverseColor)),
message,
style: const TextStyle(color: AppTheme.neutral50),
),
), ),
], ],
), ),
@@ -126,7 +125,7 @@ class EnhancedErrorService {
Text( Text(
getTechnicalDetails(error), getTechnicalDetails(error),
style: TextStyle( style: TextStyle(
color: AppTheme.neutral50.withValues(alpha: Alpha.strong), color: inverseColor.withValues(alpha: Alpha.strong),
fontSize: AppTypography.labelMedium, fontSize: AppTypography.labelMedium,
), ),
), ),
@@ -140,7 +139,7 @@ class EnhancedErrorService {
label: retryDelay != null && retryDelay.inSeconds > 5 label: retryDelay != null && retryDelay.inSeconds > 5
? "${AppLocalizations.of(context)!.retry} (${retryDelay.inSeconds}s)" ? "${AppLocalizations.of(context)!.retry} (${retryDelay.inSeconds}s)"
: AppLocalizations.of(context)!.retry, : AppLocalizations.of(context)!.retry,
textColor: AppTheme.neutral50, textColor: inverseColor,
onPressed: onRetry, onPressed: onRetry,
) )
: null, : null,
@@ -166,6 +165,7 @@ class EnhancedErrorService {
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (BuildContext context) { builder: (BuildContext context) {
final theme = context.conduitTheme;
return AlertDialog( return AlertDialog(
title: Row( title: Row(
children: [ children: [
@@ -178,25 +178,29 @@ class EnhancedErrorService {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(message), Text(message, style: TextStyle(color: theme.textPrimary)),
if (showTechnicalDetails) ...[ if (showTechnicalDetails) ...[
const SizedBox(height: Spacing.md), const SizedBox(height: Spacing.md),
const Text( Text(
'Technical Details:', 'Technical Details:',
style: TextStyle(fontWeight: FontWeight.bold), style: TextStyle(
fontWeight: FontWeight.bold,
color: theme.textPrimary,
),
), ),
const SizedBox(height: Spacing.xs), const SizedBox(height: Spacing.xs),
Container( Container(
padding: const EdgeInsets.all(Spacing.sm), padding: const EdgeInsets.all(Spacing.sm),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppTheme.neutral100, color: theme.surfaceContainer,
borderRadius: BorderRadius.circular(AppBorderRadius.xs), borderRadius: BorderRadius.circular(AppBorderRadius.xs),
), ),
child: Text( child: Text(
technicalDetails, technicalDetails,
style: const TextStyle( style: TextStyle(
fontFamily: AppTypography.monospaceFontFamily, fontFamily: AppTypography.monospaceFontFamily,
fontSize: AppTypography.labelMedium, fontSize: AppTypography.labelMedium,
color: theme.textSecondary,
), ),
), ),
), ),
@@ -236,6 +240,7 @@ class EnhancedErrorService {
final message = getUserMessage(error); final message = getUserMessage(error);
final technicalDetails = getTechnicalDetails(error); final technicalDetails = getTechnicalDetails(error);
final isRetryableError = isRetryable(error); final isRetryableError = isRetryable(error);
final theme = context.conduitTheme;
return Container( return Container(
padding: padding ?? const EdgeInsets.all(Spacing.md), padding: padding ?? const EdgeInsets.all(Spacing.md),
@@ -260,21 +265,22 @@ class EnhancedErrorService {
Text( Text(
message, message,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(color: AppTheme.neutral600), style: TextStyle(color: theme.textSecondary),
), ),
if (showTechnicalDetails) ...[ if (showTechnicalDetails) ...[
const SizedBox(height: Spacing.md), const SizedBox(height: Spacing.md),
Container( Container(
padding: const EdgeInsets.all(Spacing.xs), padding: const EdgeInsets.all(Spacing.xs),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppTheme.neutral100, color: theme.surfaceContainer,
borderRadius: BorderRadius.circular(AppBorderRadius.sm), borderRadius: BorderRadius.circular(AppBorderRadius.sm),
), ),
child: Text( child: Text(
technicalDetails, technicalDetails,
style: const TextStyle( style: TextStyle(
fontFamily: AppTypography.monospaceFontFamily, fontFamily: AppTypography.monospaceFontFamily,
fontSize: AppTypography.labelMedium, fontSize: AppTypography.labelMedium,
color: theme.textSecondary,
), ),
), ),
), ),

View File

@@ -314,6 +314,7 @@ class _AuthenticationPageState extends ConsumerState<AuthenticationPage> {
size: 48, size: 48,
useGradient: true, useGradient: true,
addShadow: true, addShadow: true,
context: context,
).animate().scale( ).animate().scale(
duration: AnimationDuration.pageTransition, duration: AnimationDuration.pageTransition,
curve: Curves.easeOutBack, curve: Curves.easeOutBack,

View File

@@ -356,6 +356,7 @@ class _ServerConnectionPageState extends ConsumerState<ServerConnectionPage> {
size: 64, size: 64,
useGradient: true, useGradient: true,
addShadow: true, addShadow: true,
context: context,
), ),
// Reviewer mode badge // Reviewer mode badge
if (reviewerMode) if (reviewerMode)

View File

@@ -1,3 +1,5 @@
import 'dart:math' as math;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
// Using system fonts; no GoogleFonts dependency required // Using system fonts; no GoogleFonts dependency required
import 'app_theme.dart'; import 'app_theme.dart';
@@ -505,10 +507,11 @@ class ConduitThemeExtension extends ThemeExtension<ConduitThemeExtension> {
/// Dark theme extension derived from the active color palette. /// Dark theme extension derived from the active color palette.
static ConduitThemeExtension darkPalette(AppColorPalette palette) { static ConduitThemeExtension darkPalette(AppColorPalette palette) {
final darkTone = palette.dark; final darkTone = palette.dark;
final onDarkPrimary = _onSurfaceColor(darkTone.primary);
return ConduitThemeExtension( return ConduitThemeExtension(
chatBubbleUser: darkTone.primary, chatBubbleUser: darkTone.primary,
chatBubbleAssistant: const Color(0xFF0E1010), chatBubbleAssistant: const Color(0xFF0E1010),
chatBubbleUserText: AppTheme.neutral50, chatBubbleUserText: onDarkPrimary,
chatBubbleAssistantText: AppTheme.neutral50, chatBubbleAssistantText: AppTheme.neutral50,
chatBubbleUserBorder: darkTone.secondary, chatBubbleUserBorder: darkTone.secondary,
chatBubbleAssistantBorder: const Color(0xFF1A1D1C), chatBubbleAssistantBorder: const Color(0xFF1A1D1C),
@@ -525,7 +528,7 @@ class ConduitThemeExtension extends ThemeExtension<ConduitThemeExtension> {
surfaceContainer: const Color(0xFF0C0F0E), surfaceContainer: const Color(0xFF0C0F0E),
surfaceContainerHighest: const Color(0xFF121514), surfaceContainerHighest: const Color(0xFF121514),
buttonPrimary: darkTone.primary, buttonPrimary: darkTone.primary,
buttonPrimaryText: AppTheme.neutral50, buttonPrimaryText: onDarkPrimary,
buttonSecondary: const Color(0xFF151918), buttonSecondary: const Color(0xFF151918),
buttonSecondaryText: AppTheme.neutral50, buttonSecondaryText: AppTheme.neutral50,
buttonDisabled: AppTheme.neutral600, buttonDisabled: AppTheme.neutral600,
@@ -622,10 +625,11 @@ class ConduitThemeExtension extends ThemeExtension<ConduitThemeExtension> {
static ConduitThemeExtension lightPalette(AppColorPalette palette) { static ConduitThemeExtension lightPalette(AppColorPalette palette) {
final lightTone = palette.light; final lightTone = palette.light;
final darkTone = palette.dark; final darkTone = palette.dark;
final onLightPrimary = _onSurfaceColor(lightTone.primary);
return ConduitThemeExtension( return ConduitThemeExtension(
chatBubbleUser: lightTone.primary, chatBubbleUser: lightTone.primary,
chatBubbleAssistant: const Color(0xFFF7F7F7), chatBubbleAssistant: const Color(0xFFF7F7F7),
chatBubbleUserText: AppTheme.neutral50, chatBubbleUserText: onLightPrimary,
chatBubbleAssistantText: const Color(0xFF1C1C1C), chatBubbleAssistantText: const Color(0xFF1C1C1C),
chatBubbleUserBorder: darkTone.primary, chatBubbleUserBorder: darkTone.primary,
chatBubbleAssistantBorder: const Color(0xFFE7E7E7), chatBubbleAssistantBorder: const Color(0xFFE7E7E7),
@@ -642,7 +646,7 @@ class ConduitThemeExtension extends ThemeExtension<ConduitThemeExtension> {
surfaceContainer: const Color(0xFFF7F7F7), surfaceContainer: const Color(0xFFF7F7F7),
surfaceContainerHighest: const Color(0xFFF0F1F1), surfaceContainerHighest: const Color(0xFFF0F1F1),
buttonPrimary: lightTone.primary, buttonPrimary: lightTone.primary,
buttonPrimaryText: AppTheme.neutral50, buttonPrimaryText: onLightPrimary,
buttonSecondary: const Color(0xFFF0F1F1), buttonSecondary: const Color(0xFFF0F1F1),
buttonSecondaryText: const Color(0xFF1C1C1C), buttonSecondaryText: const Color(0xFF1C1C1C),
buttonDisabled: AppTheme.neutral300, buttonDisabled: AppTheme.neutral300,
@@ -738,6 +742,22 @@ class ConduitThemeExtension extends ThemeExtension<ConduitThemeExtension> {
static Color _surfaceTint(Color tone, Color surface, double opacity) { static Color _surfaceTint(Color tone, Color surface, double opacity) {
return Color.alphaBlend(tone.withValues(alpha: opacity), surface); return Color.alphaBlend(tone.withValues(alpha: opacity), surface);
} }
static Color _onSurfaceColor(Color background) {
final contrastOnLight = _contrastRatio(background, AppTheme.neutral50);
final contrastOnDark = _contrastRatio(background, AppTheme.neutral900);
return contrastOnLight >= contrastOnDark
? AppTheme.neutral50
: AppTheme.neutral900;
}
static double _contrastRatio(Color a, Color b) {
final luminanceA = a.computeLuminance();
final luminanceB = b.computeLuminance();
final lighter = math.max(luminanceA, luminanceB);
final darker = math.min(luminanceA, luminanceB);
return (lighter + 0.05) / (darker + 0.05);
}
} }
/// Extension method to easily access Conduit theme from BuildContext /// Extension method to easily access Conduit theme from BuildContext

View File

@@ -557,6 +557,7 @@ class ConduitAvatar extends StatelessWidget {
return BrandService.createBrandAvatar( return BrandService.createBrandAvatar(
size: isCompact ? size * 0.8 : size, size: isCompact ? size * 0.8 : size,
fallbackText: text, fallbackText: text,
context: context,
); );
} }
} }