From 4e811cc37c13445dc53b7c81789085936ea81be4 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Mon, 17 Jun 2024 21:03:55 +0200 Subject: [PATCH] leaks: imageinfo, notifiers --- lib/widgets/collection/collection_grid.dart | 4 +++- lib/widgets/dialogs/pick_dialogs/album_pick_page.dart | 4 +++- lib/widgets/dialogs/pick_dialogs/item_pick_page.dart | 7 +++++-- lib/widgets/viewer/overlay/histogram.dart | 6 ++++-- lib/widgets/viewer/slideshow_page.dart | 4 +++- lib/widgets/viewer/visual/raster.dart | 4 ++++ lib/widgets/viewer/visual/vector.dart | 4 ++++ plugins/aves_video/lib/src/controller.dart | 1 + 8 files changed, 27 insertions(+), 7 deletions(-) diff --git a/lib/widgets/collection/collection_grid.dart b/lib/widgets/collection/collection_grid.dart index 08832097a..d4d54012f 100644 --- a/lib/widgets/collection/collection_grid.dart +++ b/lib/widgets/collection/collection_grid.dart @@ -113,11 +113,13 @@ class _CollectionGridContent extends StatefulWidget { class _CollectionGridContentState extends State<_CollectionGridContent> { final ValueNotifier _focusedItemNotifier = ValueNotifier(null); final ValueNotifier _isScrollingNotifier = ValueNotifier(false); + final ValueNotifier _selectingAppModeNotifier = ValueNotifier(AppMode.pickFilteredMediaInternal); @override void dispose() { _focusedItemNotifier.dispose(); _isScrollingNotifier.dispose(); + _selectingAppModeNotifier.dispose(); super.dispose(); } @@ -252,7 +254,7 @@ class _CollectionGridContentState extends State<_CollectionGridContent> { if (selection.isSelecting) { child = MultiProvider( providers: [ - ListenableProvider>.value(value: ValueNotifier(AppMode.pickFilteredMediaInternal)), + ListenableProvider>.value(value: _selectingAppModeNotifier), ChangeNotifierProvider>.value(value: selection), ], child: child, diff --git a/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart b/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart index 634341c88..ae7d7cc7e 100644 --- a/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart +++ b/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart @@ -64,6 +64,7 @@ class _AlbumPickPage extends StatefulWidget { class _AlbumPickPageState extends State<_AlbumPickPage> { final ValueNotifier _appBarHeightNotifier = ValueNotifier(0); + final ValueNotifier _appModeNotifier = ValueNotifier(AppMode.pickFilterInternal); CollectionSource get source => widget.source; @@ -93,13 +94,14 @@ class _AlbumPickPageState extends State<_AlbumPickPage> { @override void dispose() { _appBarHeightNotifier.dispose(); + _appModeNotifier.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return ListenableProvider>.value( - value: ValueNotifier(AppMode.pickFilterInternal), + value: _appModeNotifier, child: Selector( selector: (context, s) => (s.albumGroupFactor, s.albumSortFactor), builder: (context, s, child) { diff --git a/lib/widgets/dialogs/pick_dialogs/item_pick_page.dart b/lib/widgets/dialogs/pick_dialogs/item_pick_page.dart index 490f5edb7..49fe4266a 100644 --- a/lib/widgets/dialogs/pick_dialogs/item_pick_page.dart +++ b/lib/widgets/dialogs/pick_dialogs/item_pick_page.dart @@ -29,20 +29,23 @@ class ItemPickPage extends StatefulWidget { } class _ItemPickPageState extends State { + final ValueNotifier _appModeNotifier = ValueNotifier(AppMode.initialization); + CollectionLens get collection => widget.collection; @override void dispose() { collection.dispose(); + _appModeNotifier.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final liveFilter = collection.filters.firstWhereOrNull((v) => v is QueryFilter && v.live) as QueryFilter?; - final mode = widget.canRemoveFilters ? AppMode.pickUnfilteredMediaInternal : AppMode.pickFilteredMediaInternal; + _appModeNotifier.value = widget.canRemoveFilters ? AppMode.pickUnfilteredMediaInternal : AppMode.pickFilteredMediaInternal; return ListenableProvider>.value( - value: ValueNotifier(mode), + value: _appModeNotifier, child: AvesScaffold( body: SelectionProvider( child: QueryProvider( diff --git a/lib/widgets/viewer/overlay/histogram.dart b/lib/widgets/viewer/overlay/histogram.dart index 62a4e9ca8..60ec190c3 100644 --- a/lib/widgets/viewer/overlay/histogram.dart +++ b/lib/widgets/viewer/overlay/histogram.dart @@ -53,8 +53,10 @@ class _ImageHistogramState extends State { void _registerWidget(ImageHistogram widget) { _imageStream = imageProvider.resolve(ImageConfiguration.empty); - _imageListener = ImageStreamListener((image, synchronousCall) { - _updateLevels(image); + _imageListener = ImageStreamListener((image, synchronousCall) async { + // implementer is responsible for disposing the provided `ImageInfo` + await _updateLevels(image); + image.dispose(); }); _imageStream?.addListener(_imageListener); } diff --git a/lib/widgets/viewer/slideshow_page.dart b/lib/widgets/viewer/slideshow_page.dart index 12331b58a..a67ca879d 100644 --- a/lib/widgets/viewer/slideshow_page.dart +++ b/lib/widgets/viewer/slideshow_page.dart @@ -35,6 +35,7 @@ class SlideshowPage extends StatefulWidget { } class _SlideshowPageState extends State { + final ValueNotifier _appModeNotifier = ValueNotifier(AppMode.slideshow); late ViewerController _viewerController; late CollectionLens _slideshowCollection; AvesEntry? _initialEntry; @@ -51,6 +52,7 @@ class _SlideshowPageState extends State { @override void dispose() { + _appModeNotifier.dispose(); _disposeViewerController(); super.dispose(); } @@ -59,7 +61,7 @@ class _SlideshowPageState extends State { Widget build(BuildContext context) { final initialEntry = _initialEntry; return ListenableProvider>.value( - value: ValueNotifier(AppMode.slideshow), + value: _appModeNotifier, child: AvesScaffold( body: initialEntry == null ? EmptyContent( diff --git a/lib/widgets/viewer/visual/raster.dart b/lib/widgets/viewer/visual/raster.dart index 7e38eed88..b4c015b6c 100644 --- a/lib/widgets/viewer/visual/raster.dart +++ b/lib/widgets/viewer/visual/raster.dart @@ -41,6 +41,7 @@ class _RasterImageViewState extends State { ImageStream? _fullImageStream; late ImageStreamListener _fullImageListener; final ValueNotifier _fullImageLoaded = ValueNotifier(false); + ImageInfo? _fullImageInfo; AvesEntry get entry => widget.entry; @@ -101,10 +102,13 @@ class _RasterImageViewState extends State { void _unregisterFullImage() { _fullImageStream?.removeListener(_fullImageListener); _fullImageStream = null; + _fullImageInfo?.dispose(); } void _onFullImageCompleted(ImageInfo image, bool synchronousCall) { + // implementer is responsible for disposing the provided `ImageInfo` _unregisterFullImage(); + _fullImageInfo = image; _fullImageLoaded.value = true; FullImageLoadedNotification(entry, fullImageProvider).dispatch(context); } diff --git a/lib/widgets/viewer/visual/vector.dart b/lib/widgets/viewer/visual/vector.dart index b76cbb5ab..95ced38ed 100644 --- a/lib/widgets/viewer/visual/vector.dart +++ b/lib/widgets/viewer/visual/vector.dart @@ -37,6 +37,7 @@ class _VectorImageViewState extends State { ImageStream? _fullImageStream; late ImageStreamListener _fullImageListener; final ValueNotifier _fullImageLoaded = ValueNotifier(false); + ImageInfo? _fullImageInfo; AvesEntry get entry => widget.entry; @@ -91,10 +92,13 @@ class _VectorImageViewState extends State { void _unregisterFullImage() { _fullImageStream?.removeListener(_fullImageListener); _fullImageStream = null; + _fullImageInfo?.dispose(); } void _onFullImageCompleted(ImageInfo image, bool synchronousCall) { + // implementer is responsible for disposing the provided `ImageInfo` _unregisterFullImage(); + _fullImageInfo = image; _fullImageLoaded.value = true; } diff --git a/plugins/aves_video/lib/src/controller.dart b/plugins/aves_video/lib/src/controller.dart index 7babf040f..8fac10917 100644 --- a/plugins/aves_video/lib/src/controller.dart +++ b/plugins/aves_video/lib/src/controller.dart @@ -47,6 +47,7 @@ abstract class AvesVideoController with ABRepeatMixin { if (kFlutterMemoryAllocationsEnabled) { FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); } + abRepeatNotifier.dispose(); _entry.visualChangeNotifier.removeListener(onVisualChanged); await _savePlaybackState(); }