Skip to content

Commit 4e01110

Browse files
[vector_graphics] Fix memory leaks and activate leak testing [prod-leak-fix] (#8373)
The package manipulates disposable objects. This PR - Fixes some memory leaks. - Activates leak testing to make sure disposable objects are correctly disposed. See the documentation: https://github.com/dart-lang/leak_tracker/blob/main/doc%2Fleak_tracking%2FDETECT.md
1 parent 02c6fef commit 4e01110

File tree

7 files changed

+36
-11
lines changed

7 files changed

+36
-11
lines changed

packages/vector_graphics/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.1.16
2+
3+
* Fixes some memory leaks by disposing undisposed `ImageInfo`, `ui.Picture` and `Picture`.
4+
15
## 1.1.15
26

37
* Updates error handling in VectorGraphicWidget to handle errors when the bytes of the graphic cannot be loaded.

packages/vector_graphics/lib/src/listener.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ class FlutterVectorGraphicsListener extends VectorGraphicsCodecListener {
252252
final List<_TextConfig> _textConfig = <_TextConfig>[];
253253
final List<_TextPosition> _textPositions = <_TextPosition>[];
254254
final List<Future<void>> _pendingImages = <Future<void>>[];
255-
final Map<int, Image> _images = <int, Image>{};
255+
final Map<int, ImageInfo> _images = <int, ImageInfo>{};
256256
final Map<int, _PatternState> _patterns = <int, _PatternState>{};
257257
Path? _currentPath;
258258
Size _size = Size.zero;
@@ -283,7 +283,7 @@ class FlutterVectorGraphicsListener extends VectorGraphicsCodecListener {
283283
try {
284284
return PictureInfo._(_recorder.endRecording(), _size);
285285
} finally {
286-
for (final Image image in _images.values) {
286+
for (final ImageInfo image in _images.values) {
287287
image.dispose();
288288
}
289289
_images.clear();
@@ -746,7 +746,7 @@ class FlutterVectorGraphicsListener extends VectorGraphicsCodecListener {
746746
listener = ImageStreamListener(
747747
(ImageInfo image, bool synchronousCall) {
748748
cacheCompleter.removeListener(listener);
749-
_images[imageId] = image.image;
749+
_images[imageId] = image;
750750
completer.complete();
751751
},
752752
onError: (Object exception, StackTrace? stackTrace) {
@@ -773,7 +773,7 @@ class FlutterVectorGraphicsListener extends VectorGraphicsCodecListener {
773773
@override
774774
void onDrawImage(int imageId, double x, double y, double width, double height,
775775
Float64List? transform) {
776-
final Image image = _images[imageId]!;
776+
final Image image = _images[imageId]!.image;
777777
if (transform != null) {
778778
_canvas.save();
779779
_canvas.transform(transform);

packages/vector_graphics/lib/src/render_vector_graphic.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ class RenderVectorGraphic extends RenderBox {
213213

214214
final ui.Image pending =
215215
rasterPicture.toImageSync(scaledWidth, scaledHeight);
216+
rasterPicture.dispose();
216217
return RasterData(pending, 0, key);
217218
}
218219

packages/vector_graphics/lib/src/vector_graphics.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,10 @@ class _VectorGraphicWidgetState extends State<VectorGraphic> {
340340
return;
341341
}
342342
data.count -= 1;
343-
if (data.count == 0 && _livePictureCache.containsKey(data.key)) {
344-
_livePictureCache.remove(data.key);
343+
if (data.count == 0) {
344+
if (_livePictureCache.containsKey(data.key)) {
345+
_livePictureCache.remove(data.key);
346+
}
345347
data.pictureInfo.picture.dispose();
346348
}
347349
}
@@ -382,7 +384,7 @@ class _VectorGraphicWidgetState extends State<VectorGraphic> {
382384
}
383385

384386
Future<void> _loadAssetBytes() async {
385-
// First check if we have an avilable picture and use this immediately.
387+
// First check if we have an available picture and use this immediately.
386388
final Object loaderKey = widget.loader.cacheKey(context);
387389
final _PictureKey key =
388390
_PictureKey(loaderKey, locale, textDirection, widget.clipViewbox);

packages/vector_graphics/pubspec.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: vector_graphics
22
description: A vector graphics rendering package for Flutter using a binary encoding.
33
repository: https://github.com/flutter/packages/tree/main/packages/vector_graphics
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+vector_graphics%22
5-
version: 1.1.15
5+
version: 1.1.16
66

77
environment:
88
sdk: ^3.4.0
@@ -17,6 +17,7 @@ dependencies:
1717
dev_dependencies:
1818
flutter_test:
1919
sdk: flutter
20+
leak_tracker_flutter_testing: any
2021
vector_graphics_compiler: ^1.1.11+1
2122

2223
platforms:
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
8+
9+
Future<void> testExecutable(FutureOr<void> Function() testMain) async {
10+
LeakTesting.enable();
11+
LeakTracking.warnForUnsupportedPlatforms = false;
12+
await testMain();
13+
}

packages/vector_graphics/test/vector_graphics_test.dart

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,9 @@ void main() {
442442
);
443443
await tester.pumpAndSettle();
444444

445-
expect(await completer.future, isA<PictureInfo>());
445+
final PictureInfo picture = await completer.future;
446+
addTearDown(picture.picture.dispose);
447+
expect(picture, isA<PictureInfo>());
446448
expect(debugLastLocale, const Locale('fr', 'CH'));
447449
expect(debugLastTextDirection, TextDirection.rtl);
448450
});
@@ -475,7 +477,9 @@ void main() {
475477
);
476478
await tester.pumpAndSettle();
477479

478-
expect(await completer.future, isA<PictureInfo>());
480+
final PictureInfo picture = await completer.future;
481+
addTearDown(picture.picture.dispose);
482+
expect(picture, isA<PictureInfo>());
479483
expect(debugLastLocale, PlatformDispatcher.instance.locale);
480484
expect(debugLastTextDirection, TextDirection.ltr);
481485
});
@@ -584,7 +588,7 @@ void main() {
584588
expect(imageCache.statusForKey(imageKey).live, false);
585589
expect(imageCache.statusForKey(imageKey).keepAlive, true);
586590

587-
// A blue square, becuase the image is available now.
591+
// A blue square, because the image is available now.
588592
await expectLater(
589593
find.byKey(key),
590594
matchesGoldenFile('vg_with_image_blue.png'),

0 commit comments

Comments
 (0)