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:
@@ -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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user