Improve and consolidate the image error handler

This commit is contained in:
Roland Geider
2025-11-10 20:23:52 +01:00
parent aebe3f8244
commit f6a073766a
3 changed files with 75 additions and 39 deletions

View File

@@ -1,20 +1,73 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
class ImageFormatNotSupported extends StatelessWidget {
Widget handleImageError(
BuildContext context,
Object error,
StackTrace? stackTrace,
String imageUrl,
) {
final imageFormat = imageUrl.split('.').last.toUpperCase();
final logger = Logger('handleImageError');
logger.warning('Failed to load image $imageUrl: $error, $stackTrace');
// NOTE: for the moment the other error messages are not localized
String message = '';
switch (error.runtimeType) {
case NetworkImageLoadException:
message = 'Network error';
case HttpException:
message = 'Http error';
case FormatException:
//TODO: not sure if this is the right exception for unsupported image formats?
message = AppLocalizations.of(context).imageFormatNotSupported(imageFormat);
default:
message = 'Other exception';
}
return AspectRatio(
aspectRatio: 1,
child: ImageError(
message,
errorMessage: error.toString(),
),
);
}
class ImageError extends StatelessWidget {
final String title;
final String? errorMessage;
const ImageFormatNotSupported(this.title, {super.key});
const ImageError(this.title, {this.errorMessage, super.key});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.all(5),
color: theme.colorScheme.errorContainer,
child: Row(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [const Icon(Icons.broken_image), Text(title)],
children: [
if (errorMessage != null)
Tooltip(message: errorMessage, child: const Icon(Icons.broken_image))
else
const Icon(Icons.broken_image),
Text(
title,
overflow: TextOverflow.ellipsis,
maxLines: 2,
textAlign: TextAlign.center,
),
],
),
);
}

View File

@@ -37,14 +37,12 @@ class ExerciseImageWidget extends StatelessWidget {
? Image.network(
image!.url,
semanticLabel: 'Exercise image',
errorBuilder: (context, error, stackTrace) {
_logger.warning('Failed to load image ${image!.url}: $error, $stackTrace');
final imageFormat = image!.url.split('.').last.toUpperCase();
return ImageFormatNotSupported(
i18n.imageFormatNotSupported(imageFormat),
);
},
errorBuilder: (context, error, stackTrace) => handleImageError(
context,
error,
stackTrace,
image!.url,
),
)
: const Image(
image: AssetImage('assets/images/placeholder.png'),

View File

@@ -36,8 +36,6 @@ class Gallery extends StatelessWidget {
@override
Widget build(BuildContext context) {
final provider = Provider.of<GalleryProvider>(context);
final i18n = AppLocalizations.of(context);
final theme = Theme.of(context);
return Padding(
padding: const EdgeInsets.all(5),
@@ -66,23 +64,12 @@ class Gallery extends StatelessWidget {
image: NetworkImage(currentImage.url!),
fit: BoxFit.cover,
imageSemanticLabel: currentImage.description,
imageErrorBuilder: (context, error, stackTrace) {
final imageFormat = currentImage.url!.split('.').last.toUpperCase();
return AspectRatio(
aspectRatio: 1,
child: Container(
color: theme.colorScheme.errorContainer,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
const Icon(Icons.broken_image),
Text(i18n.imageFormatNotSupported(imageFormat)),
],
),
),
);
},
imageErrorBuilder: (context, error, stackTrace) => handleImageError(
context,
error,
stackTrace,
currentImage.url!,
),
),
);
},
@@ -102,7 +89,6 @@ class ImageDetail extends StatelessWidget {
@override
Widget build(BuildContext context) {
final i18n = AppLocalizations.of(context);
return Container(
key: Key('image-${image.id!}-detail'),
padding: const EdgeInsets.all(10),
@@ -116,13 +102,12 @@ class ImageDetail extends StatelessWidget {
child: Image.network(
image.url!,
semanticLabel: image.description,
errorBuilder: (context, error, stackTrace) {
final imageFormat = image.url!.split('.').last.toUpperCase();
return ImageFormatNotSupported(
i18n.imageFormatNotSupported(imageFormat),
);
},
errorBuilder: (context, error, stackTrace) => handleImageError(
context,
error,
stackTrace,
image.url!,
),
),
),
Padding(