Unverified Commit eaff1562 authored by Dan Field's avatar Dan Field Committed by GitHub

Dispose Paragraph objects (#110627)

parent 783e7b01
...@@ -36,6 +36,7 @@ class _MyScrollContainerState extends State<_MyScrollContainer> { ...@@ -36,6 +36,7 @@ class _MyScrollContainerState extends State<_MyScrollContainer> {
static const Duration stepDuration = Duration(milliseconds: 500); static const Duration stepDuration = Duration(milliseconds: 500);
late PageController pageController; late PageController pageController;
final _CustomPainter _painter = _CustomPainter('aa');
int pageNumber = 0; int pageNumber = 0;
@override @override
...@@ -57,6 +58,7 @@ class _MyScrollContainerState extends State<_MyScrollContainer> { ...@@ -57,6 +58,7 @@ class _MyScrollContainerState extends State<_MyScrollContainer> {
@override @override
void dispose() { void dispose() {
pageController.dispose(); pageController.dispose();
_painter._textPainter.dispose();
super.dispose(); super.dispose();
} }
...@@ -66,7 +68,7 @@ class _MyScrollContainerState extends State<_MyScrollContainer> { ...@@ -66,7 +68,7 @@ class _MyScrollContainerState extends State<_MyScrollContainer> {
controller: pageController, controller: pageController,
itemBuilder: (BuildContext context, int position) { itemBuilder: (BuildContext context, int position) {
return CustomPaint( return CustomPaint(
painter: _CustomPainter('aa'), painter: _painter,
size: const Size(300, 500), size: const Size(300, 500),
); );
}); });
......
...@@ -182,6 +182,7 @@ class BoardPainter extends CustomPainter { ...@@ -182,6 +182,7 @@ class BoardPainter extends CustomPainter {
); );
final Vertices vertices = board!.getVerticesForBoardPoint(boardPoint, color); final Vertices vertices = board!.getVerticesForBoardPoint(boardPoint, color);
canvas.drawVertices(vertices, BlendMode.color, Paint()); canvas.drawVertices(vertices, BlendMode.color, Paint());
vertices.dispose();
} }
board!.forEach(drawBoardPoint); board!.forEach(drawBoardPoint);
......
...@@ -482,8 +482,11 @@ class CupertinoDatePicker extends StatefulWidget { ...@@ -482,8 +482,11 @@ class CupertinoDatePicker extends StatefulWidget {
// because there's no other way to get the information we want without // because there's no other way to get the information we want without
// laying out the text. // laying out the text.
painter.layout(); painter.layout();
try {
return painter.maxIntrinsicWidth; return painter.maxIntrinsicWidth;
} finally {
painter.dispose();
}
} }
} }
...@@ -1663,6 +1666,7 @@ class _CupertinoTimerPickerState extends State<CupertinoTimerPicker> { ...@@ -1663,6 +1666,7 @@ class _CupertinoTimerPickerState extends State<CupertinoTimerPicker> {
@override @override
void dispose() { void dispose() {
PaintingBinding.instance.systemFonts.removeListener(_handleSystemFontsChange); PaintingBinding.instance.systemFonts.removeListener(_handleSystemFontsChange);
textPainter.dispose();
super.dispose(); super.dispose();
} }
......
...@@ -1090,6 +1090,13 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix ...@@ -1090,6 +1090,13 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
super.detach(); super.detach();
} }
@override
void dispose() {
_startLabelPainter.dispose();
_endLabelPainter.dispose();
super.dispose();
}
double _getValueFromVisualPosition(double visualPosition) { double _getValueFromVisualPosition(double visualPosition) {
switch (textDirection) { switch (textDirection) {
case TextDirection.rtl: case TextDirection.rtl:
......
...@@ -1342,6 +1342,12 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { ...@@ -1342,6 +1342,12 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
super.detach(); super.detach();
} }
@override
void dispose() {
_labelPainter.dispose();
super.dispose();
}
double _getValueFromVisualPosition(double visualPosition) { double _getValueFromVisualPosition(double visualPosition) {
switch (textDirection) { switch (textDirection) {
case TextDirection.rtl: case TextDirection.rtl:
......
...@@ -860,6 +860,17 @@ class _DialPainter extends CustomPainter { ...@@ -860,6 +860,17 @@ class _DialPainter extends CustomPainter {
static const double _labelPadding = 28.0; 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 @override
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {
final double radius = size.shortestSide / 2.0; final double radius = size.shortestSide / 2.0;
...@@ -966,6 +977,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { ...@@ -966,6 +977,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
late ThemeData themeData; late ThemeData themeData;
late MaterialLocalizations localizations; late MaterialLocalizations localizations;
late MediaQueryData media; late MediaQueryData media;
_DialPainter? painter;
@override @override
void didChangeDependencies() { void didChangeDependencies() {
...@@ -989,6 +1001,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { ...@@ -989,6 +1001,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
@override @override
void dispose() { void dispose() {
_thetaController.dispose(); _thetaController.dispose();
painter?.dispose();
super.dispose(); super.dispose();
} }
...@@ -1280,6 +1293,18 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { ...@@ -1280,6 +1293,18 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
break; 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( return GestureDetector(
excludeFromSemantics: true, excludeFromSemantics: true,
onPanStart: _handlePanStart, onPanStart: _handlePanStart,
...@@ -1288,16 +1313,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { ...@@ -1288,16 +1313,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
onTapUp: _handleTapUp, onTapUp: _handleTapUp,
child: CustomPaint( child: CustomPaint(
key: const ValueKey<String>('time-picker-dial'), key: const ValueKey<String>('time-picker-dial'),
painter: _DialPainter( painter: painter,
selectedValue: selectedDialValue,
primaryLabels: primaryLabels,
secondaryLabels: secondaryLabels,
backgroundColor: backgroundColor,
accentColor: accentColor,
dotColor: theme.colorScheme.surface,
theta: _theta.value,
textDirection: Directionality.of(context),
),
), ),
); );
} }
......
...@@ -227,6 +227,7 @@ class _FlutterLogoPainter extends BoxPainter { ...@@ -227,6 +227,7 @@ class _FlutterLogoPainter extends BoxPainter {
final FlutterLogoDecoration _config; final FlutterLogoDecoration _config;
// these are configured assuming a font size of 100.0. // 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 TextPainter _textPainter;
late Rect _textBoundingRect; late Rect _textBoundingRect;
......
...@@ -222,6 +222,7 @@ class TextPainter { ...@@ -222,6 +222,7 @@ class TextPainter {
bool _rebuildParagraphForPaint = true; bool _rebuildParagraphForPaint = true;
bool get _debugAssertTextLayoutIsValid { bool get _debugAssertTextLayoutIsValid {
assert(!debugDisposed);
if (_paragraph == null) { if (_paragraph == null) {
throw FlutterError.fromParts(<DiagnosticsNode>[ throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Text layout not available'), ErrorSummary('Text layout not available'),
...@@ -247,6 +248,7 @@ class TextPainter { ...@@ -247,6 +248,7 @@ class TextPainter {
} }
return true; return true;
}()); }());
_paragraph?.dispose();
_paragraph = null; _paragraph = null;
_lineMetricsCache = null; _lineMetricsCache = null;
_previousCaretPosition = null; _previousCaretPosition = null;
...@@ -271,6 +273,7 @@ class TextPainter { ...@@ -271,6 +273,7 @@ class TextPainter {
return; return;
} }
if (_text?.style != value?.style) { if (_text?.style != value?.style) {
_layoutTemplate?.dispose();
_layoutTemplate = null; _layoutTemplate = null;
} }
...@@ -329,6 +332,7 @@ class TextPainter { ...@@ -329,6 +332,7 @@ class TextPainter {
} }
_textDirection = value; _textDirection = value;
markNeedsLayout(); markNeedsLayout();
_layoutTemplate?.dispose();
_layoutTemplate = null; // Shouldn't really matter, but for strict correctness... _layoutTemplate = null; // Shouldn't really matter, but for strict correctness...
} }
...@@ -347,6 +351,7 @@ class TextPainter { ...@@ -347,6 +351,7 @@ class TextPainter {
} }
_textScaleFactor = value; _textScaleFactor = value;
markNeedsLayout(); markNeedsLayout();
_layoutTemplate?.dispose();
_layoutTemplate = null; _layoutTemplate = null;
} }
...@@ -1060,4 +1065,33 @@ class TextPainter { ...@@ -1060,4 +1065,33 @@ class TextPainter {
assert(_debugAssertTextLayoutIsValid); assert(_debugAssertTextLayoutIsValid);
return _lineMetricsCache ??= _paragraph!.computeLineMetrics(); 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;
}
} }
...@@ -114,6 +114,14 @@ mixin DebugOverflowIndicatorMixin on RenderObject { ...@@ -114,6 +114,14 @@ mixin DebugOverflowIndicatorMixin on RenderObject {
TextPainter(textDirection: TextDirection.ltr), // This label is in English. 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 // Set to true to trigger a debug message in the console upon
// the next paint call. Will be reset after each paint. // the next paint call. Will be reset after each paint.
bool _overflowReportNeeded = true; bool _overflowReportNeeded = true;
......
...@@ -388,6 +388,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin, ...@@ -388,6 +388,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
_autocorrectHighlightPainter.dispose(); _autocorrectHighlightPainter.dispose();
_selectionPainter.dispose(); _selectionPainter.dispose();
_caretPainter.dispose(); _caretPainter.dispose();
_textPainter.dispose();
super.dispose(); super.dispose();
} }
......
...@@ -256,6 +256,7 @@ class RenderParagraph extends RenderBox ...@@ -256,6 +256,7 @@ class RenderParagraph extends RenderBox
// _lastSelectableFragments may hold references to this RenderParagraph. // _lastSelectableFragments may hold references to this RenderParagraph.
// Release them manually to avoid retain cycles. // Release them manually to avoid retain cycles.
_lastSelectableFragments = null; _lastSelectableFragments = null;
_textPainter.dispose();
super.dispose(); super.dispose();
} }
...@@ -864,6 +865,7 @@ class RenderParagraph extends RenderBox ...@@ -864,6 +865,7 @@ class RenderParagraph extends RenderBox
<Color>[const Color(0xFFFFFFFF), const Color(0x00FFFFFF)], <Color>[const Color(0xFFFFFFFF), const Color(0x00FFFFFF)],
); );
} }
fadeSizePainter.dispose();
break; break;
} }
} else { } else {
......
...@@ -1515,6 +1515,13 @@ abstract class _RenderCustomClip<T> extends RenderProxyBox { ...@@ -1515,6 +1515,13 @@ abstract class _RenderCustomClip<T> extends RenderProxyBox {
return true; return true;
}()); }());
} }
@override
void dispose() {
_debugText?.dispose();
_debugText = null;
super.dispose();
}
} }
/// Clips its child using a rectangle. /// Clips its child using a rectangle.
......
...@@ -117,14 +117,23 @@ class BannerPainter extends CustomPainter { ...@@ -117,14 +117,23 @@ class BannerPainter extends CustomPainter {
); );
bool _prepared = false; bool _prepared = false;
late TextPainter _textPainter; TextPainter? _textPainter;
late Paint _paintShadow; late Paint _paintShadow;
late Paint _paintBanner; 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() { void _prepare() {
_paintShadow = _shadow.toPaint(); _paintShadow = _shadow.toPaint();
_paintBanner = Paint() _paintBanner = Paint()
..color = color; ..color = color;
_textPainter?.dispose();
_textPainter = TextPainter( _textPainter = TextPainter(
text: TextSpan(style: textStyle, text: message), text: TextSpan(style: textStyle, text: message),
textAlign: TextAlign.center, textAlign: TextAlign.center,
...@@ -144,8 +153,8 @@ class BannerPainter extends CustomPainter { ...@@ -144,8 +153,8 @@ class BannerPainter extends CustomPainter {
..drawRect(_kRect, _paintShadow) ..drawRect(_kRect, _paintShadow)
..drawRect(_kRect, _paintBanner); ..drawRect(_kRect, _paintBanner);
const double width = _kOffset * 2.0; const double width = _kOffset * 2.0;
_textPainter.layout(minWidth: width, maxWidth: width); _textPainter!.layout(minWidth: width, maxWidth: width);
_textPainter.paint(canvas, _kRect.topLeft + Offset(0.0, (_kRect.height - _textPainter.height) / 2.0)); _textPainter!.paint(canvas, _kRect.topLeft + Offset(0.0, (_kRect.height - _textPainter!.height) / 2.0));
} }
@override @override
......
...@@ -339,6 +339,7 @@ class _SemanticsDebuggerPainter extends CustomPainter { ...@@ -339,6 +339,7 @@ class _SemanticsDebuggerPainter extends CustomPainter {
..layout(maxWidth: rect.width); ..layout(maxWidth: rect.width);
textPainter.paint(canvas, Alignment.center.inscribe(textPainter.size, rect).topLeft); textPainter.paint(canvas, Alignment.center.inscribe(textPainter.size, rect).topLeft);
textPainter.dispose();
canvas.restore(); canvas.restore();
} }
......
...@@ -2777,6 +2777,13 @@ class _InspectorOverlayLayer extends Layer { ...@@ -2777,6 +2777,13 @@ class _InspectorOverlayLayer extends Layer {
TextPainter? _textPainter; TextPainter? _textPainter;
double? _textPainterMaxWidth; double? _textPainterMaxWidth;
@override
void dispose() {
_textPainter?.dispose();
_textPainter = null;
super.dispose();
}
@override @override
void addToScene(ui.SceneBuilder builder) { void addToScene(ui.SceneBuilder builder) {
if (!selection.active) { if (!selection.active) {
...@@ -2878,6 +2885,7 @@ class _InspectorOverlayLayer extends Layer { ...@@ -2878,6 +2885,7 @@ class _InspectorOverlayLayer extends Layer {
final TextSpan? textSpan = _textPainter?.text as TextSpan?; final TextSpan? textSpan = _textPainter?.text as TextSpan?;
if (_textPainter == null || textSpan!.text != message || _textPainterMaxWidth != maxWidth) { if (_textPainter == null || textSpan!.text != message || _textPainterMaxWidth != maxWidth) {
_textPainterMaxWidth = maxWidth; _textPainterMaxWidth = maxWidth;
_textPainter?.dispose();
_textPainter = TextPainter() _textPainter = TextPainter()
..maxLines = _kMaxTooltipLines ..maxLines = _kMaxTooltipLines
..ellipsis = '...' ..ellipsis = '...'
......
...@@ -32,6 +32,7 @@ void main() { ...@@ -32,6 +32,7 @@ void main() {
painter.getWordBoundary(const TextPosition(offset: 9)), painter.getWordBoundary(const TextPosition(offset: 9)),
const TextRange(start: 8, end: 11), const TextRange(start: 8, end: 11),
); );
painter.dispose();
}); });
test('TextPainter - bidi overrides in LTR', () { test('TextPainter - bidi overrides in LTR', () {
...@@ -164,6 +165,7 @@ void main() { ...@@ -164,6 +165,7 @@ void main() {
// The list currently has one extra bogus entry (the last entry, for the // The list currently has one extra bogus entry (the last entry, for the
// trailing U+202C PDF, should be empty but is one-pixel-wide instead). // trailing U+202C PDF, should be empty but is one-pixel-wide instead).
], skip: skipExpectsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536 ], skip: skipExpectsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536 }, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - bidi overrides in RTL', () { test('TextPainter - bidi overrides in RTL', () {
...@@ -255,6 +257,7 @@ void main() { ...@@ -255,6 +257,7 @@ void main() {
// The list is currently in the wrong order (so selection boxes will paint in the wrong order). // The list is currently in the wrong order (so selection boxes will paint in the wrong order).
skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536 skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536
); );
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536 }, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - forced line-wrapping with bidi', () { test('TextPainter - forced line-wrapping with bidi', () {
...@@ -322,6 +325,7 @@ void main() { ...@@ -322,6 +325,7 @@ void main() {
TextBox.fromLTRBD(0.0, 10.0, 10.0, 20.0, TextDirection.rtl), // Alef TextBox.fromLTRBD(0.0, 10.0, 10.0, 20.0, TextDirection.rtl), // Alef
], ],
); );
painter.dispose();
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/32238 }, skip: isBrowser); // https://github.com/flutter/flutter/issues/32238
test('TextPainter - line wrap mid-word', () { test('TextPainter - line wrap mid-word', () {
...@@ -356,6 +360,7 @@ void main() { ...@@ -356,6 +360,7 @@ void main() {
// horizontal offsets are one pixel off in places; vertical offsets are good // horizontal offsets are one pixel off in places; vertical offsets are good
skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536 skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536
); );
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536 }, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - line wrap mid-word, bidi - LTR base', () { test('TextPainter - line wrap mid-word, bidi - LTR base', () {
...@@ -412,6 +417,7 @@ void main() { ...@@ -412,6 +417,7 @@ void main() {
<TextBox>[TextBox.fromLTRBD(70.0, 28.0, 80.0, 38.0, TextDirection.ltr)], <TextBox>[TextBox.fromLTRBD(70.0, 28.0, 80.0, 38.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(80.0, 28.0, 90.0, 38.0, TextDirection.ltr)], <TextBox>[TextBox.fromLTRBD(80.0, 28.0, 90.0, 38.0, TextDirection.ltr)],
]); ]);
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536 }, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - line wrap mid-word, bidi - RTL base', () { test('TextPainter - line wrap mid-word, bidi - RTL base', () {
...@@ -447,6 +453,7 @@ void main() { ...@@ -447,6 +453,7 @@ void main() {
// The list is currently in the wrong order (so selection boxes will paint in the wrong order). // The list is currently in the wrong order (so selection boxes will paint in the wrong order).
skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536 skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536
); );
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536 }, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - multiple levels', () { test('TextPainter - multiple levels', () {
...@@ -478,6 +485,7 @@ void main() { ...@@ -478,6 +485,7 @@ void main() {
// Also currently there's an extraneous box at the start of the list. // Also currently there's an extraneous box at the start of the list.
skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536 skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536
); );
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536 }, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - getPositionForOffset - RTL in LTR', () { test('TextPainter - getPositionForOffset - RTL in LTR', () {
...@@ -561,6 +569,7 @@ void main() { ...@@ -561,6 +569,7 @@ void main() {
painter.getPositionForOffset(const Offset(100.0, 5.0)).toString(), painter.getPositionForOffset(const Offset(100.0, 5.0)).toString(),
const TextPosition(offset: 9, affinity: TextAffinity.upstream).toString(), const TextPosition(offset: 9, affinity: TextAffinity.upstream).toString(),
); );
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536 }, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - getPositionForOffset - LTR in RTL', () { test('TextPainter - getPositionForOffset - LTR in RTL', () {
...@@ -606,6 +615,7 @@ void main() { ...@@ -606,6 +615,7 @@ void main() {
painter.getPositionForOffset(const Offset(62.0, 5.0)).toString(), painter.getPositionForOffset(const Offset(62.0, 5.0)).toString(),
const TextPosition(offset: 3, affinity: TextAffinity.upstream).toString(), const TextPosition(offset: 3, affinity: TextAffinity.upstream).toString(),
); );
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536 }, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - Spaces', () { test('TextPainter - Spaces', () {
...@@ -667,6 +677,7 @@ void main() { ...@@ -667,6 +677,7 @@ void main() {
// Horizontal offsets are currently one pixel off in places; vertical offsets are good. // Horizontal offsets are currently one pixel off in places; vertical offsets are good.
skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536 skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536
); );
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536 }, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - empty text baseline', () { test('TextPainter - empty text baseline', () {
...@@ -678,6 +689,7 @@ void main() { ...@@ -678,6 +689,7 @@ void main() {
); );
painter.layout(); painter.layout();
expect(painter.computeDistanceToActualBaseline(TextBaseline.alphabetic), moreOrLessEquals(80.0, epsilon: 0.001)); expect(painter.computeDistanceToActualBaseline(TextBaseline.alphabetic), moreOrLessEquals(80.0, epsilon: 0.001));
painter.dispose();
}); });
} }
......
...@@ -35,6 +35,7 @@ void main() { ...@@ -35,6 +35,7 @@ void main() {
painter.layout(); painter.layout();
caretOffset = painter.getOffsetForCaret(ui.TextPosition(offset: text.length), ui.Rect.zero); caretOffset = painter.getOffsetForCaret(ui.TextPosition(offset: text.length), ui.Rect.zero);
expect(caretOffset.dx, painter.width); expect(caretOffset.dx, painter.width);
painter.dispose();
}); });
test('TextPainter caret test with WidgetSpan', () { test('TextPainter caret test with WidgetSpan', () {
...@@ -53,6 +54,7 @@ void main() { ...@@ -53,6 +54,7 @@ void main() {
painter.layout(); painter.layout();
final Offset caretOffset = painter.getOffsetForCaret(ui.TextPosition(offset: painter.text!.toPlainText().length), ui.Rect.zero); final Offset caretOffset = painter.getOffsetForCaret(ui.TextPosition(offset: painter.text!.toPlainText().length), ui.Rect.zero);
expect(caretOffset.dx, painter.width); expect(caretOffset.dx, painter.width);
painter.dispose();
}, skip: isBrowser && !isCanvasKit); // https://github.com/flutter/flutter/issues/56308 }, skip: isBrowser && !isCanvasKit); // https://github.com/flutter/flutter/issues/56308
test('TextPainter null text test', () { test('TextPainter null text test', () {
...@@ -78,6 +80,7 @@ void main() { ...@@ -78,6 +80,7 @@ void main() {
expect(caretOffset.dx, 0); expect(caretOffset.dx, 0);
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 1), ui.Rect.zero); caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 1), ui.Rect.zero);
expect(caretOffset.dx, 0); expect(caretOffset.dx, 0);
painter.dispose();
}); });
test('TextPainter caret emoji test', () { test('TextPainter caret emoji test', () {
...@@ -144,6 +147,7 @@ void main() { ...@@ -144,6 +147,7 @@ void main() {
expect(caretOffset.dx, 98); // <medium skin tone modifier> expect(caretOffset.dx, 98); // <medium skin tone modifier>
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 23), ui.Rect.zero); caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 23), ui.Rect.zero);
expect(caretOffset.dx, 126); // end of string expect(caretOffset.dx, 126); // end of string
painter.dispose();
}, skip: isBrowser && !isCanvasKit); // https://github.com/flutter/flutter/issues/56308 }, skip: isBrowser && !isCanvasKit); // https://github.com/flutter/flutter/issues/56308
test('TextPainter caret center space test', () { test('TextPainter caret center space test', () {
...@@ -166,6 +170,7 @@ void main() { ...@@ -166,6 +170,7 @@ void main() {
expect(caretOffset.dx, 35); expect(caretOffset.dx, 35);
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 2), ui.Rect.zero); caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 2), ui.Rect.zero);
expect(caretOffset.dx, 49); expect(caretOffset.dx, 49);
painter.dispose();
}, skip: isBrowser && !isCanvasKit); // https://github.com/flutter/flutter/issues/56308 }, skip: isBrowser && !isCanvasKit); // https://github.com/flutter/flutter/issues/56308
test('TextPainter error test', () { test('TextPainter error test', () {
...@@ -180,6 +185,7 @@ void main() { ...@@ -180,6 +185,7 @@ void main() {
e.toString(), e.toString(),
contains('TextPainter.paint called when text geometry was not yet calculated'), contains('TextPainter.paint called when text geometry was not yet calculated'),
); );
painter.dispose();
}); });
test('TextPainter requires textDirection', () { test('TextPainter requires textDirection', () {
...@@ -203,6 +209,7 @@ void main() { ...@@ -203,6 +209,7 @@ void main() {
); );
painter.layout(); painter.layout();
expect(painter.size, const Size(123.0, 123.0)); expect(painter.size, const Size(123.0, 123.0));
painter.dispose();
}); });
test('TextPainter textScaleFactor test', () { test('TextPainter textScaleFactor test', () {
...@@ -220,6 +227,7 @@ void main() { ...@@ -220,6 +227,7 @@ void main() {
); );
painter.layout(); painter.layout();
expect(painter.size, const Size(20.0, 20.0)); expect(painter.size, const Size(20.0, 20.0));
painter.dispose();
}); });
test('TextPainter textScaleFactor null style test', () { test('TextPainter textScaleFactor null style test', () {
...@@ -232,6 +240,7 @@ void main() { ...@@ -232,6 +240,7 @@ void main() {
); );
painter.layout(); painter.layout();
expect(painter.size, const Size(28.0, 28.0)); expect(painter.size, const Size(28.0, 28.0));
painter.dispose();
}); });
test('TextPainter default text height is 14 pixels', () { test('TextPainter default text height is 14 pixels', () {
...@@ -242,6 +251,7 @@ void main() { ...@@ -242,6 +251,7 @@ void main() {
painter.layout(); painter.layout();
expect(painter.preferredLineHeight, 14.0); expect(painter.preferredLineHeight, 14.0);
expect(painter.size, const Size(14.0, 14.0)); expect(painter.size, const Size(14.0, 14.0));
painter.dispose();
}); });
test('TextPainter sets paragraph size from root', () { test('TextPainter sets paragraph size from root', () {
...@@ -252,6 +262,7 @@ void main() { ...@@ -252,6 +262,7 @@ void main() {
painter.layout(); painter.layout();
expect(painter.preferredLineHeight, 100.0); expect(painter.preferredLineHeight, 100.0);
expect(painter.size, const Size(100.0, 100.0)); expect(painter.size, const Size(100.0, 100.0));
painter.dispose();
}); });
test('TextPainter intrinsic dimensions', () { test('TextPainter intrinsic dimensions', () {
...@@ -273,6 +284,7 @@ void main() { ...@@ -273,6 +284,7 @@ void main() {
expect(painter.size, const Size(50.0, 10.0)); expect(painter.size, const Size(50.0, 10.0));
expect(painter.minIntrinsicWidth, 10.0); expect(painter.minIntrinsicWidth, 10.0);
expect(painter.maxIntrinsicWidth, 50.0); expect(painter.maxIntrinsicWidth, 50.0);
painter.dispose();
painter = TextPainter( painter = TextPainter(
text: const TextSpan( text: const TextSpan(
...@@ -286,6 +298,7 @@ void main() { ...@@ -286,6 +298,7 @@ void main() {
expect(painter.size, const Size(50.0, 10.0)); expect(painter.size, const Size(50.0, 10.0));
expect(painter.minIntrinsicWidth, 50.0); expect(painter.minIntrinsicWidth, 50.0);
expect(painter.maxIntrinsicWidth, 50.0); expect(painter.maxIntrinsicWidth, 50.0);
painter.dispose();
painter = TextPainter( painter = TextPainter(
text: const TextSpan( text: const TextSpan(
...@@ -299,6 +312,7 @@ void main() { ...@@ -299,6 +312,7 @@ void main() {
expect(painter.size, const Size(80.0, 10.0)); expect(painter.size, const Size(80.0, 10.0));
expect(painter.minIntrinsicWidth, 40.0); expect(painter.minIntrinsicWidth, 40.0);
expect(painter.maxIntrinsicWidth, 80.0); expect(painter.maxIntrinsicWidth, 80.0);
painter.dispose();
painter = TextPainter( painter = TextPainter(
text: const TextSpan( text: const TextSpan(
...@@ -312,6 +326,7 @@ void main() { ...@@ -312,6 +326,7 @@ void main() {
expect(painter.size, const Size(110.0, 10.0)); expect(painter.size, const Size(110.0, 10.0));
expect(painter.minIntrinsicWidth, 70.0); expect(painter.minIntrinsicWidth, 70.0);
expect(painter.maxIntrinsicWidth, 110.0); expect(painter.maxIntrinsicWidth, 110.0);
painter.dispose();
painter = TextPainter( painter = TextPainter(
text: const TextSpan( text: const TextSpan(
...@@ -325,6 +340,7 @@ void main() { ...@@ -325,6 +340,7 @@ void main() {
expect(painter.size, const Size(180.0, 10.0)); expect(painter.size, const Size(180.0, 10.0));
expect(painter.minIntrinsicWidth, 90.0); expect(painter.minIntrinsicWidth, 90.0);
expect(painter.maxIntrinsicWidth, 180.0); expect(painter.maxIntrinsicWidth, 180.0);
painter.dispose();
painter = TextPainter( painter = TextPainter(
text: const TextSpan( text: const TextSpan(
...@@ -338,6 +354,7 @@ void main() { ...@@ -338,6 +354,7 @@ void main() {
expect(painter.size, const Size(180.0, 10.0)); expect(painter.size, const Size(180.0, 10.0));
expect(painter.minIntrinsicWidth, 90.0); expect(painter.minIntrinsicWidth, 90.0);
expect(painter.maxIntrinsicWidth, 180.0); expect(painter.maxIntrinsicWidth, 180.0);
painter.dispose();
}, skip: true); // https://github.com/flutter/flutter/issues/13512 }, skip: true); // https://github.com/flutter/flutter/issues/13512
test('TextPainter handles newlines properly', () { test('TextPainter handles newlines properly', () {
...@@ -679,6 +696,7 @@ void main() { ...@@ -679,6 +696,7 @@ void main() {
); );
expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001)); expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001)); expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
painter.dispose();
}); });
test('TextPainter widget span', () { test('TextPainter widget span', () {
...@@ -773,6 +791,7 @@ void main() { ...@@ -773,6 +791,7 @@ void main() {
expect(painter.inlinePlaceholderBoxes![11], const TextBox.fromLTRBD(250, 30, 300, 60, TextDirection.ltr)); expect(painter.inlinePlaceholderBoxes![11], const TextBox.fromLTRBD(250, 30, 300, 60, TextDirection.ltr));
expect(painter.inlinePlaceholderBoxes![12], const TextBox.fromLTRBD(300, 30, 351, 60, TextDirection.ltr)); expect(painter.inlinePlaceholderBoxes![12], const TextBox.fromLTRBD(300, 30, 351, 60, TextDirection.ltr));
expect(painter.inlinePlaceholderBoxes![13], const TextBox.fromLTRBD(351, 30, 401, 60, TextDirection.ltr)); expect(painter.inlinePlaceholderBoxes![13], const TextBox.fromLTRBD(351, 30, 401, 60, TextDirection.ltr));
painter.dispose();
}, skip: isBrowser && !isCanvasKit); // https://github.com/flutter/flutter/issues/87540 }, skip: isBrowser && !isCanvasKit); // https://github.com/flutter/flutter/issues/87540
// Null values are valid. See https://github.com/flutter/flutter/pull/48346#issuecomment-584839221 // Null values are valid. See https://github.com/flutter/flutter/pull/48346#issuecomment-584839221
...@@ -782,6 +801,7 @@ void main() { ...@@ -782,6 +801,7 @@ void main() {
painter.textHeightBehavior = const TextHeightBehavior(); painter.textHeightBehavior = const TextHeightBehavior();
painter.textHeightBehavior = null; painter.textHeightBehavior = null;
painter.dispose();
}); });
test('TextPainter line metrics', () { test('TextPainter line metrics', () {
...@@ -846,6 +866,7 @@ void main() { ...@@ -846,6 +866,7 @@ void main() {
expect(lines[1].lineNumber, 1); expect(lines[1].lineNumber, 1);
expect(lines[2].lineNumber, 2); expect(lines[2].lineNumber, 2);
expect(lines[3].lineNumber, 3); expect(lines[3].lineNumber, 3);
painter.dispose();
}, skip: true); // https://github.com/flutter/flutter/issues/62819 }, skip: true); // https://github.com/flutter/flutter/issues/62819
test('TextPainter caret height and line height', () { test('TextPainter caret height and line height', () {
...@@ -862,6 +883,7 @@ void main() { ...@@ -862,6 +883,7 @@ void main() {
ui.Rect.zero, ui.Rect.zero,
)!; )!;
expect(caretHeight, 50.0); expect(caretHeight, 50.0);
painter.dispose();
}, skip: isBrowser && !isCanvasKit); // https://github.com/flutter/flutter/issues/56308 }, skip: isBrowser && !isCanvasKit); // https://github.com/flutter/flutter/issues/56308
group('TextPainter line-height', () { group('TextPainter line-height', () {
...@@ -886,6 +908,7 @@ void main() { ...@@ -886,6 +908,7 @@ void main() {
expect(insets.top, insets.bottom); expect(insets.top, insets.bottom);
// The glyph box is exactly 1 logical pixel high. // The glyph box is exactly 1 logical pixel high.
expect(insets.top, (20 - 1) / 2); expect(insets.top, (20 - 1) / 2);
painter.dispose();
}); });
test('half-leading with small height', () { test('half-leading with small height', () {
...@@ -910,6 +933,7 @@ void main() { ...@@ -910,6 +933,7 @@ void main() {
// The glyph box is exactly 10 logical pixel high (the height multiplier // The glyph box is exactly 10 logical pixel high (the height multiplier
// does not scale the glyph). Negative leading. // does not scale the glyph). Negative leading.
expect(insets.top, (1 - 10) / 2); expect(insets.top, (1 - 10) / 2);
painter.dispose();
}); });
test('half-leading with leading trim', () { test('half-leading with leading trim', () {
...@@ -935,6 +959,7 @@ void main() { ...@@ -935,6 +959,7 @@ void main() {
expect(painter.size, glyphBox.size); expect(painter.size, glyphBox.size);
// The glyph box is still centered. // The glyph box is still centered.
expect(glyphBox.topLeft, Offset.zero); expect(glyphBox.topLeft, Offset.zero);
painter.dispose();
}); });
test('TextLeadingDistribution falls back to paragraph style', () { test('TextLeadingDistribution falls back to paragraph style', () {
...@@ -955,6 +980,7 @@ void main() { ...@@ -955,6 +980,7 @@ void main() {
final RelativeRect insets = RelativeRect.fromSize(glyphBox, painter.size); final RelativeRect insets = RelativeRect.fromSize(glyphBox, painter.size);
expect(insets.top, insets.bottom); expect(insets.top, insets.bottom);
expect(insets.top, (20 - 1) / 2); expect(insets.top, (20 - 1) / 2);
painter.dispose();
}); });
test('TextLeadingDistribution does nothing if height multiplier is null', () { test('TextLeadingDistribution does nothing if height multiplier is null', () {
...@@ -978,6 +1004,7 @@ void main() { ...@@ -978,6 +1004,7 @@ void main() {
const TextSelection(baseOffset: 0, extentOffset: 1), const TextSelection(baseOffset: 0, extentOffset: 1),
).first.toRect(); ).first.toRect();
expect(glyphBox, newGlyphBox); expect(glyphBox, newGlyphBox);
painter.dispose();
}); });
}, skip: isBrowser && !isCanvasKit); // https://github.com/flutter/flutter/issues/87543 }, skip: isBrowser && !isCanvasKit); // https://github.com/flutter/flutter/issues/87543
...@@ -997,6 +1024,7 @@ void main() { ...@@ -997,6 +1024,7 @@ void main() {
// The layout should include one replacement character. // The layout should include one replacement character.
expect(painter.width, equals(fontSize)); expect(painter.width, equals(fontSize));
expect(exception, isNotNull); expect(exception, isNotNull);
painter.dispose();
}, skip: kIsWeb); // https://github.com/flutter/flutter/issues/87544 }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/87544
test('Diacritic', () { test('Diacritic', () {
...@@ -1013,6 +1041,7 @@ void main() { ...@@ -1013,6 +1041,7 @@ void main() {
offset: text.length, affinity: TextAffinity.upstream), offset: text.length, affinity: TextAffinity.upstream),
ui.Rect.zero); ui.Rect.zero);
expect(caretOffset.dx, painter.width); expect(caretOffset.dx, painter.width);
painter.dispose();
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/87545 }, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/87545
test('TextPainter line metrics update after layout', () { test('TextPainter line metrics update after layout', () {
...@@ -1033,6 +1062,7 @@ void main() { ...@@ -1033,6 +1062,7 @@ void main() {
lines = painter.computeLineMetrics(); lines = painter.computeLineMetrics();
expect(lines.length, 1); expect(lines.length, 1);
painter.dispose();
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/62819 }, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/62819
test('TextPainter throws with stack trace when accessing text layout', () { test('TextPainter throws with stack trace when accessing text layout', () {
...@@ -1068,6 +1098,7 @@ void main() { ...@@ -1068,6 +1098,7 @@ void main() {
expect(exception?.message, contains('The calls that first invalidated the text layout were:')); expect(exception?.message, contains('The calls that first invalidated the text layout were:'));
exception = null; exception = null;
painter.dispose();
}); });
test('TextPainter requires layout after providing different placeholder dimensions', () { test('TextPainter requires layout after providing different placeholder dimensions', () {
...@@ -1105,6 +1136,7 @@ void main() { ...@@ -1105,6 +1136,7 @@ void main() {
e.toString(), e.toString(),
contains('TextPainter.paint called when text geometry was not yet calculated'), contains('TextPainter.paint called when text geometry was not yet calculated'),
); );
painter.dispose();
}, skip: isBrowser && !isCanvasKit); // https://github.com/flutter/flutter/issues/56308 }, skip: isBrowser && !isCanvasKit); // https://github.com/flutter/flutter/issues/56308
test('TextPainter does not require layout after providing identical placeholder dimensions', () { test('TextPainter does not require layout after providing identical placeholder dimensions', () {
...@@ -1143,7 +1175,15 @@ void main() { ...@@ -1143,7 +1175,15 @@ void main() {
e.toString(), e.toString(),
isNot(contains('TextPainter.paint called when text geometry was not yet calculated')), isNot(contains('TextPainter.paint called when text geometry was not yet calculated')),
); );
painter.dispose();
}, skip: isBrowser && !isCanvasKit); // https://github.com/flutter/flutter/issues/56308 }, skip: isBrowser && !isCanvasKit); // https://github.com/flutter/flutter/issues/56308
test('TextPainter - debugDisposed', () {
final TextPainter painter = TextPainter();
expect(painter.debugDisposed, false);
painter.dispose();
expect(painter.debugDisposed, true);
});
} }
class MockCanvas extends Fake implements Canvas { class MockCanvas extends Fake implements Canvas {
......
...@@ -1070,6 +1070,7 @@ void main() { ...@@ -1070,6 +1070,7 @@ void main() {
final ui.Paragraph paragraph = builder.build(); final ui.Paragraph paragraph = builder.build();
paragraph.layout(const ui.ParagraphConstraints(width: 1000)); paragraph.layout(const ui.ParagraphConstraints(width: 1000));
expect(paragraph.getBoxesForRange(2, 2), isEmpty); expect(paragraph.getBoxesForRange(2, 2), isEmpty);
paragraph.dispose();
}); });
// Regression test for https://github.com/flutter/flutter/issues/65818 // Regression test for https://github.com/flutter/flutter/issues/65818
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment