Enable `dispose-class-fields` and `dispose-fields` lints to resolve memory leaks (#9857)
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index b82109d..d1fd75d 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -74,7 +74,7 @@
wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg
echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list
sudo apt-get update
- sudo apt-get install dcm=1.36.0-1 # To avoid errors add `-1` (build number) to the version
+ sudo apt-get install dcm=1.38.1-1 # To avoid errors add `-1` (build number) to the version
sudo chmod +x /usr/bin/dcm
echo "$(dcm --version)"
- name: Setup Dart SDK
diff --git a/analysis_options.yaml b/analysis_options.yaml
index f219cfd..f59d8d0 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -186,6 +186,8 @@
- avoid-explicit-type-declaration
# - ban-name # TODO(polina-c): add configuration
# - binary-expression-operand-order Some nice catches but too many false positives to enable.
+ - dispose-class-fields
+ - dispose-fields
- double-literal-format
# - format-comment TODO(jacobr): enable this one after fixing violations.
# TODO(jacobr): enable member-ordering. This catches a bunch of real style
diff --git a/packages/devtools_app/lib/src/app.dart b/packages/devtools_app/lib/src/app.dart
index d266186..272f1fc 100644
--- a/packages/devtools_app/lib/src/app.dart
+++ b/packages/devtools_app/lib/src/app.dart
@@ -202,6 +202,7 @@
FrameworkCore.dispose();
// Workaround for https://github.com/flutter/flutter/issues/155265.
removeTextFieldFocusFixHandler();
+ routerDelegate.dispose();
super.dispose();
}
diff --git a/packages/devtools_app/lib/src/extensions/embedded/_view_web.dart b/packages/devtools_app/lib/src/extensions/embedded/_view_web.dart
index 3135be3..7901753 100644
--- a/packages/devtools_app/lib/src/extensions/embedded/_view_web.dart
+++ b/packages/devtools_app/lib/src/extensions/embedded/_view_web.dart
@@ -46,6 +46,7 @@
@override
void dispose() {
+ _embeddedExtensionController.dispose();
iFrameController.dispose();
super.dispose();
}
diff --git a/packages/devtools_app/lib/src/framework/notifications_view.dart b/packages/devtools_app/lib/src/framework/notifications_view.dart
index 83c5b69..e3c2331 100644
--- a/packages/devtools_app/lib/src/framework/notifications_view.dart
+++ b/packages/devtools_app/lib/src/framework/notifications_view.dart
@@ -86,7 +86,10 @@
@override
void dispose() {
- _overlayEntry!.remove();
+ _overlayEntry
+ ?..remove()
+ ..dispose();
+ _overlayEntry = null;
super.dispose();
}
@@ -218,6 +221,7 @@
void dispose() {
controller.dispose();
_dismissTimer?.cancel();
+ curve.dispose();
super.dispose();
}
diff --git a/packages/devtools_app/lib/src/screens/debugger/debugger_controller.dart b/packages/devtools_app/lib/src/screens/debugger/debugger_controller.dart
index aacc0ec..485fed0 100644
--- a/packages/devtools_app/lib/src/screens/debugger/debugger_controller.dart
+++ b/packages/devtools_app/lib/src/screens/debugger/debugger_controller.dart
@@ -89,6 +89,8 @@
_selectedBreakpoint.dispose();
_exceptionPauseMode.dispose();
_hasTruncatedFrames.dispose();
+ unawaited(_getStackOperation?.cancel());
+ _lastService = null;
super.dispose();
}
diff --git a/packages/devtools_app/lib/src/screens/inspector/inspector_controller.dart b/packages/devtools_app/lib/src/screens/inspector/inspector_controller.dart
index 1f1ed0c..9484ecb 100644
--- a/packages/devtools_app/lib/src/screens/inspector/inspector_controller.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/inspector_controller.dart
@@ -200,7 +200,8 @@
/// for now mainly to minimize risk.
static const refreshFramesPerSecond = 5.0;
- InspectorTreeController inspectorTree;
+ final InspectorTreeController inspectorTree;
+
final FlutterTreeType treeType;
late RateLimiter _refreshRateLimiter;
diff --git a/packages/devtools_app/lib/src/screens/inspector/inspector_tree_controller.dart b/packages/devtools_app/lib/src/screens/inspector/inspector_tree_controller.dart
index 67c39ba..130bec6 100644
--- a/packages/devtools_app/lib/src/screens/inspector/inspector_tree_controller.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/inspector_tree_controller.dart
@@ -914,6 +914,8 @@
Rect? _currentAnimateTarget;
AnimationController? _constraintDisplayController;
+
+ // ignore: dispose-fields, false positive. Disposed via autoDisposeFocusNode.
late FocusNode _focusNode;
/// When autoscrolling, the number of rows to pad the target location with.
diff --git a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/flex/flex.dart b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/flex/flex.dart
index 3c3cb55..dd16fde 100644
--- a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/flex/flex.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/flex/flex.dart
@@ -394,6 +394,12 @@
),
);
}
+
+ @override
+ void dispose() {
+ scrollController.dispose();
+ super.dispose();
+ }
}
class VisualizeFlexChildren extends StatefulWidget {
diff --git a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/layout_explorer_widget.dart b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/layout_explorer_widget.dart
index 1a9840f..9c8bf18 100644
--- a/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/layout_explorer_widget.dart
+++ b/packages/devtools_app/lib/src/screens/inspector/layout_explorer/ui/layout_explorer_widget.dart
@@ -162,6 +162,9 @@
entranceController.dispose();
changeController.dispose();
_unregisterInspectorControllerService();
+ entranceCurve.dispose();
+ changeAnimation.dispose();
+ rateLimiter.dispose();
super.dispose();
}
diff --git a/packages/devtools_app/lib/src/screens/logging/_log_details.dart b/packages/devtools_app/lib/src/screens/logging/_log_details.dart
index f277c54..19cba66 100644
--- a/packages/devtools_app/lib/src/screens/logging/_log_details.dart
+++ b/packages/devtools_app/lib/src/screens/logging/_log_details.dart
@@ -44,6 +44,12 @@
}
@override
+ void dispose() {
+ scrollController.dispose();
+ super.dispose();
+ }
+
+ @override
void didUpdateWidget(LogDetails oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.log != oldWidget.log) {
diff --git a/packages/devtools_app/lib/src/screens/memory/shared/widgets/class_filter.dart b/packages/devtools_app/lib/src/screens/memory/shared/widgets/class_filter.dart
index afae49d..3374785 100644
--- a/packages/devtools_app/lib/src/screens/memory/shared/widgets/class_filter.dart
+++ b/packages/devtools_app/lib/src/screens/memory/shared/widgets/class_filter.dart
@@ -83,6 +83,13 @@
}
@override
+ void dispose() {
+ _except.dispose();
+ _only.dispose();
+ super.dispose();
+ }
+
+ @override
void didUpdateWidget(covariant ClassFilterDialog oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.classFilter != widget.classFilter) {
diff --git a/packages/devtools_app/lib/src/screens/network/network_controller.dart b/packages/devtools_app/lib/src/screens/network/network_controller.dart
index deecab6..dfb9a90 100644
--- a/packages/devtools_app/lib/src/screens/network/network_controller.dart
+++ b/packages/devtools_app/lib/src/screens/network/network_controller.dart
@@ -203,6 +203,10 @@
selectedRequest.dispose();
_recordingNotifier.dispose();
_currentNetworkRequests.dispose();
+ for (final r in _httpRequests ?? <DartIOHttpRequestData>[]) {
+ r.dispose();
+ }
+ _httpRequests?.clear();
super.dispose();
}
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_controller_web.dart b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_controller_web.dart
index ed9bc3a..c032ba1 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_controller_web.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_controller_web.dart
@@ -191,6 +191,7 @@
await perfettoPostEventStream.close();
processor.dispose();
_activeScrollToTimeRange.dispose();
+ activeTrace.dispose();
super.dispose();
}
diff --git a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_web.dart b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_web.dart
index 2d5cafa..2284217 100644
--- a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_web.dart
+++ b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/perfetto/_perfetto_web.dart
@@ -34,14 +34,14 @@
}
class _PerfettoState extends State<Perfetto> with AutoDisposeMixin {
- late final PerfettoControllerImpl _perfettoController;
+ PerfettoControllerImpl get _perfettoController =>
+ widget.perfettoController as PerfettoControllerImpl;
late final _PerfettoViewController _viewController;
@override
void initState() {
super.initState();
- _perfettoController = widget.perfettoController as PerfettoControllerImpl;
_viewController = _PerfettoViewController(_perfettoController)..init();
// If [_perfettoController.activeTrace.trace] has a null value, the trace
diff --git a/packages/devtools_app/lib/src/screens/performance/performance_controller.dart b/packages/devtools_app/lib/src/screens/performance/performance_controller.dart
index 591e86a..a41441f 100644
--- a/packages/devtools_app/lib/src/screens/performance/performance_controller.dart
+++ b/packages/devtools_app/lib/src/screens/performance/performance_controller.dart
@@ -48,12 +48,16 @@
@override
final screenId = ScreenMetaData.performance.id;
+ // ignore: dispose-class-fields, false positive. See `applyToFeatureControllers` in the dispose() method.
late final FlutterFramesController flutterFramesController;
+ // ignore: dispose-class-fields, false positive. See `applyToFeatureControllers` in the dispose() method.
late final TimelineEventsController timelineEventsController;
+ // ignore: dispose-class-fields, false positive. See `applyToFeatureControllers` in the dispose() method.
late final RebuildStatsController rebuildStatsController;
+ // ignore: dispose-class-fields, false positive. See `applyToFeatureControllers` in the dispose() method.
late List<PerformanceFeatureController> _featureControllers;
// TODO(jacobr): add the recount controller to [_featureControllers] once your
diff --git a/packages/devtools_app/lib/src/screens/performance/tabbed_performance_view.dart b/packages/devtools_app/lib/src/screens/performance/tabbed_performance_view.dart
index 52096fe..a6fd988 100644
--- a/packages/devtools_app/lib/src/screens/performance/tabbed_performance_view.dart
+++ b/packages/devtools_app/lib/src/screens/performance/tabbed_performance_view.dart
@@ -13,7 +13,6 @@
import '../../shared/ui/common_widgets.dart';
import '../../shared/ui/tab.dart';
import 'panes/flutter_frames/flutter_frame_model.dart';
-import 'panes/flutter_frames/flutter_frames_controller.dart';
import 'panes/frame_analysis/frame_analysis.dart';
import 'panes/rebuild_stats/rebuild_stats.dart';
import 'panes/timeline_events/timeline_events_view.dart';
@@ -32,20 +31,18 @@
late PerformanceController controller;
- late FlutterFramesController _flutterFramesController;
-
FlutterFrame? _selectedFlutterFrame;
@override
void initState() {
super.initState();
controller = screenControllers.lookup<PerformanceController>();
- _flutterFramesController = controller.flutterFramesController;
+ final flutterFramesController = controller.flutterFramesController;
- _selectedFlutterFrame = _flutterFramesController.selectedFrame.value;
- addAutoDisposeListener(_flutterFramesController.selectedFrame, () {
+ _selectedFlutterFrame = flutterFramesController.selectedFrame.value;
+ addAutoDisposeListener(flutterFramesController.selectedFrame, () {
setState(() {
- _selectedFlutterFrame = _flutterFramesController.selectedFrame.value;
+ _selectedFlutterFrame = flutterFramesController.selectedFrame.value;
});
});
}
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/isolate_statistics/isolate_statistics_view.dart b/packages/devtools_app/lib/src/screens/vm_developer/isolate_statistics/isolate_statistics_view.dart
index 77bd8d4..5c25fed 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/isolate_statistics/isolate_statistics_view.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/isolate_statistics/isolate_statistics_view.dart
@@ -341,6 +341,12 @@
final selectedPort = ValueNotifier<InstanceRef?>(null);
@override
+ void dispose() {
+ selectedPort.dispose();
+ super.dispose();
+ }
+
+ @override
Widget build(BuildContext context) {
final ports = widget.controller.ports;
return OutlineDecoration(
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_inspector_view.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_inspector_view.dart
index 76249c3..277e7dd 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_inspector_view.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_inspector_view.dart
@@ -78,6 +78,7 @@
class _ObjectInspectorSelectorState extends State<ObjectInspectorSelector> {
String value = ObjectInspectorSelector.kProgramExplorer;
+
late ObjectInspectorViewController controller;
@override
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_store.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_store.dart
index fe42a31..275e82d 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_store.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_store.dart
@@ -53,7 +53,7 @@
return VmServiceObjectLink(
// TODO(srawlins): What type is `data` at runtime? If cast to `int`, no
// tests fail, but that can't be right...
- // ignore: avoid-dynamic
+ // ignore: avoid-dynamic, avoid-unnecessary-type-casts
object: (data as dynamic).value,
onTap: onTap,
);
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/process_memory/process_memory_view.dart b/packages/devtools_app/lib/src/screens/vm_developer/process_memory/process_memory_view.dart
index 9ae5d1d..2fb5ad7 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/process_memory/process_memory_view.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/process_memory/process_memory_view.dart
@@ -88,6 +88,16 @@
}
@override
+ void dispose() {
+ if (_tabControllerInitialized) {
+ _tabController.removeListener(_onTabChanged);
+ _tabController.dispose();
+ }
+ controller.dispose();
+ super.dispose();
+ }
+
+ @override
void didUpdateWidget(VMProcessMemoryViewBody oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.tabs.length != oldWidget.tabs.length) {
diff --git a/packages/devtools_app/lib/src/service/connected_app/connected_app.dart b/packages/devtools_app/lib/src/service/connected_app/connected_app.dart
index 04c25f5..f6e6419 100644
--- a/packages/devtools_app/lib/src/service/connected_app/connected_app.dart
+++ b/packages/devtools_app/lib/src/service/connected_app/connected_app.dart
@@ -87,6 +87,7 @@
void dispose() {
_variables.dispose();
_currentFrame.dispose();
+ _dapVariables.dispose();
super.dispose();
}
}
diff --git a/packages/devtools_app/lib/src/service/service_extension_widgets.dart b/packages/devtools_app/lib/src/service/service_extension_widgets.dart
index 7fdd4b4..10fab33 100644
--- a/packages/devtools_app/lib/src/service/service_extension_widgets.dart
+++ b/packages/devtools_app/lib/src/service/service_extension_widgets.dart
@@ -535,6 +535,13 @@
}
@override
+ void dispose() {
+ value.dispose();
+ extensionAvailable.dispose();
+ super.dispose();
+ }
+
+ @override
void _onMainIsolateChanged() => _initExtensionState();
void _initExtensionState() {
@@ -705,6 +712,12 @@
}
@override
+ void dispose() {
+ _enabled.dispose();
+ super.dispose();
+ }
+
+ @override
void _onMainIsolateChanged() => _initExtensionState();
void _initExtensionState() {
diff --git a/packages/devtools_app/lib/src/shared/charts/flame_chart.dart b/packages/devtools_app/lib/src/shared/charts/flame_chart.dart
index 37200e0..bff073a 100644
--- a/packages/devtools_app/lib/src/shared/charts/flame_chart.dart
+++ b/packages/devtools_app/lib/src/shared/charts/flame_chart.dart
@@ -114,6 +114,7 @@
final sections = <FlameChartSection>[];
+ // ignore: dispose-fields, false positive. Disposed via autoDisposeFocusNode.
final focusNode = FocusNode(debugLabel: 'flame-chart');
double? mouseHoverX;
@@ -277,6 +278,7 @@
void dispose() {
zoomController.dispose();
_verticalFlameChartScrollController.dispose();
+ _hoveredNodeNotifier.dispose();
super.dispose();
}
diff --git a/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/drag_and_drop.dart b/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/drag_and_drop.dart
index 2b26dd6..233de47 100644
--- a/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/drag_and_drop.dart
+++ b/packages/devtools_app/lib/src/shared/config_specific/drag_and_drop/drag_and_drop.dart
@@ -38,6 +38,7 @@
@mustCallSuper
void dispose() {
_dragAndDropStates.clear();
+ activeState = null;
}
void registerDragAndDrop(DragAndDropState state) {
@@ -113,6 +114,7 @@
class DragAndDropState extends State<DragAndDrop> {
final _dragging = ValueNotifier<bool>(false);
+
DragAndDropManager? _dragAndDropManager;
bool _isActive = false;
@@ -140,6 +142,8 @@
@override
void dispose() {
_dragAndDropManager?.unregisterDragAndDrop(this);
+ _dragAndDropManager = null;
+ _dragging.dispose();
super.dispose();
}
diff --git a/packages/devtools_app/lib/src/shared/console/console.dart b/packages/devtools_app/lib/src/shared/console/console.dart
index 91ead2a..30e9913 100644
--- a/packages/devtools_app/lib/src/shared/console/console.dart
+++ b/packages/devtools_app/lib/src/shared/console/console.dart
@@ -90,6 +90,12 @@
_initHelper();
}
+ @override
+ void dispose() {
+ _scroll.dispose();
+ super.dispose();
+ }
+
void _onScrollChanged() {
// Detect if the user has scrolled up and stop scrolling to the bottom if
// they have scrolled up.
diff --git a/packages/devtools_app/lib/src/shared/diagnostics/inspector_service.dart b/packages/devtools_app/lib/src/shared/diagnostics/inspector_service.dart
index 805e83f..2d4e8a7 100644
--- a/packages/devtools_app/lib/src/shared/diagnostics/inspector_service.dart
+++ b/packages/devtools_app/lib/src/shared/diagnostics/inspector_service.dart
@@ -245,6 +245,7 @@
void dispose() {
_cachedSelectionGroups?.clear(false);
_cachedSelectionGroups = null;
+ _rootDirectories.dispose();
super.dispose();
}
diff --git a/packages/devtools_app/lib/src/shared/editor/editor_client.dart b/packages/devtools_app/lib/src/shared/editor/editor_client.dart
index ef03c44..951790d 100644
--- a/packages/devtools_app/lib/src/shared/editor/editor_client.dart
+++ b/packages/devtools_app/lib/src/shared/editor/editor_client.dart
@@ -120,6 +120,15 @@
}
}
+ @override
+ void dispose() {
+ _editableArgumentsApiIsRegistered.dispose();
+ unawaited(_activeLocationChangedController.close());
+ unawaited(_eventController.close());
+ unawaited(_editorServiceChangedController.close());
+ super.dispose();
+ }
+
void _handleServiceRegistration({
required String service,
required String method,
diff --git a/packages/devtools_app/lib/src/shared/preferences/preferences.dart b/packages/devtools_app/lib/src/shared/preferences/preferences.dart
index a15d6d4..403c4e4 100644
--- a/packages/devtools_app/lib/src/shared/preferences/preferences.dart
+++ b/packages/devtools_app/lib/src/shared/preferences/preferences.dart
@@ -282,13 +282,17 @@
@override
void dispose() {
- cpuProfiler.dispose();
- devToolsExtensions.dispose();
- inspector.dispose();
- logging.dispose();
- memory.dispose();
- network.dispose();
- performance.dispose();
+ _cpuProfiler.dispose();
+ _extensions.dispose();
+ _inspector.dispose();
+ _logging.dispose();
+ _memory.dispose();
+ _network.dispose();
+ _performance.dispose();
+ darkModeEnabled.dispose();
+ advancedDeveloperModeEnabled.dispose();
+ wasmEnabled.dispose();
+ verboseLoggingEnabled.dispose();
super.dispose();
}
diff --git a/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart b/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart
index f6cc1b5..3794f1c 100644
--- a/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart
+++ b/packages/devtools_app/lib/src/shared/primitives/custom_pointer_scroll_view.dart
@@ -387,7 +387,11 @@
@override
void dispose() {
widget.controller?.detach(position!);
- position!.dispose();
+ _position!.dispose();
+ _hold?.cancel();
+ _hold = null;
+ _drag?.cancel();
+ _drag = null;
super.dispose();
}
diff --git a/packages/devtools_app/lib/src/shared/table/_tree_table.dart b/packages/devtools_app/lib/src/shared/table/_tree_table.dart
index 1880f50..22db562 100644
--- a/packages/devtools_app/lib/src/shared/table/_tree_table.dart
+++ b/packages/devtools_app/lib/src/shared/table/_tree_table.dart
@@ -122,6 +122,8 @@
class TreeTableState<T extends TreeNode<T>> extends State<TreeTable<T>>
with TickerProviderStateMixin, AutoDisposeMixin {
FocusNode? get focusNode => _focusNode;
+
+ // ignore: dispose-fields, false positive. Disposed via autoDisposeFocusNode.
late FocusNode _focusNode;
TreeTableController<T> get tableController => _tableController!;
@@ -165,6 +167,7 @@
@override
void dispose() {
+ _tableController?.dispose();
_tableController = null;
super.dispose();
}
diff --git a/packages/devtools_app/lib/src/shared/table/table.dart b/packages/devtools_app/lib/src/shared/table/table.dart
index 9e01cf2..19da2a1 100644
--- a/packages/devtools_app/lib/src/shared/table/table.dart
+++ b/packages/devtools_app/lib/src/shared/table/table.dart
@@ -115,9 +115,12 @@
with AutoDisposeMixin {
static const _resizingDebounceDuration = Duration(milliseconds: 200);
- late ScrollController scrollController;
+ @visibleForTesting
+ late final ScrollController verticalScrollController;
+
+ late final ScrollController _horizontalScrollbarController;
+
late ScrollController pinnedScrollController;
- late ScrollController _horizontalScrollbarController;
late List<T> _data;
@@ -142,13 +145,13 @@
? widget.tableController.tableUiState.scrollOffset
: 0.0;
widget.tableController.initScrollController(initialScrollOffset);
- scrollController = widget.tableController.verticalScrollController!;
+ verticalScrollController = widget.tableController.verticalScrollController!;
_horizontalScrollbarController =
widget.tableController.horizontalScrollController!;
if (widget.startScrolledAtBottom) {
WidgetsBinding.instance.addPostFrameCallback((_) {
- unawaited(scrollController.autoScrollToBottom(jump: true));
+ unawaited(verticalScrollController.autoScrollToBottom(jump: true));
});
}
@@ -208,7 +211,7 @@
final newPos = selectedDisplayRow * defaultRowHeight;
- maybeScrollToPosition(scrollController, newPos);
+ maybeScrollToPosition(verticalScrollController, newPos);
}
});
});
@@ -232,13 +235,15 @@
final index = _data.indexOf(activeSearch);
if (index == -1) return;
-
+ final verticalScrollController = this.verticalScrollController;
final y = index * defaultRowHeight;
final indexInView =
- y > scrollController.offset &&
- y < scrollController.offset + scrollController.position.extentInside;
+ y > verticalScrollController.offset &&
+ y <
+ verticalScrollController.offset +
+ verticalScrollController.position.extentInside;
if (!indexInView) {
- await scrollController.animateTo(
+ await verticalScrollController.animateTo(
index * defaultRowHeight,
duration: defaultDuration,
curve: defaultCurve,
@@ -257,6 +262,7 @@
@override
void dispose() {
pinnedScrollController.dispose();
+ _resizingDebouncer.dispose();
super.dispose();
}
@@ -388,10 +394,13 @@
@override
Widget build(BuildContext context) {
+ final verticalScrollController = this.verticalScrollController;
+
// If we're at the end already, scroll to expose the new content.
if (widget.autoScrollContent) {
- if (scrollController.hasClients && scrollController.atScrollBottom) {
- unawaited(scrollController.autoScrollToBottom());
+ if (verticalScrollController.hasClients &&
+ verticalScrollController.atScrollBottom) {
+ unawaited(verticalScrollController.autoScrollToBottom());
}
}
@@ -405,8 +414,9 @@
final tableUiState = widget.tableController.tableUiState;
final sortColumn =
widget.tableController.columns[tableUiState.sortColumnIndex];
- if (widget.preserveVerticalScrollPosition && scrollController.hasClients) {
- scrollController.jumpTo(tableUiState.scrollOffset);
+ if (widget.preserveVerticalScrollPosition &&
+ verticalScrollController.hasClients) {
+ verticalScrollController.jumpTo(tableUiState.scrollOffset);
}
return LayoutBuilder(
@@ -478,7 +488,7 @@
Expanded(
child: Scrollbar(
thumbVisibility: true,
- controller: scrollController,
+ controller: verticalScrollController,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTapDown: (a) => widget.focusNode?.requestFocus(),
@@ -488,13 +498,13 @@
widget.handleKeyEvent != null
? widget.handleKeyEvent!(
event,
- scrollController,
+ verticalScrollController,
constraints,
)
: KeyEventResult.ignored,
focusNode: widget.focusNode,
child: ListView.builder(
- controller: scrollController,
+ controller: verticalScrollController,
itemCount: _dataRowCount(
constraints,
showColumnGroupHeader,
diff --git a/packages/devtools_app/lib/src/shared/ui/editable_list.dart b/packages/devtools_app/lib/src/shared/ui/editable_list.dart
index b72f586..392950b 100644
--- a/packages/devtools_app/lib/src/shared/ui/editable_list.dart
+++ b/packages/devtools_app/lib/src/shared/ui/editable_list.dart
@@ -80,6 +80,7 @@
@override
void dispose() {
textFieldController.dispose();
+ textFieldFocusNode.dispose();
super.dispose();
}
diff --git a/packages/devtools_app/lib/src/shared/ui/vm_flag_widgets.dart b/packages/devtools_app/lib/src/shared/ui/vm_flag_widgets.dart
index 187ebff..8ddbce4 100644
--- a/packages/devtools_app/lib/src/shared/ui/vm_flag_widgets.dart
+++ b/packages/devtools_app/lib/src/shared/ui/vm_flag_widgets.dart
@@ -175,6 +175,12 @@
});
}
+ @override
+ void dispose() {
+ filterController.dispose();
+ super.dispose();
+ }
+
void _updateFromController() {
flags = (serviceConnection.vmFlagManager.flags.value?.flags ?? [])
.map((flag) => _DialogFlag(flag))
diff --git a/packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_controller.dart b/packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_controller.dart
index bad9c16..c8bf0e1 100644
--- a/packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_controller.dart
+++ b/packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_controller.dart
@@ -123,6 +123,7 @@
@override
void dispose() {
_requestDebouncer.dispose();
+ _editableWidgetData.dispose();
super.dispose();
}
diff --git a/packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_inputs.dart b/packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_inputs.dart
index 037d460..9d01365 100644
--- a/packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_inputs.dart
+++ b/packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_inputs.dart
@@ -206,6 +206,7 @@
with _PropertyInputMixin<_TextInput<T>, T>, AutoDisposeMixin {
static const _paddingDiffComparedToDropdown = 1.0;
+ // ignore: dispose-fields, false positive. Disposed via autoDisposeFocusNode.
late final FocusNode _focusNode;
late final TextEditingController _controller;
@@ -224,6 +225,13 @@
// Edit property when clicking or tabbing away from input.
await _editProperty();
});
+ autoDisposeFocusNode(_focusNode);
+ }
+
+ @override
+ void dispose() {
+ _controller.dispose();
+ super.dispose();
}
@override
diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
index 37c1e05..a0519fa 100644
--- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
+++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
@@ -15,6 +15,7 @@
## General updates
+* Resolve several memory leaks. - [#9857](https://github.com/flutter/devtools/pull/9857)
* Fixed a bug where highlighted search matches in tables were unreadable in dark
mode because the highlight color had become fully opaque. -
[#9863](https://github.com/flutter/devtools/pull/9863)
diff --git a/packages/devtools_app/test/shared/managers/error_badge_manager_test.dart b/packages/devtools_app/test/shared/managers/error_badge_manager_test.dart
index 06388d2..e3c12b6 100644
--- a/packages/devtools_app/test/shared/managers/error_badge_manager_test.dart
+++ b/packages/devtools_app/test/shared/managers/error_badge_manager_test.dart
@@ -30,7 +30,7 @@
late ErrorBadgeManager errorBadgeManager;
group('ErrorBadgeManager', () {
- int getActiveErrorCount(screenId) =>
+ int getActiveErrorCount(String screenId) =>
errorBadgeManager.erroredItemsForPage(screenId).value.entries.length;
setUp(() {
diff --git a/packages/devtools_app/test/shared/primitives/linked_scroll_controller_test.dart b/packages/devtools_app/test/shared/primitives/linked_scroll_controller_test.dart
index 13f0046..2ec8be6 100644
--- a/packages/devtools_app/test/shared/primitives/linked_scroll_controller_test.dart
+++ b/packages/devtools_app/test/shared/primitives/linked_scroll_controller_test.dart
@@ -346,6 +346,13 @@
}
@override
+ void dispose() {
+ _letters.dispose();
+ _numbers.dispose();
+ super.dispose();
+ }
+
+ @override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.ltr,
diff --git a/packages/devtools_app/test/shared/table/table_test.dart b/packages/devtools_app/test/shared/table/table_test.dart
index dd2dcd9..f977bb6 100644
--- a/packages/devtools_app/test/shared/table/table_test.dart
+++ b/packages/devtools_app/test/shared/table/table_test.dart
@@ -1193,7 +1193,7 @@
final tableState = tester.state<DevToolsTableState>(
find.byType(DevToolsTable<TestData>),
);
- final scrollController = tableState.scrollController;
+ final scrollController = tableState.verticalScrollController;
expect(scrollController.offset, 0.0);
expect(scrollController.position.maxScrollExtent, greaterThan(0.0));
@@ -1221,7 +1221,7 @@
final tableState = tester.state<DevToolsTableState>(
find.byType(DevToolsTable<TestData>),
);
- final scrollController = tableState.scrollController;
+ final scrollController = tableState.verticalScrollController;
expect(scrollController.offset, isNot(0.0));
expect(scrollController.position.maxScrollExtent, greaterThan(0.0));
diff --git a/packages/devtools_app/test/test_infra/fixtures/flutter_error_app/lib/missing_material_error.dart b/packages/devtools_app/test/test_infra/fixtures/flutter_error_app/lib/missing_material_error.dart
index 1c48982..04110b1 100644
--- a/packages/devtools_app/test/test_infra/fixtures/flutter_error_app/lib/missing_material_error.dart
+++ b/packages/devtools_app/test/test_infra/fixtures/flutter_error_app/lib/missing_material_error.dart
@@ -34,6 +34,12 @@
final _controller = TextEditingController();
@override
+ void dispose() {
+ _controller.dispose();
+ super.dispose();
+ }
+
+ @override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
diff --git a/packages/devtools_app/test/test_infra/scenes/standalone_ui/editor_service/simulated_editor.dart b/packages/devtools_app/test/test_infra/scenes/standalone_ui/editor_service/simulated_editor.dart
index c17709e..a762658 100644
--- a/packages/devtools_app/test/test_infra/scenes/standalone_ui/editor_service/simulated_editor.dart
+++ b/packages/devtools_app/test/test_infra/scenes/standalone_ui/editor_service/simulated_editor.dart
@@ -35,6 +35,11 @@
return editor;
}
+ void dispose() {
+ unawaited(_logger.close());
+ unawaited(close());
+ }
+
/// The URI of the DTD instance we are connecting/connected to.
final Uri _dtdUri;
diff --git a/packages/devtools_app/test/test_infra/scenes/standalone_ui/mock_editor_widget.dart b/packages/devtools_app/test/test_infra/scenes/standalone_ui/mock_editor_widget.dart
index 08329ba..d13c090 100644
--- a/packages/devtools_app/test/test_infra/scenes/standalone_ui/mock_editor_widget.dart
+++ b/packages/devtools_app/test/test_infra/scenes/standalone_ui/mock_editor_widget.dart
@@ -88,6 +88,13 @@
}
@override
+ void dispose() {
+ clientLogRing.dispose();
+ editorLogRing.dispose();
+ super.dispose();
+ }
+
+ @override
Widget build(BuildContext context) {
final editorTheme = VsCodeTheme.of(context);
final theme = Theme.of(context);
diff --git a/packages/devtools_app/test/test_infra/scenes/standalone_ui/property_editor_sidebar.dart b/packages/devtools_app/test/test_infra/scenes/standalone_ui/property_editor_sidebar.dart
index 889c170..7f67925 100644
--- a/packages/devtools_app/test/test_infra/scenes/standalone_ui/property_editor_sidebar.dart
+++ b/packages/devtools_app/test/test_infra/scenes/standalone_ui/property_editor_sidebar.dart
@@ -67,8 +67,9 @@
SimulatedEditor? editor;
@override
- void initState() {
- super.initState();
+ void dispose() {
+ unawaited(editor?.close());
+ super.dispose();
}
@override
diff --git a/packages/devtools_app_shared/lib/src/service/dtd_manager.dart b/packages/devtools_app_shared/lib/src/service/dtd_manager.dart
index 0035f7a..83f9970 100644
--- a/packages/devtools_app_shared/lib/src/service/dtd_manager.dart
+++ b/packages/devtools_app_shared/lib/src/service/dtd_manager.dart
@@ -276,6 +276,9 @@
await disconnect();
await _currentServiceRegistrationSubscription?.cancel();
await _serviceRegistrationController.close();
+ _periodicConnectionCheck?.cancel();
+ _periodicConnectionCheck = null;
+ _connectionState.dispose();
_connection.dispose();
}
diff --git a/packages/devtools_app_shared/lib/src/service/isolate_state.dart b/packages/devtools_app_shared/lib/src/service/isolate_state.dart
index ae8ec45..3376476 100644
--- a/packages/devtools_app_shared/lib/src/service/isolate_state.dart
+++ b/packages/devtools_app_shared/lib/src/service/isolate_state.dart
@@ -48,6 +48,7 @@
null,
() => _isolateLoadCompleter = Completer()..complete(null),
);
+ _isPaused.dispose();
}
void handleDebugEvent(String? kind) {
diff --git a/packages/devtools_extensions/example/packages_with_extensions/foo/packages/foo_devtools_extension/lib/src/feature_examples/dtd_example.dart b/packages/devtools_extensions/example/packages_with_extensions/foo/packages/foo_devtools_extension/lib/src/feature_examples/dtd_example.dart
index 8ab0725..1149022 100644
--- a/packages/devtools_extensions/example/packages_with_extensions/foo/packages/foo_devtools_extension/lib/src/feature_examples/dtd_example.dart
+++ b/packages/devtools_extensions/example/packages_with_extensions/foo/packages/foo_devtools_extension/lib/src/feature_examples/dtd_example.dart
@@ -169,6 +169,12 @@
textEditingController = TextEditingController();
}
+ @override
+ void dispose() {
+ textEditingController.dispose();
+ super.dispose();
+ }
+
Future<void> _writeFilesAndUpdate() async {
await dtdManager.writeFile(tmpFileUri, textEditingController.text);
await _readTmpFile();
diff --git a/packages/devtools_extensions/example/packages_with_extensions/foo/packages/foo_devtools_extension/lib/src/feature_examples/service_extension_example.dart b/packages/devtools_extensions/example/packages_with_extensions/foo/packages/foo_devtools_extension/lib/src/feature_examples/service_extension_example.dart
index 1ee3062..f82d677 100644
--- a/packages/devtools_extensions/example/packages_with_extensions/foo/packages/foo_devtools_extension/lib/src/feature_examples/service_extension_example.dart
+++ b/packages/devtools_extensions/example/packages_with_extensions/foo/packages/foo_devtools_extension/lib/src/feature_examples/service_extension_example.dart
@@ -109,6 +109,12 @@
}
@override
+ void dispose() {
+ things.dispose();
+ super.dispose();
+ }
+
+ @override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
diff --git a/packages/devtools_extensions/lib/src/template/_simulated_devtools_environment/_simulated_devtools_controller.dart b/packages/devtools_extensions/lib/src/template/_simulated_devtools_environment/_simulated_devtools_controller.dart
index 2853cbb..024550e 100644
--- a/packages/devtools_extensions/lib/src/template/_simulated_devtools_environment/_simulated_devtools_controller.dart
+++ b/packages/devtools_extensions/lib/src/template/_simulated_devtools_environment/_simulated_devtools_controller.dart
@@ -48,6 +48,7 @@
void dispose() {
window.removeEventListener('message', _handleMessageListener);
_handleMessageListener = null;
+ messageLogs.dispose();
super.dispose();
}
diff --git a/pubspec.lock b/pubspec.lock
index 090a021..cc8b58e 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -173,10 +173,10 @@
dependency: transitive
description:
name: coverage
- sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d"
+ sha256: "956a3de0725ca232ad353565a8290d3357592bf4250f6f298a185e2d949c5d3d"
url: "https://pub.dev"
source: hosted
- version: "1.15.0"
+ version: "1.15.1"
cross_file:
dependency: transitive
description:
@@ -286,10 +286,10 @@
dependency: transitive
description:
name: file_selector_android
- sha256: ff9f9a900e0b0cf546f05abe86c91a280d611005b977c2c46a446436d7a776e3
+ sha256: "6a26687fa65cbc28a5345c7ae6f227e89f0b47740978a4c475b1a625da7a331b"
url: "https://pub.dev"
source: hosted
- version: "0.5.2+7"
+ version: "0.5.2+8"
file_selector_ios:
dependency: transitive
description:
@@ -555,10 +555,10 @@
dependency: transitive
description:
name: meta
- sha256: df0c643f44ad098eb37988027a8e2b2b5a031fd3977f06bbfd3a76637e8df739
+ sha256: c82594181e3312f3d0695fc95aaaf7758d75b8d4ae2bbecf223b9fd5109a059d
url: "https://pub.dev"
source: hosted
- version: "1.18.2"
+ version: "1.18.3"
mime:
dependency: transitive
description:
@@ -911,10 +911,10 @@
dependency: transitive
description:
name: url_launcher_android
- sha256: "8a46fcbcd5b865e87053fc5096101d12f56f3fe352c42aa621e7fec9290054b7"
+ sha256: b413d49b73867ac08dd2f9890efd3cc11f2a0e577618d50843440a1fb3776c32
url: "https://pub.dev"
source: hosted
- version: "6.3.31"
+ version: "6.3.32"
url_launcher_ios:
dependency: transitive
description:
@@ -967,10 +967,10 @@
dependency: transitive
description:
name: vector_math
- sha256: "47a1b32ee755c3fcffa33db52a7258c137f97bdb2209a1075be847809fac4ccf"
+ sha256: "1d774bbdf6b72a0b12122fc1560c9c2d2a67db5a4a4cc2bd8a5c990ab20e3188"
url: "https://pub.dev"
source: hosted
- version: "2.3.0"
+ version: "2.4.0"
vm_service:
dependency: transitive
description: