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: