From e5f62cc5a029469f46464a6930075731ce42a94d Mon Sep 17 00:00:00 2001 From: Kostia Sokolovskyi Date: Sun, 14 Jan 2024 22:24:50 +0100 Subject: [PATCH] Private disposables should dispatch creation and disposal events. (#141535) --- .../lib/src/material/mergeable_material.dart | 16 +++++++++- packages/flutter/lib/src/material/tabs.dart | 12 ++++++++ .../flutter/lib/src/material/time_picker.dart | 16 +++++++++- .../lib/src/painting/decoration_image.dart | 30 +++++++++++++++++-- .../flutter/lib/src/painting/image_cache.dart | 15 +++++++++- .../flutter/lib/src/rendering/binding.dart | 3 ++ .../widgets/draggable_scrollable_sheet.dart | 15 +++++++++- .../lib/src/widgets/focus_manager.dart | 15 ++++++++++ packages/flutter/lib/src/widgets/heroes.dart | 14 ++++++++- .../lib/src/widgets/nested_scroll_view.dart | 12 ++++++++ .../lib/src/widgets/overscroll_indicator.dart | 6 ++++ .../lib/src/widgets/reorderable_list.dart | 13 ++++++++ .../lib/src/widgets/semantics_debugger.dart | 12 ++++++++ .../lib/src/widgets/widget_inspector.dart | 15 +++++++++- .../test/widgets/memory_allocations_test.dart | 2 +- 15 files changed, 187 insertions(+), 9 deletions(-) diff --git a/packages/flutter/lib/src/material/mergeable_material.dart b/packages/flutter/lib/src/material/mergeable_material.dart index f209555d0d32..598be315eae1 100644 --- a/packages/flutter/lib/src/material/mergeable_material.dart +++ b/packages/flutter/lib/src/material/mergeable_material.dart @@ -4,6 +4,7 @@ import 'dart:ui' show lerpDouble; +import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; @@ -147,7 +148,17 @@ class _AnimationTuple { required this.startAnimation, required this.endAnimation, required this.gapAnimation, - }); + }) { + // TODO(polina-c): stop duplicating code across disposables + // https://github.com/flutter/flutter/issues/137435 + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectCreated( + library: 'package:flutter/material.dart', + className: '$_AnimationTuple', + object: this, + ); + } + } final AnimationController controller; final CurvedAnimation startAnimation; @@ -157,6 +168,9 @@ class _AnimationTuple { @mustCallSuper void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + } controller.dispose(); startAnimation.dispose(); endAnimation.dispose(); diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart index d6af252a1d0f..f630920386fd 100644 --- a/packages/flutter/lib/src/material/tabs.dart +++ b/packages/flutter/lib/src/material/tabs.dart @@ -437,6 +437,15 @@ class _IndicatorPainter extends CustomPainter { this.dividerHeight, required this.showDivider, }) : super(repaint: controller.animation) { + // TODO(polina-c): stop duplicating code across disposables + // https://github.com/flutter/flutter/issues/137435 + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectCreated( + library: 'package:flutter/material.dart', + className: '$_IndicatorPainter', + object: this, + ); + } if (old != null) { saveTabOffsets(old._currentTabOffsets, old._currentTextDirection); } @@ -466,6 +475,9 @@ class _IndicatorPainter extends CustomPainter { } void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + } _painter?.dispose(); } diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart index b6400d5eb5bc..ca97ffdb1ef9 100644 --- a/packages/flutter/lib/src/material/time_picker.dart +++ b/packages/flutter/lib/src/material/time_picker.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:math' as math; import 'dart:ui'; +import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; @@ -904,7 +905,17 @@ class _DialPainter extends CustomPainter { required this.radius, required this.textDirection, required this.selectedValue, - }) : super(repaint: PaintingBinding.instance.systemFonts); + }) : super(repaint: PaintingBinding.instance.systemFonts) { + // TODO(polina-c): stop duplicating code across disposables + // https://github.com/flutter/flutter/issues/137435 + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectCreated( + library: 'package:flutter/material.dart', + className: '$_DialPainter', + object: this, + ); + } + } final List<_TappableLabel> primaryLabels; final List<_TappableLabel> selectedLabels; @@ -920,6 +931,9 @@ class _DialPainter extends CustomPainter { final int selectedValue; void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + } for (final _TappableLabel label in primaryLabels) { label.painter.dispose(); } diff --git a/packages/flutter/lib/src/painting/decoration_image.dart b/packages/flutter/lib/src/painting/decoration_image.dart index eb9f76567947..85cef1af6533 100644 --- a/packages/flutter/lib/src/painting/decoration_image.dart +++ b/packages/flutter/lib/src/painting/decoration_image.dart @@ -311,7 +311,17 @@ abstract interface class DecorationImagePainter { } class _DecorationImagePainter implements DecorationImagePainter { - _DecorationImagePainter._(this._details, this._onChanged); + _DecorationImagePainter._(this._details, this._onChanged) { + // TODO(polina-c): stop duplicating code across disposables + // https://github.com/flutter/flutter/issues/137435 + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectCreated( + library: 'package:flutter/painting.dart', + className: '$_DecorationImagePainter', + object: this, + ); + } + } final DecorationImage _details; final VoidCallback _onChanged; @@ -404,6 +414,9 @@ class _DecorationImagePainter implements DecorationImagePainter { @override void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + } _imageStream?.removeListener(ImageStreamListener( _handleImage, onError: _details.onError, @@ -801,7 +814,17 @@ class _BlendedDecorationImage implements DecorationImage { } class _BlendedDecorationImagePainter implements DecorationImagePainter { - _BlendedDecorationImagePainter._(this.a, this.b, this.t); + _BlendedDecorationImagePainter._(this.a, this.b, this.t) { + // TODO(polina-c): stop duplicating code across disposables + // https://github.com/flutter/flutter/issues/137435 + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectCreated( + library: 'package:flutter/painting.dart', + className: '$_BlendedDecorationImagePainter', + object: this, + ); + } + } final DecorationImagePainter? a; final DecorationImagePainter? b; @@ -817,6 +840,9 @@ class _BlendedDecorationImagePainter implements DecorationImagePainter { @override void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + } a?.dispose(); b?.dispose(); } diff --git a/packages/flutter/lib/src/painting/image_cache.dart b/packages/flutter/lib/src/painting/image_cache.dart index 04b69a3f3d18..f2a36ed8f76e 100644 --- a/packages/flutter/lib/src/painting/image_cache.dart +++ b/packages/flutter/lib/src/painting/image_cache.dart @@ -606,7 +606,17 @@ abstract class _CachedImageBase { _CachedImageBase( this.completer, { this.sizeBytes, - }) : handle = completer.keepAlive(); + }) : handle = completer.keepAlive() { + // TODO(polina-c): stop duplicating code across disposables + // https://github.com/flutter/flutter/issues/137435 + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectCreated( + library: 'package:flutter/painting.dart', + className: '$_CachedImageBase', + object: this, + ); + } + } final ImageStreamCompleter completer; int? sizeBytes; @@ -615,6 +625,9 @@ abstract class _CachedImageBase { @mustCallSuper void dispose() { assert(handle != null); + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + } // Give any interested parties a chance to listen to the stream before we // potentially dispose it. SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) { diff --git a/packages/flutter/lib/src/rendering/binding.dart b/packages/flutter/lib/src/rendering/binding.dart index 6417185f8b01..56c67b5cc5a6 100644 --- a/packages/flutter/lib/src/rendering/binding.dart +++ b/packages/flutter/lib/src/rendering/binding.dart @@ -754,6 +754,9 @@ class RenderingFlutterBinding extends BindingBase with GestureBinding, Scheduler /// A [PipelineManifold] implementation that is backed by the [RendererBinding]. class _BindingPipelineManifold extends ChangeNotifier implements PipelineManifold { _BindingPipelineManifold(this._binding) { + if (kFlutterMemoryAllocationsEnabled) { + ChangeNotifier.maybeDispatchObjectCreation(this); + } _binding.addSemanticsEnabledListener(notifyListeners); } diff --git a/packages/flutter/lib/src/widgets/draggable_scrollable_sheet.dart b/packages/flutter/lib/src/widgets/draggable_scrollable_sheet.dart index b4543d2346e8..ce8cbd452991 100644 --- a/packages/flutter/lib/src/widgets/draggable_scrollable_sheet.dart +++ b/packages/flutter/lib/src/widgets/draggable_scrollable_sheet.dart @@ -494,7 +494,17 @@ class _DraggableSheetExtent { _currentSize = currentSize ?? ValueNotifier(initialSize), availablePixels = double.infinity, hasDragged = hasDragged ?? false, - hasChanged = hasChanged ?? false; + hasChanged = hasChanged ?? false { + // TODO(polina-c): stop duplicating code across disposables + // https://github.com/flutter/flutter/issues/137435 + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectCreated( + library: 'package:flutter/widgets.dart', + className: '$_DraggableSheetExtent', + object: this, + ); + } + } VoidCallback? _cancelActivity; @@ -590,6 +600,9 @@ class _DraggableSheetExtent { } void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + } _currentSize.dispose(); } diff --git a/packages/flutter/lib/src/widgets/focus_manager.dart b/packages/flutter/lib/src/widgets/focus_manager.dart index c6b72ddc83e3..f9464c270853 100644 --- a/packages/flutter/lib/src/widgets/focus_manager.dart +++ b/packages/flutter/lib/src/widgets/focus_manager.dart @@ -1831,6 +1831,18 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier { // This doesn't extend ChangeNotifier because the callback passes the updated // value, and ChangeNotifier requires using VoidCallback. class _HighlightModeManager { + _HighlightModeManager() { + // TODO(polina-c): stop duplicating code across disposables + // https://github.com/flutter/flutter/issues/137435 + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectCreated( + library: 'package:flutter/widgets.dart', + className: '$_HighlightModeManager', + object: this, + ); + } + } + // If set, indicates if the last interaction detected was touch or not. If // null, no interactions have occurred yet. bool? _lastInteractionWasTouch; @@ -1888,6 +1900,9 @@ class _HighlightModeManager { @mustCallSuper void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + } if (ServicesBinding.instance.keyEventManager.keyMessageHandler == handleKeyMessage) { GestureBinding.instance.pointerRouter.removeGlobalRoute(handlePointerEvent); ServicesBinding.instance.keyEventManager.keyMessageHandler = null; diff --git a/packages/flutter/lib/src/widgets/heroes.dart b/packages/flutter/lib/src/widgets/heroes.dart index aca86e3dfd01..4ca022cb7b12 100644 --- a/packages/flutter/lib/src/widgets/heroes.dart +++ b/packages/flutter/lib/src/widgets/heroes.dart @@ -510,6 +510,15 @@ class _HeroFlightManifest { // Builds the in-flight hero widget. class _HeroFlight { _HeroFlight(this.onFlightEnded) { + // TODO(polina-c): stop duplicating code across disposables + // https://github.com/flutter/flutter/issues/137435 + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectCreated( + library: 'package:flutter/widgets.dart', + className: '$_HeroFlight', + object: this, + ); + } _proxyAnimation = ProxyAnimation()..addStatusListener(_handleAnimationUpdate); } @@ -614,6 +623,9 @@ class _HeroFlight { /// Releases resources. @mustCallSuper void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + } if (overlayEntry != null) { overlayEntry!.remove(); overlayEntry!.dispose(); @@ -1008,7 +1020,7 @@ class HeroController extends NavigatorObserver { } void _handleFlightEnded(_HeroFlight flight) { - _flights.remove(flight.manifest.tag); + _flights.remove(flight.manifest.tag)?.dispose(); } Widget _defaultHeroFlightShuttleBuilder( diff --git a/packages/flutter/lib/src/widgets/nested_scroll_view.dart b/packages/flutter/lib/src/widgets/nested_scroll_view.dart index 6df3d917d875..5f3f8b0b4f4f 100644 --- a/packages/flutter/lib/src/widgets/nested_scroll_view.dart +++ b/packages/flutter/lib/src/widgets/nested_scroll_view.dart @@ -596,6 +596,15 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont this._onHasScrolledBodyChanged, this._floatHeaderSlivers, ) { + // TODO(polina-c): stop duplicating code across disposables + // https://github.com/flutter/flutter/issues/137435 + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectCreated( + library: 'package:flutter/widgets.dart', + className: '$_NestedScrollCoordinator', + object: this, + ); + } final double initialScrollOffset = _parent?.initialScrollOffset ?? 0.0; _outerController = _NestedScrollController( this, @@ -1123,6 +1132,9 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont @mustCallSuper void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + } _currentDrag?.dispose(); _currentDrag = null; _outerController.dispose(); diff --git a/packages/flutter/lib/src/widgets/overscroll_indicator.dart b/packages/flutter/lib/src/widgets/overscroll_indicator.dart index ef198d769be6..3d8b9f94d66d 100644 --- a/packages/flutter/lib/src/widgets/overscroll_indicator.dart +++ b/packages/flutter/lib/src/widgets/overscroll_indicator.dart @@ -308,6 +308,9 @@ class _GlowController extends ChangeNotifier { required Axis axis, }) : _color = color, _axis = axis { + if (kFlutterMemoryAllocationsEnabled) { + ChangeNotifier.maybeDispatchObjectCreation(this); + } _glowController = AnimationController(vsync: vsync) ..addStatusListener(_changePhase); final Animation decelerator = CurvedAnimation( @@ -812,6 +815,9 @@ enum _StretchState { class _StretchController extends ChangeNotifier { _StretchController({ required TickerProvider vsync }) { + if (kFlutterMemoryAllocationsEnabled) { + ChangeNotifier.maybeDispatchObjectCreation(this); + } _stretchController = AnimationController(vsync: vsync) ..addStatusListener(_changePhase); final Animation decelerator = CurvedAnimation( diff --git a/packages/flutter/lib/src/widgets/reorderable_list.dart b/packages/flutter/lib/src/widgets/reorderable_list.dart index 239bd18eaf1e..739404f851df 100644 --- a/packages/flutter/lib/src/widgets/reorderable_list.dart +++ b/packages/flutter/lib/src/widgets/reorderable_list.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; @@ -1339,6 +1340,15 @@ class _DragInfo extends Drag { this.proxyDecorator, required this.tickerProvider, }) { + // TODO(polina-c): stop duplicating code across disposables + // https://github.com/flutter/flutter/issues/137435 + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectCreated( + library: 'package:flutter/widgets.dart', + className: '$_DragInfo', + object: this, + ); + } final RenderBox itemRenderBox = item.context.findRenderObject()! as RenderBox; listState = item._listState; index = item.index; @@ -1371,6 +1381,9 @@ class _DragInfo extends Drag { AnimationController? _proxyAnimation; void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + } _proxyAnimation?.dispose(); } diff --git a/packages/flutter/lib/src/widgets/semantics_debugger.dart b/packages/flutter/lib/src/widgets/semantics_debugger.dart index 6b6c69aa2ba9..16c0667e6ad9 100644 --- a/packages/flutter/lib/src/widgets/semantics_debugger.dart +++ b/packages/flutter/lib/src/widgets/semantics_debugger.dart @@ -182,6 +182,15 @@ class _SemanticsDebuggerState extends State with WidgetsBindi class _SemanticsClient extends ChangeNotifier { _SemanticsClient(PipelineOwner pipelineOwner) { + // TODO(polina-c): stop duplicating code across disposables + // https://github.com/flutter/flutter/issues/137435 + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectCreated( + library: 'package:flutter/widgets.dart', + className: '$_SemanticsClient', + object: this, + ); + } _semanticsHandle = pipelineOwner.ensureSemantics( listener: _didUpdateSemantics, ); @@ -191,6 +200,9 @@ class _SemanticsClient extends ChangeNotifier { @override void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + } _semanticsHandle!.dispose(); _semanticsHandle = null; super.dispose(); diff --git a/packages/flutter/lib/src/widgets/widget_inspector.dart b/packages/flutter/lib/src/widgets/widget_inspector.dart index 2ad546bedad2..11e758e7bb63 100644 --- a/packages/flutter/lib/src/widgets/widget_inspector.dart +++ b/packages/flutter/lib/src/widgets/widget_inspector.dart @@ -336,7 +336,17 @@ class _ScreenshotContainerLayer extends OffsetLayer { class _ScreenshotData { _ScreenshotData({ required this.target, - }) : containerLayer = _ScreenshotContainerLayer(); + }) : containerLayer = _ScreenshotContainerLayer() { + // TODO(polina-c): stop duplicating code across disposables + // https://github.com/flutter/flutter/issues/137435 + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectCreated( + library: 'package:flutter/widgets.dart', + className: '$_ScreenshotData', + object: this, + ); + } + } /// Target to take a screenshot of. final RenderObject target; @@ -376,6 +386,9 @@ class _ScreenshotData { /// Releases allocated resources. @mustCallSuper void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this); + } containerLayer.dispose(); } } diff --git a/packages/flutter/test/widgets/memory_allocations_test.dart b/packages/flutter/test/widgets/memory_allocations_test.dart index a6c45cc45729..6b8bfad9e0e5 100644 --- a/packages/flutter/test/widgets/memory_allocations_test.dart +++ b/packages/flutter/test/widgets/memory_allocations_test.dart @@ -131,7 +131,7 @@ Future<_EventStats> _activateFlutterObjectsAndReturnCountOfEvents() async { final _TestElement element = _TestElement(); result.creations++; final RenderObject renderObject = _TestRenderObject(); result.creations++; - element.makeInactive(); result.creations += 3; // 1 for the new BuildOwner, 1 for the new FocusManager, 1 for the new FocusScopeNode + element.makeInactive(); result.creations += 4; // 1 for the new BuildOwner, 1 for the new FocusManager, 1 for the new FocusScopeNode, 1 for the new _HighlightModeManager element.unmount(); result.disposals += 2; // 1 for the old BuildOwner, 1 for the element renderObject.dispose(); result.disposals += 1;