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: