Skip to content

Commit

Permalink
Dispose Paragraph objects (#110627)
Browse files Browse the repository at this point in the history
  • Loading branch information
dnfield committed Sep 6, 2022
1 parent 783e7b0 commit eaff156
Show file tree
Hide file tree
Showing 18 changed files with 176 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class _MyScrollContainerState extends State<_MyScrollContainer> {
static const Duration stepDuration = Duration(milliseconds: 500);

late PageController pageController;
final _CustomPainter _painter = _CustomPainter('aa');
int pageNumber = 0;

@override
Expand All @@ -57,6 +58,7 @@ class _MyScrollContainerState extends State<_MyScrollContainer> {
@override
void dispose() {
pageController.dispose();
_painter._textPainter.dispose();
super.dispose();
}

Expand All @@ -66,7 +68,7 @@ class _MyScrollContainerState extends State<_MyScrollContainer> {
controller: pageController,
itemBuilder: (BuildContext context, int position) {
return CustomPaint(
painter: _CustomPainter('aa'),
painter: _painter,
size: const Size(300, 500),
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ class BoardPainter extends CustomPainter {
);
final Vertices vertices = board!.getVerticesForBoardPoint(boardPoint, color);
canvas.drawVertices(vertices, BlendMode.color, Paint());
vertices.dispose();
}

board!.forEach(drawBoardPoint);
Expand Down
8 changes: 6 additions & 2 deletions packages/flutter/lib/src/cupertino/date_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -482,8 +482,11 @@ class CupertinoDatePicker extends StatefulWidget {
// because there's no other way to get the information we want without
// laying out the text.
painter.layout();

return painter.maxIntrinsicWidth;
try {
return painter.maxIntrinsicWidth;
} finally {
painter.dispose();
}
}
}

Expand Down Expand Up @@ -1663,6 +1666,7 @@ class _CupertinoTimerPickerState extends State<CupertinoTimerPicker> {
@override
void dispose() {
PaintingBinding.instance.systemFonts.removeListener(_handleSystemFontsChange);
textPainter.dispose();
super.dispose();
}

Expand Down
7 changes: 7 additions & 0 deletions packages/flutter/lib/src/material/range_slider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,13 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
super.detach();
}

@override
void dispose() {
_startLabelPainter.dispose();
_endLabelPainter.dispose();
super.dispose();
}

double _getValueFromVisualPosition(double visualPosition) {
switch (textDirection) {
case TextDirection.rtl:
Expand Down
6 changes: 6 additions & 0 deletions packages/flutter/lib/src/material/slider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1342,6 +1342,12 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
super.detach();
}

@override
void dispose() {
_labelPainter.dispose();
super.dispose();
}

double _getValueFromVisualPosition(double visualPosition) {
switch (textDirection) {
case TextDirection.rtl:
Expand Down
36 changes: 26 additions & 10 deletions packages/flutter/lib/src/material/time_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,17 @@ class _DialPainter extends CustomPainter {

static const double _labelPadding = 28.0;

void dispose() {
for (final _TappableLabel label in primaryLabels) {
label.painter.dispose();
}
for (final _TappableLabel label in secondaryLabels) {
label.painter.dispose();
}
primaryLabels.clear();
secondaryLabels.clear();
}

@override
void paint(Canvas canvas, Size size) {
final double radius = size.shortestSide / 2.0;
Expand Down Expand Up @@ -966,6 +977,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
late ThemeData themeData;
late MaterialLocalizations localizations;
late MediaQueryData media;
_DialPainter? painter;

@override
void didChangeDependencies() {
Expand All @@ -989,6 +1001,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
@override
void dispose() {
_thetaController.dispose();
painter?.dispose();
super.dispose();
}

Expand Down Expand Up @@ -1280,6 +1293,18 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
break;
}

painter?.dispose();
painter = _DialPainter(
selectedValue: selectedDialValue,
primaryLabels: primaryLabels,
secondaryLabels: secondaryLabels,
backgroundColor: backgroundColor,
accentColor: accentColor,
dotColor: theme.colorScheme.surface,
theta: _theta.value,
textDirection: Directionality.of(context),
);

return GestureDetector(
excludeFromSemantics: true,
onPanStart: _handlePanStart,
Expand All @@ -1288,16 +1313,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
onTapUp: _handleTapUp,
child: CustomPaint(
key: const ValueKey<String>('time-picker-dial'),
painter: _DialPainter(
selectedValue: selectedDialValue,
primaryLabels: primaryLabels,
secondaryLabels: secondaryLabels,
backgroundColor: backgroundColor,
accentColor: accentColor,
dotColor: theme.colorScheme.surface,
theta: _theta.value,
textDirection: Directionality.of(context),
),
painter: painter,
),
);
}
Expand Down
1 change: 1 addition & 0 deletions packages/flutter/lib/src/painting/flutter_logo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ class _FlutterLogoPainter extends BoxPainter {
final FlutterLogoDecoration _config;

// these are configured assuming a font size of 100.0.
// TODO(dnfield): Figure out how to dispose this https://github.com/flutter/flutter/issues/110601
late TextPainter _textPainter;
late Rect _textBoundingRect;

Expand Down
34 changes: 34 additions & 0 deletions packages/flutter/lib/src/painting/text_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ class TextPainter {
bool _rebuildParagraphForPaint = true;

bool get _debugAssertTextLayoutIsValid {
assert(!debugDisposed);
if (_paragraph == null) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Text layout not available'),
Expand All @@ -247,6 +248,7 @@ class TextPainter {
}
return true;
}());
_paragraph?.dispose();
_paragraph = null;
_lineMetricsCache = null;
_previousCaretPosition = null;
Expand All @@ -271,6 +273,7 @@ class TextPainter {
return;
}
if (_text?.style != value?.style) {
_layoutTemplate?.dispose();
_layoutTemplate = null;
}

Expand Down Expand Up @@ -329,6 +332,7 @@ class TextPainter {
}
_textDirection = value;
markNeedsLayout();
_layoutTemplate?.dispose();
_layoutTemplate = null; // Shouldn't really matter, but for strict correctness...
}

Expand All @@ -347,6 +351,7 @@ class TextPainter {
}
_textScaleFactor = value;
markNeedsLayout();
_layoutTemplate?.dispose();
_layoutTemplate = null;
}

Expand Down Expand Up @@ -1060,4 +1065,33 @@ class TextPainter {
assert(_debugAssertTextLayoutIsValid);
return _lineMetricsCache ??= _paragraph!.computeLineMetrics();
}

bool _disposed = false;

/// Whether this object has been disposed or not.
///
/// Only for use when asserts are enabled.
bool get debugDisposed {
bool? disposed;
assert(() {
disposed = _disposed;
return true;
}());
return disposed ?? (throw StateError('debugDisposed only available when asserts are on.'));
}

/// Releases the resources associated with this painter.
///
/// After disposal this painter is unusable.
void dispose() {
assert(() {
_disposed = true;
return true;
}());
_layoutTemplate?.dispose();
_layoutTemplate = null;
_paragraph?.dispose();
_paragraph = null;
_text = null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ mixin DebugOverflowIndicatorMixin on RenderObject {
TextPainter(textDirection: TextDirection.ltr), // This label is in English.
);

@override
void dispose() {
for (final TextPainter painter in _indicatorLabel) {
painter.dispose();
}
super.dispose();
}

// Set to true to trigger a debug message in the console upon
// the next paint call. Will be reset after each paint.
bool _overflowReportNeeded = true;
Expand Down
1 change: 1 addition & 0 deletions packages/flutter/lib/src/rendering/editable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
_autocorrectHighlightPainter.dispose();
_selectionPainter.dispose();
_caretPainter.dispose();
_textPainter.dispose();
super.dispose();
}

Expand Down
2 changes: 2 additions & 0 deletions packages/flutter/lib/src/rendering/paragraph.dart
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ class RenderParagraph extends RenderBox
// _lastSelectableFragments may hold references to this RenderParagraph.
// Release them manually to avoid retain cycles.
_lastSelectableFragments = null;
_textPainter.dispose();
super.dispose();
}

Expand Down Expand Up @@ -864,6 +865,7 @@ class RenderParagraph extends RenderBox
<Color>[const Color(0xFFFFFFFF), const Color(0x00FFFFFF)],
);
}
fadeSizePainter.dispose();
break;
}
} else {
Expand Down
7 changes: 7 additions & 0 deletions packages/flutter/lib/src/rendering/proxy_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1515,6 +1515,13 @@ abstract class _RenderCustomClip<T> extends RenderProxyBox {
return true;
}());
}

@override
void dispose() {
_debugText?.dispose();
_debugText = null;
super.dispose();
}
}

/// Clips its child using a rectangle.
Expand Down
15 changes: 12 additions & 3 deletions packages/flutter/lib/src/widgets/banner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,23 @@ class BannerPainter extends CustomPainter {
);

bool _prepared = false;
late TextPainter _textPainter;
TextPainter? _textPainter;
late Paint _paintShadow;
late Paint _paintBanner;

/// Release resources held by this painter.
///
/// After calling this method, this object is no longer usable.
void dispose() {
_textPainter?.dispose();
_textPainter = null;
}

void _prepare() {
_paintShadow = _shadow.toPaint();
_paintBanner = Paint()
..color = color;
_textPainter?.dispose();
_textPainter = TextPainter(
text: TextSpan(style: textStyle, text: message),
textAlign: TextAlign.center,
Expand All @@ -144,8 +153,8 @@ class BannerPainter extends CustomPainter {
..drawRect(_kRect, _paintShadow)
..drawRect(_kRect, _paintBanner);
const double width = _kOffset * 2.0;
_textPainter.layout(minWidth: width, maxWidth: width);
_textPainter.paint(canvas, _kRect.topLeft + Offset(0.0, (_kRect.height - _textPainter.height) / 2.0));
_textPainter!.layout(minWidth: width, maxWidth: width);
_textPainter!.paint(canvas, _kRect.topLeft + Offset(0.0, (_kRect.height - _textPainter!.height) / 2.0));
}

@override
Expand Down
1 change: 1 addition & 0 deletions packages/flutter/lib/src/widgets/semantics_debugger.dart
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ class _SemanticsDebuggerPainter extends CustomPainter {
..layout(maxWidth: rect.width);

textPainter.paint(canvas, Alignment.center.inscribe(textPainter.size, rect).topLeft);
textPainter.dispose();
canvas.restore();
}

Expand Down
8 changes: 8 additions & 0 deletions packages/flutter/lib/src/widgets/widget_inspector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2777,6 +2777,13 @@ class _InspectorOverlayLayer extends Layer {
TextPainter? _textPainter;
double? _textPainterMaxWidth;

@override
void dispose() {
_textPainter?.dispose();
_textPainter = null;
super.dispose();
}

@override
void addToScene(ui.SceneBuilder builder) {
if (!selection.active) {
Expand Down Expand Up @@ -2878,6 +2885,7 @@ class _InspectorOverlayLayer extends Layer {
final TextSpan? textSpan = _textPainter?.text as TextSpan?;
if (_textPainter == null || textSpan!.text != message || _textPainterMaxWidth != maxWidth) {
_textPainterMaxWidth = maxWidth;
_textPainter?.dispose();
_textPainter = TextPainter()
..maxLines = _kMaxTooltipLines
..ellipsis = '...'
Expand Down
Loading

0 comments on commit eaff156

Please sign in to comment.