Skip to content

Commit

Permalink
[flutter_svg] Implement errorBuilder callback (flutter#8364)
Browse files Browse the repository at this point in the history
closes flutter/flutter#158612

## Description
- Implemented errorBuilder callback to handle loading failures
  • Loading branch information
memishood authored Jan 9, 2025
1 parent 4efc3d1 commit c6caf68
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 1 deletion.
4 changes: 4 additions & 0 deletions third_party/packages/flutter_svg/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.0.17

* Implement errorBuilder callback

## 2.0.16

* Adopts code excerpts for README.
Expand Down
18 changes: 18 additions & 0 deletions third_party/packages/flutter_svg/lib/svg.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ export 'src/cache.dart';
export 'src/default_theme.dart';
export 'src/loaders.dart';

/// Builder function to create an error widget. This builder is called when
/// the image failed loading.
typedef SvgErrorWidgetBuilder = Widget Function(
BuildContext context,
Object error,
StackTrace stackTrace,
);

/// Instance for [Svg]'s utility methods, which can produce a [DrawableRoot]
/// or [PictureInfo] from [String] or [Uint8List].
final Svg svg = Svg._();
Expand Down Expand Up @@ -86,6 +94,7 @@ class SvgPicture extends StatelessWidget {
this.semanticsLabel,
this.excludeFromSemantics = false,
this.clipBehavior = Clip.hardEdge,
this.errorBuilder,
@Deprecated(
'No code should use this parameter. It never was implemented properly. '
'The SVG theme must be set on the bytesLoader.')
Expand Down Expand Up @@ -184,6 +193,7 @@ class SvgPicture extends StatelessWidget {
this.semanticsLabel,
this.excludeFromSemantics = false,
this.clipBehavior = Clip.hardEdge,
this.errorBuilder,
SvgTheme? theme,
ui.ColorFilter? colorFilter,
@Deprecated('Use colorFilter instead.') ui.Color? color,
Expand Down Expand Up @@ -248,6 +258,7 @@ class SvgPicture extends StatelessWidget {
this.semanticsLabel,
this.excludeFromSemantics = false,
this.clipBehavior = Clip.hardEdge,
this.errorBuilder,
@Deprecated('This no longer does anything.') bool cacheColorFilter = false,
SvgTheme? theme,
http.Client? httpClient,
Expand Down Expand Up @@ -306,6 +317,7 @@ class SvgPicture extends StatelessWidget {
this.semanticsLabel,
this.excludeFromSemantics = false,
this.clipBehavior = Clip.hardEdge,
this.errorBuilder,
SvgTheme? theme,
@Deprecated('This no longer does anything.') bool cacheColorFilter = false,
}) : bytesLoader = SvgFileLoader(file, theme: theme),
Expand Down Expand Up @@ -355,6 +367,7 @@ class SvgPicture extends StatelessWidget {
this.semanticsLabel,
this.excludeFromSemantics = false,
this.clipBehavior = Clip.hardEdge,
this.errorBuilder,
SvgTheme? theme,
@Deprecated('This no longer does anything.') bool cacheColorFilter = false,
}) : bytesLoader = SvgBytesLoader(bytes, theme: theme),
Expand Down Expand Up @@ -404,6 +417,7 @@ class SvgPicture extends StatelessWidget {
this.semanticsLabel,
this.excludeFromSemantics = false,
this.clipBehavior = Clip.hardEdge,
this.errorBuilder,
SvgTheme? theme,
@Deprecated('This no longer does anything.') bool cacheColorFilter = false,
}) : bytesLoader = SvgStringLoader(string, theme: theme),
Expand Down Expand Up @@ -487,6 +501,9 @@ class SvgPicture extends StatelessWidget {
/// Defaults to [Clip.hardEdge], and must not be null.
final Clip clipBehavior;

/// Widget displayed while the target image failed loading.
final SvgErrorWidgetBuilder? errorBuilder;

/// The color filter, if any, to apply to this widget.
final ColorFilter? colorFilter;

Expand All @@ -501,6 +518,7 @@ class SvgPicture extends StatelessWidget {
semanticsLabel: semanticsLabel,
excludeFromSemantics: excludeFromSemantics,
clipBehavior: clipBehavior,
errorBuilder: errorBuilder,
colorFilter: colorFilter,
placeholderBuilder: placeholderBuilder,
clipViewbox: !allowDrawingOutsideViewBox,
Expand Down
2 changes: 1 addition & 1 deletion third_party/packages/flutter_svg/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: flutter_svg
description: An SVG rendering and widget library for Flutter, which allows painting and displaying Scalable Vector Graphics 1.1 files.
repository: https://github.com/flutter/packages/tree/main/third_party/packages/flutter_svg
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_svg%22
version: 2.0.16
version: 2.0.17

environment:
sdk: ^3.4.0
Expand Down
102 changes: 102 additions & 0 deletions third_party/packages/flutter_svg/test/widget_svg_test.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
Expand Down Expand Up @@ -767,6 +768,107 @@ void main() {
widgetFinder, matchesGoldenFile('golden_widget/image_$key.png'));
}
});

group('SvgPicture - errorBuilder', () {
testWidgets('SvgPicture.string handles failure',
(WidgetTester tester) async {
await tester.pumpWidget(
MediaQuery(
data: mediaQueryData,
child: SvgPicture.string(
'<!-- invalid svg -->',
errorBuilder: (
BuildContext context,
Object error,
StackTrace stackTrace,
) {
return const Directionality(
textDirection: TextDirection.ltr,
child: Text('image failed'),
);
},
),
),
);
await tester.pumpAndSettle();

expect(find.text('image failed'), findsOneWidget);
});

testWidgets('SvgPicture.memory handles failure',
(WidgetTester tester) async {
await tester.pumpWidget(
MediaQuery(
data: mediaQueryData,
child: SvgPicture.memory(
Uint8List.fromList(utf8.encode('<!-- invalid svg -->')),
errorBuilder: (
BuildContext context,
Object error,
StackTrace stackTrace,
) {
return const Directionality(
textDirection: TextDirection.ltr,
child: Text('image failed'),
);
},
),
),
);
await tester.pumpAndSettle();

expect(find.text('image failed'), findsOneWidget);
});

testWidgets('SvgPicture.asset handles failure',
(WidgetTester tester) async {
await tester.pumpWidget(
MediaQuery(
data: mediaQueryData,
child: SvgPicture.asset(
'/wrong path',
errorBuilder: (
BuildContext context,
Object error,
StackTrace stackTrace,
) {
return const Directionality(
textDirection: TextDirection.ltr,
child: Text('image failed'),
);
},
),
),
);
await tester.pumpAndSettle();

expect(find.text('image failed'), findsOneWidget);
});

testWidgets('SvgPicture.file handles failure', (WidgetTester tester) async {
await tester.pumpWidget(
MediaQuery(
data: mediaQueryData,
child: SvgPicture.file(
File('nosuchfile'),
errorBuilder: (
BuildContext context,
Object error,
StackTrace stackTrace,
) {
return const Directionality(
textDirection: TextDirection.ltr,
child: Text('image failed'),
);
},
),
),
);
await tester.pumpAndSettle();

expect(find.text('image failed'), findsOneWidget);
});
});
}

class FakeAssetBundle extends Fake implements AssetBundle {
Expand Down

0 comments on commit c6caf68

Please sign in to comment.