Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 24 additions & 13 deletions packages/vector_graphics/lib/src/vector_graphics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ import 'dart:ui' as ui;

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

import 'package:vector_graphics_codec/vector_graphics_codec.dart';

import 'html_render_vector_graphics.dart';
import 'loader.dart';
import 'listener.dart';
import 'loader.dart';
import 'render_object_selection.dart';
import 'render_vector_graphic.dart';

Expand Down Expand Up @@ -309,8 +308,8 @@ class _VectorGraphicWidgetState extends State<VectorGraphic> {

static final Map<_PictureKey, _PictureData> _livePictureCache =
<_PictureKey, _PictureData>{};
static final Map<_PictureKey, Future<_PictureData>> _pendingPictures =
<_PictureKey, Future<_PictureData>>{};
static final Map<_PictureKey, Future<_PictureData?>> _pendingPictures =
<_PictureKey, Future<_PictureData?>>{};

@override
void didChangeDependencies() {
Expand Down Expand Up @@ -346,12 +345,13 @@ class _VectorGraphicWidgetState extends State<VectorGraphic> {
}
}

Future<_PictureData> _loadPicture(
Future<_PictureData?> _loadPicture(
BuildContext context, _PictureKey key, BytesLoader loader) {
if (_pendingPictures.containsKey(key)) {
return _pendingPictures[key]!;
}
final Future<_PictureData> result =

final Future<_PictureData?> result =
loader.loadBytes(context).then((ByteData data) {
return decodeVectorGraphics(
data,
Expand All @@ -360,19 +360,25 @@ class _VectorGraphicWidgetState extends State<VectorGraphic> {
clipViewbox: key.clipViewbox,
loader: loader,
onError: (Object error, StackTrace? stackTrace) {
return _handleError(
error,
stackTrace,
);
_handleError(error, stackTrace);
},
);
}).then((PictureInfo pictureInfo) {
}).then<_PictureData?>((PictureInfo pictureInfo) {
return _PictureData(pictureInfo, 0, key);
}).catchError((Object error, StackTrace stackTrace) {
if (widget.errorBuilder != null) {
_handleError(error, stackTrace);
return null;
} else {
// ignore: only_throw_errors
throw error;
}
});
_pendingPictures[key] = result;
result.whenComplete(() {
_pendingPictures.remove(key);
});

return result;
}

Expand All @@ -384,7 +390,7 @@ class _VectorGraphicWidgetState extends State<VectorGraphic> {
}

void _loadAssetBytes() {
// First check if we have an avilable picture and use this immediately.
// First check if we have an available picture and use this immediately.
final Object loaderKey = widget.loader.cacheKey(context);
final _PictureKey key =
_PictureKey(loaderKey, locale, textDirection, widget.clipViewbox);
Expand All @@ -399,7 +405,12 @@ class _VectorGraphicWidgetState extends State<VectorGraphic> {
}
// If not, then check if there is a pending load.
final BytesLoader loader = widget.loader;
_loadPicture(context, key, loader).then((_PictureData data) {
_loadPicture(context, key, loader).then((_PictureData? data) {
if (data == null) {
// If data is null, an error occurred, and it has been handled.
return;
}

data.count += 1;

// The widget may have changed, requesting a new vector graphic before
Expand Down
45 changes: 45 additions & 0 deletions packages/vector_graphics/test/vector_graphics_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,31 @@ void main() {
expect(matrix.row0.x, -1);
expect(matrix.row1.y, 1);
});

testWidgets('Displays errorBuilder on loadBytes failure', (WidgetTester tester) async {
const ErrorBytesLoader loader = ErrorBytesLoader();
final GlobalKey key = GlobalKey();

// Build the VectorGraphic widget with an errorBuilder
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: VectorGraphic(
key: key,
loader: loader,
width: 100,
height: 100,
errorBuilder: (BuildContext context, Object error, StackTrace stackTrace) {
return const Text('Error occurred');
},
),
));

// Let the widget rebuild and show the error
await tester.pumpAndSettle();

// Expect that the error widget is displayed
expect(find.text('Error occurred'), findsOneWidget);
});
}

class TestBundle extends Fake implements AssetBundle {
Expand Down Expand Up @@ -719,3 +744,23 @@ class TestBytesLoader extends BytesLoader {
@override
String toString() => 'TestBytesLoader: $source';
}

class ErrorBytesLoader extends BytesLoader {
const ErrorBytesLoader();

@override
Future<ByteData> loadBytes(BuildContext? context) async {
throw StateError('Load failure');
}

@override
int get hashCode => 0;

@override
bool operator ==(Object other) {
return other is ErrorBytesLoader;
}

@override
String toString() => 'ErrorBytesLoader';
}