TextPainter should dispatch creation and disposal events. (#137416)
This commit is contained in:
parent
14f95576e5
commit
fea561301b
@ -664,6 +664,7 @@ class _SnackBarState extends State<SnackBar> {
|
|||||||
final double actionAndIconWidth = actionTextPainter.size.width +
|
final double actionAndIconWidth = actionTextPainter.size.width +
|
||||||
(widget.action != null ? actionHorizontalMargin : 0) +
|
(widget.action != null ? actionHorizontalMargin : 0) +
|
||||||
(showCloseIcon ? (iconButton?.iconSize ?? 0 + iconHorizontalMargin) : 0);
|
(showCloseIcon ? (iconButton?.iconSize ?? 0 + iconHorizontalMargin) : 0);
|
||||||
|
actionTextPainter.dispose();
|
||||||
|
|
||||||
final EdgeInsets margin = widget.margin?.resolve(TextDirection.ltr) ?? snackBarTheme.insetPadding ?? defaults.insetPadding!;
|
final EdgeInsets margin = widget.margin?.resolve(TextDirection.ltr) ?? snackBarTheme.insetPadding ?? defaults.insetPadding!;
|
||||||
|
|
||||||
|
@ -1309,6 +1309,7 @@ class _SwitchPainter extends ToggleablePainter {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final TextPainter _textPainter = TextPainter();
|
||||||
Color? _cachedThumbColor;
|
Color? _cachedThumbColor;
|
||||||
ImageProvider? _cachedThumbImage;
|
ImageProvider? _cachedThumbImage;
|
||||||
ImageErrorListener? _cachedThumbErrorListener;
|
ImageErrorListener? _cachedThumbErrorListener;
|
||||||
@ -1594,16 +1595,15 @@ class _SwitchPainter extends ToggleablePainter {
|
|||||||
shadows: iconShadows,
|
shadows: iconShadows,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final TextPainter textPainter = TextPainter(
|
_textPainter
|
||||||
textDirection: textDirection,
|
..textDirection = textDirection
|
||||||
text: textSpan,
|
..text = textSpan;
|
||||||
);
|
_textPainter.layout();
|
||||||
textPainter.layout();
|
|
||||||
final double additionalHorizontalOffset = (thumbSize.width - iconSize) / 2;
|
final double additionalHorizontalOffset = (thumbSize.width - iconSize) / 2;
|
||||||
final double additionalVerticalOffset = (thumbSize.height - iconSize) / 2;
|
final double additionalVerticalOffset = (thumbSize.height - iconSize) / 2;
|
||||||
final Offset offset = thumbPaintOffset + Offset(additionalHorizontalOffset, additionalVerticalOffset);
|
final Offset offset = thumbPaintOffset + Offset(additionalHorizontalOffset, additionalVerticalOffset);
|
||||||
|
|
||||||
textPainter.paint(canvas, offset);
|
_textPainter.paint(canvas, offset);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
_isPainting = false;
|
_isPainting = false;
|
||||||
@ -1612,6 +1612,7 @@ class _SwitchPainter extends ToggleablePainter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_textPainter.dispose();
|
||||||
_cachedThumbPainter?.dispose();
|
_cachedThumbPainter?.dispose();
|
||||||
_cachedThumbPainter = null;
|
_cachedThumbPainter = null;
|
||||||
_cachedThumbColor = null;
|
_cachedThumbColor = null;
|
||||||
|
@ -215,10 +215,15 @@ 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;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_textPainter.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void _prepareText() {
|
void _prepareText() {
|
||||||
const String kLabel = 'Flutter';
|
const String kLabel = 'Flutter';
|
||||||
_textPainter = TextPainter(
|
_textPainter = TextPainter(
|
||||||
|
@ -438,6 +438,8 @@ final class _EmptyLineCaretMetrics implements _CaretMetrics {
|
|||||||
final double lineVerticalOffset;
|
final double lineVerticalOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const String _flutterPaintingLibrary = 'package:flutter/painting.dart';
|
||||||
|
|
||||||
/// An object that paints a [TextSpan] tree into a [Canvas].
|
/// An object that paints a [TextSpan] tree into a [Canvas].
|
||||||
///
|
///
|
||||||
/// To use a [TextPainter], follow these steps:
|
/// To use a [TextPainter], follow these steps:
|
||||||
@ -498,7 +500,17 @@ class TextPainter {
|
|||||||
_locale = locale,
|
_locale = locale,
|
||||||
_strutStyle = strutStyle,
|
_strutStyle = strutStyle,
|
||||||
_textWidthBasis = textWidthBasis,
|
_textWidthBasis = textWidthBasis,
|
||||||
_textHeightBehavior = textHeightBehavior;
|
_textHeightBehavior = textHeightBehavior {
|
||||||
|
// TODO(polina-c): stop duplicating code across disposables
|
||||||
|
// https://github.com/flutter/flutter/issues/137435
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: _flutterPaintingLibrary,
|
||||||
|
className: '$TextPainter',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Computes the width of a configured [TextPainter].
|
/// Computes the width of a configured [TextPainter].
|
||||||
///
|
///
|
||||||
@ -1598,6 +1610,11 @@ class TextPainter {
|
|||||||
_disposed = true;
|
_disposed = true;
|
||||||
return true;
|
return true;
|
||||||
}());
|
}());
|
||||||
|
// TODO(polina-c): stop duplicating code across disposables
|
||||||
|
// https://github.com/flutter/flutter/issues/137435
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||||
|
}
|
||||||
_layoutTemplate?.dispose();
|
_layoutTemplate?.dispose();
|
||||||
_layoutTemplate = null;
|
_layoutTemplate = null;
|
||||||
_layoutCache?.paragraph.dispose();
|
_layoutCache?.paragraph.dispose();
|
||||||
|
@ -2263,6 +2263,12 @@ class RenderDecoratedBox extends RenderProxyBox {
|
|||||||
markNeedsPaint();
|
markNeedsPaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_painter?.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool hitTestSelf(Offset position) {
|
bool hitTestSelf(Offset position) {
|
||||||
return _decoration.hitTest(size, position, textDirection: configuration.textDirection);
|
return _decoration.hitTest(size, position, textDirection: configuration.textDirection);
|
||||||
|
@ -231,7 +231,7 @@ class BannerPainter extends CustomPainter {
|
|||||||
///
|
///
|
||||||
/// * [CheckedModeBanner], which the [WidgetsApp] widget includes by default in
|
/// * [CheckedModeBanner], which the [WidgetsApp] widget includes by default in
|
||||||
/// debug mode, to show a banner that says "DEBUG".
|
/// debug mode, to show a banner that says "DEBUG".
|
||||||
class Banner extends StatelessWidget {
|
class Banner extends StatefulWidget {
|
||||||
/// Creates a banner.
|
/// Creates a banner.
|
||||||
const Banner({
|
const Banner({
|
||||||
super.key,
|
super.key,
|
||||||
@ -288,31 +288,48 @@ class Banner extends StatelessWidget {
|
|||||||
/// The style of the text shown on the banner.
|
/// The style of the text shown on the banner.
|
||||||
final TextStyle textStyle;
|
final TextStyle textStyle;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<Banner> createState() => _BannerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BannerState extends State<Banner> {
|
||||||
|
BannerPainter? _painter;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_painter?.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
assert((textDirection != null && layoutDirection != null) || debugCheckHasDirectionality(context));
|
assert((widget.textDirection != null && widget.layoutDirection != null) || debugCheckHasDirectionality(context));
|
||||||
|
|
||||||
|
_painter?.dispose();
|
||||||
|
_painter = BannerPainter(
|
||||||
|
message: widget.message,
|
||||||
|
textDirection: widget.textDirection ?? Directionality.of(context),
|
||||||
|
location: widget.location,
|
||||||
|
layoutDirection: widget.layoutDirection ?? Directionality.of(context),
|
||||||
|
color: widget.color,
|
||||||
|
textStyle: widget.textStyle,
|
||||||
|
);
|
||||||
|
|
||||||
return CustomPaint(
|
return CustomPaint(
|
||||||
foregroundPainter: BannerPainter(
|
foregroundPainter: _painter,
|
||||||
message: message,
|
child: widget.child,
|
||||||
textDirection: textDirection ?? Directionality.of(context),
|
|
||||||
location: location,
|
|
||||||
layoutDirection: layoutDirection ?? Directionality.of(context),
|
|
||||||
color: color,
|
|
||||||
textStyle: textStyle,
|
|
||||||
),
|
|
||||||
child: child,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
super.debugFillProperties(properties);
|
super.debugFillProperties(properties);
|
||||||
properties.add(StringProperty('message', message, showName: false));
|
properties.add(StringProperty('message', widget.message, showName: false));
|
||||||
properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
|
properties.add(EnumProperty<TextDirection>('textDirection', widget.textDirection, defaultValue: null));
|
||||||
properties.add(EnumProperty<BannerLocation>('location', location));
|
properties.add(EnumProperty<BannerLocation>('location', widget.location));
|
||||||
properties.add(EnumProperty<TextDirection>('layoutDirection', layoutDirection, defaultValue: null));
|
properties.add(EnumProperty<TextDirection>('layoutDirection', widget.layoutDirection, defaultValue: null));
|
||||||
properties.add(ColorProperty('color', color, showName: false));
|
properties.add(ColorProperty('color', widget.color, showName: false));
|
||||||
textStyle.debugFillProperties(properties, prefix: 'text ');
|
widget.textStyle.debugFillProperties(properties, prefix: 'text ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import 'dart:ui' as ui;
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
|
||||||
|
|
||||||
void _checkCaretOffsetsLtrAt(String text, List<int> boundaries) {
|
void _checkCaretOffsetsLtrAt(String text, List<int> boundaries) {
|
||||||
expect(boundaries.first, 0);
|
expect(boundaries.first, 0);
|
||||||
@ -1525,6 +1526,13 @@ void main() {
|
|||||||
expect(metrics, hasLength(1));
|
expect(metrics, hasLength(1));
|
||||||
}
|
}
|
||||||
}, skip: kIsWeb && !isCanvasKit); // [intended] Browsers seem to always round font/glyph metrics.
|
}, skip: kIsWeb && !isCanvasKit); // [intended] Browsers seem to always round font/glyph metrics.
|
||||||
|
|
||||||
|
test('TextPainter dispatches memory events', () async {
|
||||||
|
await expectLater(
|
||||||
|
await memoryEvents(() => TextPainter().dispose(), TextPainter),
|
||||||
|
areCreateAndDispose,
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockCanvas extends Fake implements Canvas {
|
class MockCanvas extends Fake implements Canvas {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user