diff --git a/packages/vector_graphics/CHANGELOG.md b/packages/vector_graphics/CHANGELOG.md index a51da2ef873..dd9cf3fd563 100644 --- a/packages/vector_graphics/CHANGELOG.md +++ b/packages/vector_graphics/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.1.15 + +* Updates error handling in VectorGraphicWidget to handle errors when the bytes of the graphic cannot be loaded. + ## 1.1.14 * Relaxes dependency constraint on vector_graphics_codec. diff --git a/packages/vector_graphics/lib/src/vector_graphics.dart b/packages/vector_graphics/lib/src/vector_graphics.dart index eb5eca76fd1..c849a81dd09 100644 --- a/packages/vector_graphics/lib/src/vector_graphics.dart +++ b/packages/vector_graphics/lib/src/vector_graphics.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; import 'dart:math' as math; import 'dart:ui' as ui; @@ -315,14 +316,14 @@ class _VectorGraphicWidgetState extends State { void didChangeDependencies() { locale = Localizations.maybeLocaleOf(context); textDirection = Directionality.maybeOf(context); - _loadAssetBytes(); + unawaited(_loadAssetBytes()); super.didChangeDependencies(); } @override void didUpdateWidget(covariant VectorGraphic oldWidget) { if (oldWidget.loader != widget.loader) { - _loadAssetBytes(); + unawaited(_loadAssetBytes()); } super.didUpdateWidget(oldWidget); } @@ -358,12 +359,6 @@ class _VectorGraphicWidgetState extends State { textDirection: key.textDirection, clipViewbox: key.clipViewbox, loader: loader, - onError: (Object error, StackTrace? stackTrace) { - return _handleError( - error, - stackTrace, - ); - }, ); }).then((PictureInfo pictureInfo) { return _PictureData(pictureInfo, 0, key); @@ -376,13 +371,17 @@ class _VectorGraphicWidgetState extends State { } void _handleError(Object error, StackTrace? stackTrace) { + if (!mounted) { + return; + } + setState(() { _error = error; _stackTrace = stackTrace; }); } - void _loadAssetBytes() { + Future _loadAssetBytes() async { // First check if we have an avilable picture and use this immediately. final Object loaderKey = widget.loader.cacheKey(context); final _PictureKey key = @@ -398,7 +397,9 @@ class _VectorGraphicWidgetState extends State { } // If not, then check if there is a pending load. final BytesLoader loader = widget.loader; - _loadPicture(context, key, loader).then((_PictureData data) { + + try { + final _PictureData data = await _loadPicture(context, key, loader); data.count += 1; // The widget may have changed, requesting a new vector graphic before @@ -407,14 +408,18 @@ class _VectorGraphicWidgetState extends State { _maybeReleasePicture(data); return; } + if (data.count == 1) { _livePictureCache[key] = data; } + setState(() { _maybeReleasePicture(_pictureInfo); _pictureInfo = data; }); - }); + } catch (error, stackTrace) { + _handleError(error, stackTrace); + } } static final bool _webRenderObject = useHtmlRenderObject(); diff --git a/packages/vector_graphics/pubspec.yaml b/packages/vector_graphics/pubspec.yaml index 2b74c23555a..8dd286357ba 100644 --- a/packages/vector_graphics/pubspec.yaml +++ b/packages/vector_graphics/pubspec.yaml @@ -2,7 +2,7 @@ name: vector_graphics description: A vector graphics rendering package for Flutter using a binary encoding. repository: https://github.com/flutter/packages/tree/main/packages/vector_graphics issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+vector_graphics%22 -version: 1.1.14 +version: 1.1.15 environment: sdk: ^3.4.0 diff --git a/packages/vector_graphics/test/vector_graphics_test.dart b/packages/vector_graphics/test/vector_graphics_test.dart index 33da9600af7..8d8bc7fcb12 100644 --- a/packages/vector_graphics/test/vector_graphics_test.dart +++ b/packages/vector_graphics/test/vector_graphics_test.dart @@ -653,6 +653,28 @@ void main() { expect(matrix.row0.x, -1); expect(matrix.row1.y, 1); }); + + testWidgets('VectorGraphicsWidget can handle errors from bytes loader', + (WidgetTester tester) async { + await tester.pumpWidget( + VectorGraphic( + loader: const ThrowingBytesLoader(), + width: 100, + height: 100, + errorBuilder: + (BuildContext context, Object error, StackTrace stackTrace) { + return const Directionality( + textDirection: TextDirection.ltr, + child: Text('Error is handled'), + ); + }, + ), + ); + await tester.pumpAndSettle(); + + expect(find.text('Error is handled'), findsOneWidget); + expect(tester.takeException(), isNull); + }); } class TestBundle extends Fake implements AssetBundle { @@ -719,3 +741,12 @@ class TestBytesLoader extends BytesLoader { @override String toString() => 'TestBytesLoader: $source'; } + +class ThrowingBytesLoader extends BytesLoader { + const ThrowingBytesLoader(); + + @override + Future loadBytes(BuildContext? context) { + throw UnimplementedError('Test exception'); + } +}