memory leak tracking & fixes
This commit is contained in:
parent
506190f882
commit
e883be1787
22 changed files with 107 additions and 13 deletions
|
@ -19,6 +19,7 @@ class Query extends ChangeNotifier {
|
|||
@override
|
||||
void dispose() {
|
||||
_focusRequestNotifier.dispose();
|
||||
_queryNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ class CollectionLens with ChangeNotifier {
|
|||
bool listenToSource, groupBursts, fixedSort;
|
||||
List<AvesEntry>? fixedSelection;
|
||||
|
||||
final Set<AvesEntry> _syntheticEntries = {};
|
||||
List<AvesEntry> _filteredSortedEntries = [];
|
||||
|
||||
Map<SectionKey, List<AvesEntry>> sections = Map.unmodifiable({});
|
||||
|
@ -101,6 +102,7 @@ class CollectionLens with ChangeNotifier {
|
|||
favourites.removeListener(_onFavouritesChanged);
|
||||
filterChangeNotifier.dispose();
|
||||
sortSectionChangeNotifier.dispose();
|
||||
_disposeSyntheticEntries();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -118,6 +120,11 @@ class CollectionLens with ChangeNotifier {
|
|||
fixedSelection: fixedSelection ?? this.fixedSelection,
|
||||
);
|
||||
|
||||
void _disposeSyntheticEntries() {
|
||||
_syntheticEntries.forEach((v) => v.dispose());
|
||||
_syntheticEntries.clear();
|
||||
}
|
||||
|
||||
bool get isEmpty => _filteredSortedEntries.isEmpty;
|
||||
|
||||
int get entryCount => _filteredSortedEntries.length;
|
||||
|
@ -182,6 +189,7 @@ class CollectionLens with ChangeNotifier {
|
|||
|
||||
void _applyFilters() {
|
||||
final entries = fixedSelection ?? (filters.contains(TrashFilter.instance) ? source.trashedEntries : source.visibleEntries);
|
||||
_disposeSyntheticEntries();
|
||||
_filteredSortedEntries = List.of(filters.isEmpty ? entries : entries.where((entry) => filters.every((filter) => filter.test(entry))));
|
||||
|
||||
if (groupBursts) {
|
||||
|
@ -196,6 +204,7 @@ class CollectionLens with ChangeNotifier {
|
|||
entries.sort(AvesEntrySort.compareByName);
|
||||
final mainEntry = entries.first;
|
||||
final burstEntry = mainEntry.copyWith(burstEntries: entries);
|
||||
_syntheticEntries.add(burstEntry);
|
||||
|
||||
entries.skip(1).toList().forEach((subEntry) {
|
||||
_filteredSortedEntries.remove(subEntry);
|
||||
|
|
|
@ -41,6 +41,12 @@ class _QueryBarState extends State<QueryBar> {
|
|||
_controller = TextEditingController(text: queryNotifier.value);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final clearButton = IconButton(
|
||||
|
|
|
@ -55,6 +55,12 @@ class _GridScaleGestureDetectorState<T> extends State<GridScaleGestureDetector<T
|
|||
|
||||
TileLayout get tileLayout => widget.tileLayout;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scaledSizeNotifier?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final gestureSettings = MediaQuery.gestureSettingsOf(context);
|
||||
|
@ -166,6 +172,7 @@ class _GridScaleGestureDetectorState<T> extends State<GridScaleGestureDetector<T
|
|||
|
||||
void _onScaleUpdate(ScaleUpdateDetails details) {
|
||||
if (_scaledSizeNotifier == null) return;
|
||||
|
||||
final s = details.scale;
|
||||
switch (tileLayout) {
|
||||
case TileLayout.mosaic:
|
||||
|
@ -181,6 +188,10 @@ class _GridScaleGestureDetectorState<T> extends State<GridScaleGestureDetector<T
|
|||
void _onScaleEnd(ScaleEndDetails details) {
|
||||
if (_scaledSizeNotifier == null) return;
|
||||
|
||||
final scaledSize = _scaledSizeNotifier!.value;
|
||||
_scaledSizeNotifier!.dispose();
|
||||
_scaledSizeNotifier = null;
|
||||
|
||||
final overlayEntry = _overlayEntry;
|
||||
_overlayEntry = null;
|
||||
if (overlayEntry != null) {
|
||||
|
@ -196,12 +207,11 @@ class _GridScaleGestureDetectorState<T> extends State<GridScaleGestureDetector<T
|
|||
switch (tileLayout) {
|
||||
case TileLayout.mosaic:
|
||||
case TileLayout.grid:
|
||||
preferredExtent = _scaledSizeNotifier!.value.width;
|
||||
preferredExtent = scaledSize.width;
|
||||
case TileLayout.list:
|
||||
preferredExtent = _scaledSizeNotifier!.value.height;
|
||||
preferredExtent = scaledSize.height;
|
||||
}
|
||||
final newExtent = tileExtentController.setUserPreferredExtent(preferredExtent);
|
||||
_scaledSizeNotifier = null;
|
||||
if (newExtent == oldExtent) {
|
||||
_applyingScale = false;
|
||||
} else {
|
||||
|
|
|
@ -44,6 +44,7 @@ class _OverlayCoordinateFilterChipState extends State<OverlayCoordinateFilterChi
|
|||
@override
|
||||
void dispose() {
|
||||
_unregisterWidget(widget);
|
||||
_idleBoundsNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ import 'package:flutter/services.dart';
|
|||
abstract class AvesSearchDelegate extends SearchDelegate {
|
||||
final String routeName;
|
||||
final bool canPop;
|
||||
final TextEditingController queryTextController = TextEditingController();
|
||||
final ValueNotifier<SearchBody?> currentBodyNotifier = ValueNotifier(null);
|
||||
|
||||
AvesSearchDelegate({
|
||||
required this.routeName,
|
||||
|
@ -31,6 +33,8 @@ abstract class AvesSearchDelegate extends SearchDelegate {
|
|||
if (kFlutterMemoryAllocationsEnabled) {
|
||||
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||
}
|
||||
queryTextController.dispose();
|
||||
currentBodyNotifier.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -113,8 +117,6 @@ abstract class AvesSearchDelegate extends SearchDelegate {
|
|||
|
||||
ScrollController? get suggestionsScrollController => null;
|
||||
|
||||
final TextEditingController queryTextController = TextEditingController();
|
||||
|
||||
final ProxyAnimation proxyAnimation = ProxyAnimation(kAlwaysDismissedAnimation);
|
||||
|
||||
@override
|
||||
|
@ -128,8 +130,6 @@ abstract class AvesSearchDelegate extends SearchDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
final ValueNotifier<SearchBody?> currentBodyNotifier = ValueNotifier(null);
|
||||
|
||||
SearchBody? get currentBody => currentBodyNotifier.value;
|
||||
|
||||
set currentBody(SearchBody? value) {
|
||||
|
|
|
@ -26,6 +26,12 @@ class _DebugAndroidAppSectionState extends State<DebugAndroidAppSection> with Au
|
|||
_loader = appService.getPackages();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_queryNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
|
|
|
@ -23,6 +23,12 @@ class _DebugAndroidCodecSectionState extends State<DebugAndroidCodecSection> wit
|
|||
_loader = AndroidDebugService.getCodecs();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_queryNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
|
|
|
@ -71,9 +71,15 @@ class _DebugGeneralSectionState extends State<DebugGeneralSection> with Automati
|
|||
debugPrint(' * report type=$reportType');
|
||||
groupBy(typedReports, (report) => report.trackedClass).forEach((trackedClass, classedReports) {
|
||||
debugPrint(' trackedClass=$trackedClass reports=${classedReports.length}');
|
||||
// classedReports.forEach((report) {
|
||||
// debugPrint(' phase=${report.phase} retainingPath=${report.retainingPath} detailedPath=${report.detailedPath} context=${report.context}');
|
||||
// });
|
||||
classedReports.forEach((report) {
|
||||
final phase = report.phase;
|
||||
final retainingPath = report.retainingPath;
|
||||
final detailedPath = report.detailedPath;
|
||||
final context = report.context;
|
||||
if (phase != null || retainingPath != null || detailedPath != null || context != null) {
|
||||
debugPrint(' phase=$phase retainingPath=$retainingPath detailedPath=$detailedPath context=$context');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -34,6 +34,13 @@ class _EditEntryTitleDescriptionDialogState extends State<EditEntryTitleDescript
|
|||
_descriptionTextController = TextEditingController(text: widget.initialDescription);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_titleTextController.dispose();
|
||||
_descriptionTextController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MediaQueryDataProvider(
|
||||
|
|
|
@ -52,7 +52,8 @@ class _RenameEntrySetPageState extends State<RenameEntrySetPage> {
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
_patternTextController.removeListener(_onUserPatternChanged);
|
||||
_patternTextController.dispose();
|
||||
_namingPatternNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ class _TagEditorPageState extends State<TagEditorPage> {
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
_newTagTextController.dispose();
|
||||
_newTagTextFocusNode.dispose();
|
||||
_expandedSectionNotifier.dispose();
|
||||
super.dispose();
|
||||
|
|
|
@ -39,6 +39,7 @@ class _CreateAlbumDialogState extends State<CreateAlbumDialog> {
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
_nameController.dispose();
|
||||
_nameFieldFocusNode.removeListener(_onFocus);
|
||||
_nameFieldFocusNode.dispose();
|
||||
|
|
|
@ -28,6 +28,13 @@ class _PasswordDialogState extends State<PasswordDialog> {
|
|||
_focusNode.requestFocus();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AvesDialog(
|
||||
|
|
|
@ -22,6 +22,12 @@ class _PinDialogState extends State<PinDialog> {
|
|||
bool _confirming = false;
|
||||
String? _firstPin;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
|
|
@ -37,6 +37,12 @@ class _AppPickPageState extends State<AppPickPage> {
|
|||
_loader = appService.getPackages();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_queryNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final useTvLayout = settings.useTvLayout;
|
||||
|
|
|
@ -253,6 +253,12 @@ class _AddressRowState extends State<_AddressRow> {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_addressLineNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final textScaleFactor = MediaQuery.textScaleFactorOf(context);
|
||||
|
|
|
@ -33,7 +33,7 @@ class HomeWidgetPainter {
|
|||
ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba,
|
||||
}) async {
|
||||
final widgetSizePx = Size(widthPx.toDouble(), heightPx.toDouble());
|
||||
late final ui.Image? entryImage;
|
||||
final ui.Image? entryImage;
|
||||
if (entry != null) {
|
||||
final extent = shape.extentPx(widgetSizePx, entry!) / devicePixelRatio;
|
||||
entryImage = await _getEntryImage(entry, extent);
|
||||
|
@ -48,6 +48,7 @@ class HomeWidgetPainter {
|
|||
canvas.clipPath(path);
|
||||
if (entryImage != null) {
|
||||
canvas.drawImage(entryImage, Offset(widgetSizePx.width - entryImage.width, widgetSizePx.height - entryImage.height) / 2, Paint());
|
||||
entryImage.dispose();
|
||||
} else {
|
||||
canvas.drawPaint(Paint()..shader = backgroundGradient.createShader(rect));
|
||||
}
|
||||
|
|
|
@ -41,6 +41,12 @@ class _MapAddressRowState extends State<MapAddressRow> {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_addressLineNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final textScaleFactor = MediaQuery.textScaleFactorOf(context);
|
||||
|
|
|
@ -29,6 +29,12 @@ class _LocaleSelectionPageState extends State<LocaleSelectionPage> {
|
|||
_selectedValue = settings.locale ?? LocaleTile.systemLocaleOption;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_queryNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final useTvLayout = settings.useTvLayout;
|
||||
|
|
|
@ -55,6 +55,7 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> {
|
|||
@override
|
||||
void dispose() {
|
||||
_unregisterWidget(widget);
|
||||
_expandedDirectoryNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> with WidgetsBindi
|
|||
_unregisterWidget(widget);
|
||||
_serviceMapController?.dispose();
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_sizeNotifier.removeListener(_onSizeChange);
|
||||
_sizeNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue