memory leak tracking & fixes
This commit is contained in:
parent
300d232b9b
commit
4c07a9da43
44 changed files with 270 additions and 45 deletions
|
@ -3,7 +3,9 @@ import 'dart:isolate';
|
||||||
import 'package:aves/app_flavor.dart';
|
import 'package:aves/app_flavor.dart';
|
||||||
import 'package:aves/services/common/services.dart';
|
import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves/widgets/aves_app.dart';
|
import 'package:aves/widgets/aves_app.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:leak_tracker/leak_tracker.dart';
|
||||||
|
|
||||||
void mainCommon(AppFlavor flavor, {Map? debugIntentData}) {
|
void mainCommon(AppFlavor flavor, {Map? debugIntentData}) {
|
||||||
// HttpClient.enableTimelineLogging = true; // enable network traffic logging
|
// HttpClient.enableTimelineLogging = true; // enable network traffic logging
|
||||||
|
@ -35,5 +37,9 @@ void mainCommon(AppFlavor flavor, {Map? debugIntentData}) {
|
||||||
// ErrorWidget.builder = (details) => ErrorWidget(details.exception);
|
// ErrorWidget.builder = (details) => ErrorWidget(details.exception);
|
||||||
// cf https://docs.flutter.dev/testing/errors
|
// cf https://docs.flutter.dev/testing/errors
|
||||||
|
|
||||||
|
LeakTracking.start();
|
||||||
|
MemoryAllocations.instance.addListener(
|
||||||
|
(event) => LeakTracking.dispatchObjectEvent(event.toMap()),
|
||||||
|
);
|
||||||
runApp(AvesApp(flavor: flavor, debugIntentData: debugIntentData));
|
runApp(AvesApp(flavor: flavor, debugIntentData: debugIntentData));
|
||||||
}
|
}
|
||||||
|
|
|
@ -347,6 +347,11 @@ class Dependencies {
|
||||||
licenseUrl: 'https://github.com/material-foundation/material-color-utilities/tree/main/dart/LICENSE',
|
licenseUrl: 'https://github.com/material-foundation/material-color-utilities/tree/main/dart/LICENSE',
|
||||||
sourceUrl: 'https://github.com/material-foundation/material-color-utilities/tree/main/dart',
|
sourceUrl: 'https://github.com/material-foundation/material-color-utilities/tree/main/dart',
|
||||||
),
|
),
|
||||||
|
Dependency(
|
||||||
|
name: 'Memory Leak Tracker',
|
||||||
|
license: bsd3,
|
||||||
|
sourceUrl: 'https://github.com/dart-lang/leak_tracker',
|
||||||
|
),
|
||||||
Dependency(
|
Dependency(
|
||||||
name: 'Path',
|
name: 'Path',
|
||||||
license: bsd3,
|
license: bsd3,
|
||||||
|
|
|
@ -49,8 +49,7 @@ class AvesEntry with AvesEntryBase {
|
||||||
@override
|
@override
|
||||||
final AChangeNotifier visualChangeNotifier = AChangeNotifier();
|
final AChangeNotifier visualChangeNotifier = AChangeNotifier();
|
||||||
|
|
||||||
final AChangeNotifier metadataChangeNotifier = AChangeNotifier();
|
final AChangeNotifier metadataChangeNotifier = AChangeNotifier(), addressChangeNotifier = AChangeNotifier();
|
||||||
final AChangeNotifier addressChangeNotifier = AChangeNotifier();
|
|
||||||
|
|
||||||
AvesEntry({
|
AvesEntry({
|
||||||
required int? id,
|
required int? id,
|
||||||
|
@ -72,6 +71,13 @@ class AvesEntry with AvesEntryBase {
|
||||||
required this.origin,
|
required this.origin,
|
||||||
this.burstEntries,
|
this.burstEntries,
|
||||||
}) : id = id ?? 0 {
|
}) : id = id ?? 0 {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: 'aves',
|
||||||
|
className: '$AvesEntry',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.sourceTitle = sourceTitle;
|
this.sourceTitle = sourceTitle;
|
||||||
this.dateModifiedSecs = dateModifiedSecs;
|
this.dateModifiedSecs = dateModifiedSecs;
|
||||||
|
@ -181,6 +187,9 @@ class AvesEntry with AvesEntryBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
|
}
|
||||||
visualChangeNotifier.dispose();
|
visualChangeNotifier.dispose();
|
||||||
metadataChangeNotifier.dispose();
|
metadataChangeNotifier.dispose();
|
||||||
addressChangeNotifier.dispose();
|
addressChangeNotifier.dispose();
|
||||||
|
|
|
@ -16,6 +16,12 @@ class Query extends ChangeNotifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_focusRequestNotifier.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
bool _enabled = false;
|
bool _enabled = false;
|
||||||
|
|
||||||
bool get enabled => _enabled;
|
bool get enabled => _enabled;
|
||||||
|
|
|
@ -99,6 +99,8 @@ class CollectionLens with ChangeNotifier {
|
||||||
..forEach((sub) => sub.cancel())
|
..forEach((sub) => sub.cancel())
|
||||||
..clear();
|
..clear();
|
||||||
favourites.removeListener(_onFavouritesChanged);
|
favourites.removeListener(_onFavouritesChanged);
|
||||||
|
filterChangeNotifier.dispose();
|
||||||
|
sortSectionChangeNotifier.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves/utils/android_file_utils.dart';
|
import 'package:aves/utils/android_file_utils.dart';
|
||||||
import 'package:aves/view/view.dart';
|
import 'package:aves/view/view.dart';
|
||||||
import 'package:aves_model/aves_model.dart';
|
import 'package:aves_model/aves_model.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
@ -92,12 +93,22 @@ class Analyzer {
|
||||||
|
|
||||||
Analyzer() {
|
Analyzer() {
|
||||||
debugPrint('$runtimeType create');
|
debugPrint('$runtimeType create');
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: 'aves',
|
||||||
|
className: '$Analyzer',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
_serviceStateNotifier.addListener(_onServiceStateChanged);
|
_serviceStateNotifier.addListener(_onServiceStateChanged);
|
||||||
_source.stateNotifier.addListener(_onSourceStateChanged);
|
_source.stateNotifier.addListener(_onSourceStateChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
debugPrint('$runtimeType dispose');
|
debugPrint('$runtimeType dispose');
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
|
}
|
||||||
_serviceStateNotifier.removeListener(_onServiceStateChanged);
|
_serviceStateNotifier.removeListener(_onServiceStateChanged);
|
||||||
_source.stateNotifier.removeListener(_onSourceStateChanged);
|
_source.stateNotifier.removeListener(_onSourceStateChanged);
|
||||||
_stopUpdateTimer();
|
_stopUpdateTimer();
|
||||||
|
|
|
@ -32,6 +32,12 @@ class _LicensesState extends State<Licenses> {
|
||||||
_sortPackages();
|
_sortPackages();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_expandedNotifier.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void _sortPackages() {
|
void _sortPackages() {
|
||||||
int compare(Dependency a, Dependency b) => compareAsciiUpperCase(a.name, b.name);
|
int compare(Dependency a, Dependency b) => compareAsciiUpperCase(a.name, b.name);
|
||||||
_platform.sort(compare);
|
_platform.sort(compare);
|
||||||
|
|
|
@ -644,7 +644,6 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
||||||
builder: (context) => MapPage(collection: mapCollection),
|
builder: (context) => MapPage(collection: mapCollection),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
mapCollection.dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _goToSlideshow(BuildContext context) {
|
void _goToSlideshow(BuildContext context) {
|
||||||
|
|
|
@ -32,20 +32,21 @@ class EntryListDetailsTheme extends StatelessWidget {
|
||||||
final titleStyle = textTheme.bodyMedium!;
|
final titleStyle = textTheme.bodyMedium!;
|
||||||
final captionStyle = textTheme.bodySmall!;
|
final captionStyle = textTheme.bodySmall!;
|
||||||
|
|
||||||
final titleLineHeight = (RenderParagraph(
|
final titleLineHeightParagraph = RenderParagraph(
|
||||||
TextSpan(text: 'Fake Title', style: titleStyle),
|
TextSpan(text: 'Fake Title', style: titleStyle),
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
textScaleFactor: textScaleFactor,
|
textScaleFactor: textScaleFactor,
|
||||||
)..layout(const BoxConstraints(), parentUsesSize: true))
|
)..layout(const BoxConstraints(), parentUsesSize: true);
|
||||||
.getMaxIntrinsicHeight(double.infinity);
|
final titleLineHeight = titleLineHeightParagraph.getMaxIntrinsicHeight(double.infinity);
|
||||||
|
titleLineHeightParagraph.dispose();
|
||||||
|
|
||||||
final captionLineHeight = (RenderParagraph(
|
final captionLineHeightParagraph = RenderParagraph(
|
||||||
TextSpan(text: formatDateTime(DateTime.now(), locale, use24hour), style: captionStyle),
|
TextSpan(text: formatDateTime(DateTime.now(), locale, use24hour), style: captionStyle),
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
textScaleFactor: textScaleFactor,
|
textScaleFactor: textScaleFactor,
|
||||||
strutStyle: AStyles.overflowStrut,
|
strutStyle: AStyles.overflowStrut,
|
||||||
)..layout(const BoxConstraints(), parentUsesSize: true))
|
)..layout(const BoxConstraints(), parentUsesSize: true);
|
||||||
.getMaxIntrinsicHeight(double.infinity);
|
final captionLineHeight = captionLineHeightParagraph.getMaxIntrinsicHeight(double.infinity);
|
||||||
|
|
||||||
var titleMaxLines = 1;
|
var titleMaxLines = 1;
|
||||||
var showDate = false;
|
var showDate = false;
|
||||||
|
|
|
@ -49,6 +49,7 @@ abstract class ChooserQuickButtonState<T extends ChooserQuickButton<U>, U> exten
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_animationController?.dispose();
|
_animationController?.dispose();
|
||||||
_clearChooserOverlayEntry();
|
_clearChooserOverlayEntry();
|
||||||
|
_chooserValueNotifier.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,14 +114,15 @@ class _AnimatedDiffTextState extends State<AnimatedDiffText> with SingleTickerPr
|
||||||
}
|
}
|
||||||
|
|
||||||
Size textSize(String text) {
|
Size textSize(String text) {
|
||||||
final para = RenderParagraph(
|
final paragraph = RenderParagraph(
|
||||||
TextSpan(text: text, style: widget.textStyle),
|
TextSpan(text: text, style: widget.textStyle),
|
||||||
textDirection: Directionality.of(context),
|
textDirection: Directionality.of(context),
|
||||||
textScaleFactor: MediaQuery.textScaleFactorOf(context),
|
textScaleFactor: MediaQuery.textScaleFactorOf(context),
|
||||||
strutStyle: widget.strutStyle,
|
strutStyle: widget.strutStyle,
|
||||||
)..layout(const BoxConstraints(), parentUsesSize: true);
|
)..layout(const BoxConstraints(), parentUsesSize: true);
|
||||||
final width = para.getMaxIntrinsicWidth(double.infinity);
|
final width = paragraph.getMaxIntrinsicWidth(double.infinity);
|
||||||
final height = para.getMaxIntrinsicHeight(double.infinity);
|
final height = paragraph.getMaxIntrinsicHeight(double.infinity);
|
||||||
|
paragraph.dispose();
|
||||||
return Size(width, height);
|
return Size(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ class TextBackgroundPainter extends StatelessWidget {
|
||||||
TextSelection(baseOffset: 0, extentOffset: textLength),
|
TextSelection(baseOffset: 0, extentOffset: textLength),
|
||||||
boxHeightStyle: ui.BoxHeightStyle.max,
|
boxHeightStyle: ui.BoxHeightStyle.max,
|
||||||
);
|
);
|
||||||
|
paragraph.dispose();
|
||||||
|
|
||||||
// merge boxes to avoid artifacts at box edges, from anti-aliasing and rounding hacks
|
// merge boxes to avoid artifacts at box edges, from anti-aliasing and rounding hacks
|
||||||
final lineRects = groupBy<TextBox, double>(allBoxes, (v) => v.top).entries.map((kv) {
|
final lineRects = groupBy<TextBox, double>(allBoxes, (v) => v.top).entries.map((kv) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:overlay_support/overlay_support.dart';
|
import 'package:overlay_support/overlay_support.dart';
|
||||||
|
|
||||||
|
@ -10,7 +11,20 @@ class DoubleBackPopHandler {
|
||||||
bool _backOnce = false;
|
bool _backOnce = false;
|
||||||
Timer? _backTimer;
|
Timer? _backTimer;
|
||||||
|
|
||||||
|
DoubleBackPopHandler() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: 'aves',
|
||||||
|
className: '$DoubleBackPopHandler',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
|
}
|
||||||
_stopBackTimer();
|
_stopBackTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,7 +127,7 @@ class SectionHeader<T> extends StatelessWidget {
|
||||||
}) {
|
}) {
|
||||||
final textScaleFactor = MediaQuery.textScaleFactorOf(context);
|
final textScaleFactor = MediaQuery.textScaleFactorOf(context);
|
||||||
final maxContentWidth = maxWidth - (SectionHeader.padding.horizontal + SectionHeader.margin.horizontal);
|
final maxContentWidth = maxWidth - (SectionHeader.padding.horizontal + SectionHeader.margin.horizontal);
|
||||||
final para = RenderParagraph(
|
final paragraph = RenderParagraph(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
children: [
|
children: [
|
||||||
// as of Flutter v3.7.7, `RenderParagraph` fails to lay out `WidgetSpan` offscreen
|
// as of Flutter v3.7.7, `RenderParagraph` fails to lay out `WidgetSpan` offscreen
|
||||||
|
@ -148,7 +148,9 @@ class SectionHeader<T> extends StatelessWidget {
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
textScaleFactor: textScaleFactor,
|
textScaleFactor: textScaleFactor,
|
||||||
)..layout(BoxConstraints(maxWidth: maxContentWidth), parentUsesSize: true);
|
)..layout(BoxConstraints(maxWidth: maxContentWidth), parentUsesSize: true);
|
||||||
return para.getMaxIntrinsicHeight(maxContentWidth);
|
final height = paragraph.getMaxIntrinsicHeight(maxContentWidth);
|
||||||
|
paragraph.dispose();
|
||||||
|
return height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,13 +38,14 @@ class CaptionedButton extends StatefulWidget {
|
||||||
final width = getWidth(context);
|
final width = getWidth(context);
|
||||||
var height = width;
|
var height = width;
|
||||||
if (showCaption) {
|
if (showCaption) {
|
||||||
final para = RenderParagraph(
|
final paragraph = RenderParagraph(
|
||||||
TextSpan(text: text, style: CaptionedButtonText.textStyle(context)),
|
TextSpan(text: text, style: CaptionedButtonText.textStyle(context)),
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
textScaleFactor: MediaQuery.textScaleFactorOf(context),
|
textScaleFactor: MediaQuery.textScaleFactorOf(context),
|
||||||
maxLines: CaptionedButtonText.maxLines,
|
maxLines: CaptionedButtonText.maxLines,
|
||||||
)..layout(const BoxConstraints(), parentUsesSize: true);
|
)..layout(const BoxConstraints(), parentUsesSize: true);
|
||||||
height += para.getMaxIntrinsicHeight(width) + padding.vertical;
|
height += paragraph.getMaxIntrinsicHeight(width) + padding.vertical;
|
||||||
|
paragraph.dispose();
|
||||||
}
|
}
|
||||||
return Size(width, height);
|
return Size(width, height);
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,7 @@ class _GeoMapState extends State<GeoMap> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_clusterChangeNotifier.dispose();
|
||||||
_unregisterWidget(widget);
|
_unregisterWidget(widget);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/theme/icons.dart';
|
import 'package:aves/theme/icons.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/search/route.dart';
|
import 'package:aves/widgets/common/search/route.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
@ -15,11 +16,22 @@ abstract class AvesSearchDelegate extends SearchDelegate {
|
||||||
String? initialQuery,
|
String? initialQuery,
|
||||||
required super.searchFieldLabel,
|
required super.searchFieldLabel,
|
||||||
}) {
|
}) {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: 'aves',
|
||||||
|
className: '$AvesSearchDelegate',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
query = initialQuery ?? '';
|
query = initialQuery ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@mustCallSuper
|
@mustCallSuper
|
||||||
void dispose() {}
|
void dispose() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget? buildLeading(BuildContext context) {
|
Widget? buildLeading(BuildContext context) {
|
||||||
|
|
|
@ -88,6 +88,7 @@ class _ThumbnailImageState extends State<ThumbnailImage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _registerWidget(ThumbnailImage widget) {
|
void _registerWidget(ThumbnailImage widget) {
|
||||||
|
// TODO TLAD [leak] `widget.entry.visualChangeNotifier`
|
||||||
widget.entry.visualChangeNotifier.addListener(_onVisualChanged);
|
widget.entry.visualChangeNotifier.addListener(_onVisualChanged);
|
||||||
_initProvider();
|
_initProvider();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
@ -26,6 +27,13 @@ class TileExtentController {
|
||||||
required this.spacing,
|
required this.spacing,
|
||||||
required this.horizontalPadding,
|
required this.horizontalPadding,
|
||||||
}) {
|
}) {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: 'aves',
|
||||||
|
className: '$TileExtentController',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
// initialize extent to 0, so that it will be dynamically sized on first launch
|
// initialize extent to 0, so that it will be dynamically sized on first launch
|
||||||
extentNotifier = ValueNotifier(0);
|
extentNotifier = ValueNotifier(0);
|
||||||
userPreferredExtent = settings.getTileExtent(settingsRouteKey);
|
userPreferredExtent = settings.getTileExtent(settingsRouteKey);
|
||||||
|
@ -33,6 +41,9 @@ class TileExtentController {
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
|
}
|
||||||
_subscriptions
|
_subscriptions
|
||||||
..forEach((sub) => sub.cancel())
|
..forEach((sub) => sub.cancel())
|
||||||
..clear();
|
..clear();
|
||||||
|
|
|
@ -27,9 +27,11 @@ import 'package:aves/widgets/debug/report.dart';
|
||||||
import 'package:aves/widgets/debug/settings.dart';
|
import 'package:aves/widgets/debug/settings.dart';
|
||||||
import 'package:aves/widgets/debug/storage.dart';
|
import 'package:aves/widgets/debug/storage.dart';
|
||||||
import 'package:aves/widgets/viewer/info/common.dart';
|
import 'package:aves/widgets/viewer/info/common.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:leak_tracker/leak_tracker.dart';
|
||||||
|
|
||||||
class AppDebugPage extends StatefulWidget {
|
class AppDebugPage extends StatefulWidget {
|
||||||
static const routeName = '/debug';
|
static const routeName = '/debug';
|
||||||
|
@ -133,6 +135,23 @@ class _AppDebugPageState extends State<AppDebugPage> {
|
||||||
},
|
},
|
||||||
title: const Text('Show tasks overlay'),
|
title: const Text('Show tasks overlay'),
|
||||||
),
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => LeakTracking.collectLeaks().then((leaks) {
|
||||||
|
leaks.byType.forEach((type, reports) {
|
||||||
|
debugPrint('* leak type=$type');
|
||||||
|
groupBy(reports, (report) => report.type).forEach((reportType, typedReports) {
|
||||||
|
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}');
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
child: const Text('Collect leaks'),
|
||||||
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => source.init(loadTopEntriesFirst: false),
|
onPressed: () => source.init(loadTopEntriesFirst: false),
|
||||||
child: const Text('Source refresh (top off)'),
|
child: const Text('Source refresh (top off)'),
|
||||||
|
|
|
@ -437,12 +437,13 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
|
||||||
l10n.setCoverDialogCustom,
|
l10n.setCoverDialogCustom,
|
||||||
}.fold('', (previousValue, element) => '$previousValue\n$element');
|
}.fold('', (previousValue, element) => '$previousValue\n$element');
|
||||||
|
|
||||||
final para = RenderParagraph(
|
final paragraph = RenderParagraph(
|
||||||
TextSpan(text: _optionLines, style: Theme.of(context).textTheme.titleMedium!),
|
TextSpan(text: _optionLines, style: Theme.of(context).textTheme.titleMedium!),
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
textScaleFactor: MediaQuery.textScaleFactorOf(context),
|
textScaleFactor: MediaQuery.textScaleFactorOf(context),
|
||||||
)..layout(const BoxConstraints(), parentUsesSize: true);
|
)..layout(const BoxConstraints(), parentUsesSize: true);
|
||||||
final textWidth = para.getMaxIntrinsicWidth(double.infinity);
|
final textWidth = paragraph.getMaxIntrinsicWidth(double.infinity);
|
||||||
|
paragraph.dispose();
|
||||||
|
|
||||||
// from `RadioListTile` layout
|
// from `RadioListTile` layout
|
||||||
const contentPadding = 32;
|
const contentPadding = 32;
|
||||||
|
|
|
@ -33,11 +33,21 @@ class TransformController {
|
||||||
final Size displaySize;
|
final Size displaySize;
|
||||||
|
|
||||||
TransformController(this.displaySize) {
|
TransformController(this.displaySize) {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: 'aves',
|
||||||
|
className: '$TransformController',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
reset();
|
reset();
|
||||||
aspectRatioNotifier.addListener(_onAspectRatioChanged);
|
aspectRatioNotifier.addListener(_onAspectRatioChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
|
}
|
||||||
aspectRatioNotifier.dispose();
|
aspectRatioNotifier.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -255,7 +255,6 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
|
||||||
builder: (context) => MapPage(collection: mapCollection),
|
builder: (context) => MapPage(collection: mapCollection),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
mapCollection.dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _goToSlideshow(BuildContext context, Set<T> filters) {
|
void _goToSlideshow(BuildContext context, Set<T> filters) {
|
||||||
|
|
|
@ -38,20 +38,22 @@ class FilterListDetailsTheme extends StatelessWidget {
|
||||||
final captionStyle = textTheme.bodySmall!;
|
final captionStyle = textTheme.bodySmall!;
|
||||||
|
|
||||||
final titleIconSize = AvesFilterChip.iconSize * textScaleFactor;
|
final titleIconSize = AvesFilterChip.iconSize * textScaleFactor;
|
||||||
final titleLineHeight = (RenderParagraph(
|
final titleLineHeightParagraph = RenderParagraph(
|
||||||
TextSpan(text: 'Fake Title', style: titleStyle),
|
TextSpan(text: 'Fake Title', style: titleStyle),
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
textScaleFactor: textScaleFactor,
|
textScaleFactor: textScaleFactor,
|
||||||
)..layout(const BoxConstraints(), parentUsesSize: true))
|
)..layout(const BoxConstraints(), parentUsesSize: true);
|
||||||
.getMaxIntrinsicHeight(double.infinity);
|
final titleLineHeight = titleLineHeightParagraph.getMaxIntrinsicHeight(double.infinity);
|
||||||
|
titleLineHeightParagraph.dispose();
|
||||||
|
|
||||||
final captionLineHeight = (RenderParagraph(
|
final captionLineHeightParagraph = RenderParagraph(
|
||||||
TextSpan(text: formatDateTime(DateTime.now(), locale, use24hour), style: captionStyle),
|
TextSpan(text: formatDateTime(DateTime.now(), locale, use24hour), style: captionStyle),
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
textScaleFactor: textScaleFactor,
|
textScaleFactor: textScaleFactor,
|
||||||
strutStyle: AStyles.overflowStrut,
|
strutStyle: AStyles.overflowStrut,
|
||||||
)..layout(const BoxConstraints(), parentUsesSize: true))
|
)..layout(const BoxConstraints(), parentUsesSize: true);
|
||||||
.getMaxIntrinsicHeight(double.infinity);
|
final captionLineHeight = captionLineHeightParagraph.getMaxIntrinsicHeight(double.infinity);
|
||||||
|
captionLineHeightParagraph.dispose();
|
||||||
|
|
||||||
var titleMaxLines = 1;
|
var titleMaxLines = 1;
|
||||||
var showCount = false;
|
var showCount = false;
|
||||||
|
|
|
@ -165,6 +165,9 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
|
||||||
_mapController.dispose();
|
_mapController.dispose();
|
||||||
_selectedIndexNotifier.removeListener(_onThumbnailIndexChanged);
|
_selectedIndexNotifier.removeListener(_onThumbnailIndexChanged);
|
||||||
regionCollection?.dispose();
|
regionCollection?.dispose();
|
||||||
|
// provided collection should be a new instance specifically created
|
||||||
|
// for the `MapPage` widget, so it can be safely disposed here
|
||||||
|
widget.collection.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,10 +397,11 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
|
||||||
TransparentMaterialPageRoute(
|
TransparentMaterialPageRoute(
|
||||||
settings: const RouteSettings(name: EntryViewerPage.routeName),
|
settings: const RouteSettings(name: EntryViewerPage.routeName),
|
||||||
pageBuilder: (context, a, sa) {
|
pageBuilder: (context, a, sa) {
|
||||||
return EntryViewerPage(
|
final viewerCollection = regionCollection?.copyWith(
|
||||||
collection: regionCollection?.copyWith(
|
|
||||||
listenToSource: false,
|
listenToSource: false,
|
||||||
),
|
);
|
||||||
|
return EntryViewerPage(
|
||||||
|
collection: viewerCollection,
|
||||||
initialEntry: initialEntry,
|
initialEntry: initialEntry,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -104,6 +104,7 @@ class _QuickActionEditorBodyState<T extends Object> extends State<QuickActionEdi
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_quickActionsChangeNotifier.dispose();
|
||||||
_stopLeavingTimer();
|
_stopLeavingTimer();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
|
@ -271,7 +271,6 @@ class EntryInfoActionDelegate with FeedbackMixin, PermissionAwareMixin, EntryEdi
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
mapCollection.dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _goToDebug(BuildContext context, AvesEntry targetEntry) {
|
void _goToDebug(BuildContext context, AvesEntry targetEntry) {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import 'package:aves/widgets/viewer/controls/notifications.dart';
|
||||||
import 'package:aves_model/aves_model.dart';
|
import 'package:aves_model/aves_model.dart';
|
||||||
import 'package:aves_video/aves_video.dart';
|
import 'package:aves_video/aves_video.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
@ -32,9 +33,20 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
||||||
|
|
||||||
VideoActionDelegate({
|
VideoActionDelegate({
|
||||||
required this.collection,
|
required this.collection,
|
||||||
});
|
}) {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: 'aves',
|
||||||
|
className: '$VideoActionDelegate',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
|
}
|
||||||
stopOverlayHidingTimer();
|
stopOverlayHidingTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/widgets/viewer/controls/events.dart';
|
import 'package:aves/widgets/viewer/controls/events.dart';
|
||||||
import 'package:aves_magnifier/aves_magnifier.dart';
|
import 'package:aves_magnifier/aves_magnifier.dart';
|
||||||
import 'package:aves_model/aves_model.dart';
|
import 'package:aves_model/aves_model.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
class ViewerController {
|
class ViewerController {
|
||||||
|
@ -47,6 +48,13 @@ class ViewerController {
|
||||||
this.autopilotInterval,
|
this.autopilotInterval,
|
||||||
this.autopilotAnimatedZoom = false,
|
this.autopilotAnimatedZoom = false,
|
||||||
}) {
|
}) {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: 'aves',
|
||||||
|
className: '$ViewerController',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
_initialScale = initialScale;
|
_initialScale = initialScale;
|
||||||
_autopilotNotifier = ValueNotifier(autopilot);
|
_autopilotNotifier = ValueNotifier(autopilot);
|
||||||
_autopilotNotifier.addListener(_onAutopilotChanged);
|
_autopilotNotifier.addListener(_onAutopilotChanged);
|
||||||
|
@ -54,6 +62,9 @@ class ViewerController {
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
|
}
|
||||||
_autopilotNotifier.removeListener(_onAutopilotChanged);
|
_autopilotNotifier.removeListener(_onAutopilotChanged);
|
||||||
_clearAutopilotAnimations();
|
_clearAutopilotAnimations();
|
||||||
_stopPlayTimer();
|
_stopPlayTimer();
|
||||||
|
|
|
@ -34,6 +34,9 @@ class _EntryViewerPageState extends State<EntryViewerPage> {
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_viewerController.dispose();
|
_viewerController.dispose();
|
||||||
|
// provided collection should be a new instance specifically created
|
||||||
|
// for the `EntryViewerPage` widget, so it can be safely disposed here
|
||||||
|
widget.collection?.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -192,7 +192,10 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
||||||
_overlayVisible.dispose();
|
_overlayVisible.dispose();
|
||||||
_viewLocked.dispose();
|
_viewLocked.dispose();
|
||||||
_overlayExpandedNotifier.dispose();
|
_overlayExpandedNotifier.dispose();
|
||||||
|
_currentVerticalPage.dispose();
|
||||||
|
_horizontalPager.dispose();
|
||||||
_verticalPager.dispose();
|
_verticalPager.dispose();
|
||||||
|
_verticalScrollNotifier.dispose();
|
||||||
_heroInfoNotifier.dispose();
|
_heroInfoNotifier.dispose();
|
||||||
_stopOverlayHidingTimer();
|
_stopOverlayHidingTimer();
|
||||||
AvesApp.lifecycleStateNotifier.removeListener(_onAppLifecycleStateChanged);
|
AvesApp.lifecycleStateNotifier.removeListener(_onAppLifecycleStateChanged);
|
||||||
|
|
|
@ -139,12 +139,14 @@ class _InfoRowGroupState extends State<InfoRowGroup> {
|
||||||
}
|
}
|
||||||
|
|
||||||
double _getSpanWidth(TextSpan span, double textScaleFactor) {
|
double _getSpanWidth(TextSpan span, double textScaleFactor) {
|
||||||
final para = RenderParagraph(
|
final paragraph = RenderParagraph(
|
||||||
span,
|
span,
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
textScaleFactor: textScaleFactor,
|
textScaleFactor: textScaleFactor,
|
||||||
)..layout(const BoxConstraints(), parentUsesSize: true);
|
)..layout(const BoxConstraints(), parentUsesSize: true);
|
||||||
return para.getMaxIntrinsicWidth(double.infinity);
|
final width = paragraph.getMaxIntrinsicWidth(double.infinity);
|
||||||
|
paragraph.dispose();
|
||||||
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<InlineSpan> _buildTextValueSpans(BuildContext context, String key, String value) {
|
List<InlineSpan> _buildTextValueSpans(BuildContext context, String key, String value) {
|
||||||
|
|
|
@ -153,7 +153,6 @@ class _LocationSectionState extends State<LocationSection> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
mapCollection.dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onMetadataChanged() {
|
void _onMetadataChanged() {
|
||||||
|
|
|
@ -24,6 +24,13 @@ class MultiPageController {
|
||||||
set page(int? page) => pageNotifier.value = page;
|
set page(int? page) => pageNotifier.value = page;
|
||||||
|
|
||||||
MultiPageController(this.entry) {
|
MultiPageController(this.entry) {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: 'aves',
|
||||||
|
className: '$MultiPageController',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +47,9 @@ class MultiPageController {
|
||||||
});
|
});
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
|
}
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
pageNotifier.dispose();
|
pageNotifier.dispose();
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ class VideoConductor {
|
||||||
_subscriptions
|
_subscriptions
|
||||||
..forEach((sub) => sub.cancel())
|
..forEach((sub) => sub.cancel())
|
||||||
..clear();
|
..clear();
|
||||||
|
_controllers.forEach((v) => v.dispose());
|
||||||
_controllers.clear();
|
_controllers.clear();
|
||||||
if (settings.keepScreenOn == KeepScreenOn.videoPlayback) {
|
if (settings.keepScreenOn == KeepScreenOn.videoPlayback) {
|
||||||
await windowService.keepScreenOn(false);
|
await windowService.keepScreenOn(false);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:aves/model/entry/entry.dart';
|
import 'package:aves/model/entry/entry.dart';
|
||||||
import 'package:aves/model/view_state.dart';
|
import 'package:aves/model/view_state.dart';
|
||||||
import 'package:aves/widgets/viewer/view/histogram.dart';
|
import 'package:aves/widgets/viewer/view/histogram.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class ViewStateController with HistogramMixin {
|
class ViewStateController with HistogramMixin {
|
||||||
|
@ -13,9 +14,20 @@ class ViewStateController with HistogramMixin {
|
||||||
ViewStateController({
|
ViewStateController({
|
||||||
required this.entry,
|
required this.entry,
|
||||||
required this.viewStateNotifier,
|
required this.viewStateNotifier,
|
||||||
});
|
}) {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: 'aves',
|
||||||
|
className: '$ViewStateController',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
|
}
|
||||||
viewStateNotifier.dispose();
|
viewStateNotifier.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,13 +153,14 @@ class VideoSubtitles extends StatelessWidget {
|
||||||
var transform = Matrix4.identity();
|
var transform = Matrix4.identity();
|
||||||
|
|
||||||
if (position != null) {
|
if (position != null) {
|
||||||
final para = RenderParagraph(
|
final paragraph = RenderParagraph(
|
||||||
TextSpan(children: spans),
|
TextSpan(children: spans),
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
textScaleFactor: MediaQuery.textScaleFactorOf(context),
|
textScaleFactor: MediaQuery.textScaleFactorOf(context),
|
||||||
)..layout(const BoxConstraints());
|
)..layout(const BoxConstraints());
|
||||||
final textWidth = para.getMaxIntrinsicWidth(double.infinity);
|
final textWidth = paragraph.getMaxIntrinsicWidth(double.infinity);
|
||||||
final textHeight = para.getMaxIntrinsicHeight(double.infinity);
|
final textHeight = paragraph.getMaxIntrinsicHeight(double.infinity);
|
||||||
|
paragraph.dispose();
|
||||||
|
|
||||||
late double anchorOffsetX, anchorOffsetY;
|
late double anchorOffsetX, anchorOffsetY;
|
||||||
switch (textHAlign) {
|
switch (textHAlign) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:aves_magnifier/src/controller/state.dart';
|
||||||
import 'package:aves_magnifier/src/scale/scale_boundaries.dart';
|
import 'package:aves_magnifier/src/scale/scale_boundaries.dart';
|
||||||
import 'package:aves_magnifier/src/scale/scale_level.dart';
|
import 'package:aves_magnifier/src/scale/scale_level.dart';
|
||||||
import 'package:aves_magnifier/src/scale/state.dart';
|
import 'package:aves_magnifier/src/scale/state.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
class AvesMagnifierController {
|
class AvesMagnifierController {
|
||||||
|
@ -19,6 +20,13 @@ class AvesMagnifierController {
|
||||||
AvesMagnifierController({
|
AvesMagnifierController({
|
||||||
MagnifierState? initialState,
|
MagnifierState? initialState,
|
||||||
}) : super() {
|
}) : super() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: 'aves',
|
||||||
|
className: '$AvesMagnifierController',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
const source = ChangeSource.internal;
|
const source = ChangeSource.internal;
|
||||||
initial = initialState ?? const MagnifierState(position: Offset.zero, scale: null, source: source);
|
initial = initialState ?? const MagnifierState(position: Offset.zero, scale: null, source: source);
|
||||||
previousState = initial;
|
previousState = initial;
|
||||||
|
@ -31,6 +39,16 @@ class AvesMagnifierController {
|
||||||
_setScaleState(_initialScaleState);
|
_setScaleState(_initialScaleState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
|
}
|
||||||
|
_disposed = true;
|
||||||
|
_stateStreamController.close();
|
||||||
|
_scaleBoundariesStreamController.close();
|
||||||
|
_scaleStateChangeStreamController.close();
|
||||||
|
}
|
||||||
|
|
||||||
Stream<MagnifierState> get stateStream => _stateStreamController.stream;
|
Stream<MagnifierState> get stateStream => _stateStreamController.stream;
|
||||||
|
|
||||||
Stream<ScaleBoundaries> get scaleBoundariesStream => _scaleBoundariesStreamController.stream;
|
Stream<ScaleBoundaries> get scaleBoundariesStream => _scaleBoundariesStreamController.stream;
|
||||||
|
@ -51,13 +69,6 @@ class AvesMagnifierController {
|
||||||
|
|
||||||
bool get isZooming => scaleState.state == ScaleState.zoomedIn || scaleState.state == ScaleState.zoomedOut;
|
bool get isZooming => scaleState.state == ScaleState.zoomedIn || scaleState.state == ScaleState.zoomedOut;
|
||||||
|
|
||||||
void dispose() {
|
|
||||||
_disposed = true;
|
|
||||||
_stateStreamController.close();
|
|
||||||
_scaleBoundariesStreamController.close();
|
|
||||||
_scaleStateChangeStreamController.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void update({
|
void update({
|
||||||
Offset? position,
|
Offset? position,
|
||||||
double? scale,
|
double? scale,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:aves_map/src/zoomed_bounds.dart';
|
import 'package:aves_map/src/zoomed_bounds.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
|
|
||||||
class AvesMapController {
|
class AvesMapController {
|
||||||
|
@ -16,7 +17,20 @@ class AvesMapController {
|
||||||
|
|
||||||
Stream<MapMarkerLocationChangeEvent> get markerLocationChanges => _events.where((event) => event is MapMarkerLocationChangeEvent).cast<MapMarkerLocationChangeEvent>();
|
Stream<MapMarkerLocationChangeEvent> get markerLocationChanges => _events.where((event) => event is MapMarkerLocationChangeEvent).cast<MapMarkerLocationChangeEvent>();
|
||||||
|
|
||||||
|
AvesMapController() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: 'aves',
|
||||||
|
className: '$AvesMapController',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
|
}
|
||||||
_streamController.close();
|
_streamController.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,11 +29,21 @@ abstract class AvesVideoController {
|
||||||
required this.playbackStateHandler,
|
required this.playbackStateHandler,
|
||||||
required this.settings,
|
required this.settings,
|
||||||
}) : _entry = entry {
|
}) : _entry = entry {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: 'aves',
|
||||||
|
className: '$AvesVideoController',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
entry.visualChangeNotifier.addListener(onVisualChanged);
|
entry.visualChangeNotifier.addListener(onVisualChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
@mustCallSuper
|
@mustCallSuper
|
||||||
Future<void> dispose() async {
|
Future<void> dispose() async {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
|
}
|
||||||
_entry.visualChangeNotifier.removeListener(onVisualChanged);
|
_entry.visualChangeNotifier.removeListener(onVisualChanged);
|
||||||
await _savePlaybackState();
|
await _savePlaybackState();
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,7 @@ class IjkVideoController extends AvesVideoController {
|
||||||
await _valueStreamController.close();
|
await _valueStreamController.close();
|
||||||
await _timedTextStreamController.close();
|
await _timedTextStreamController.close();
|
||||||
await _instance.release();
|
await _instance.release();
|
||||||
|
_completedNotifier.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _startListening() {
|
void _startListening() {
|
||||||
|
|
|
@ -69,6 +69,7 @@ class MpvVideoController extends AvesVideoController {
|
||||||
await _statusStreamController.close();
|
await _statusStreamController.close();
|
||||||
await _timedTextStreamController.close();
|
await _timedTextStreamController.close();
|
||||||
await _instance.dispose();
|
await _instance.dispose();
|
||||||
|
_completedNotifier.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _startListening() {
|
void _startListening() {
|
||||||
|
|
|
@ -735,6 +735,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.0"
|
version: "0.9.0"
|
||||||
|
leak_tracker:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: leak_tracker
|
||||||
|
sha256: b63ca5cc296c7509d71f6d4a8cb6085eec8461970c503f3ef3c5c541bc3f0a9a
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.0.6"
|
||||||
lints:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -83,6 +83,7 @@ dependencies:
|
||||||
get_it:
|
get_it:
|
||||||
intl:
|
intl:
|
||||||
latlong2:
|
latlong2:
|
||||||
|
leak_tracker:
|
||||||
local_auth:
|
local_auth:
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
material_design_icons_flutter:
|
material_design_icons_flutter:
|
||||||
|
|
Loading…
Reference in a new issue