diff --git a/packages/flutter/lib/src/painting/text_painter.dart b/packages/flutter/lib/src/painting/text_painter.dart index c0d9406fb1f..50ca4fd2ffc 100644 --- a/packages/flutter/lib/src/painting/text_painter.dart +++ b/packages/flutter/lib/src/painting/text_painter.dart @@ -24,6 +24,7 @@ import 'placeholder_span.dart'; import 'strut_style.dart'; import 'text_scaler.dart'; import 'text_span.dart'; +import 'text_style.dart'; export 'dart:ui' show LineMetrics; export 'package:flutter/services.dart' show TextRange, TextSelection; @@ -632,6 +633,12 @@ class TextPainter { } } + @Deprecated( // flutter_ignore: deprecation_syntax (see analyze.dart) + 'The disableStrutHalfLeading flag is for internal migration purposes only and should not be used.' + ) + /// Migration only flag, do not use. + static bool disableStrutHalfLeading = true; + // Whether textWidthBasis has changed after the most recent `layout` call. bool _debugNeedsRelayout = true; // The result of the most recent `layout` call. @@ -968,7 +975,27 @@ class TextPainter { // The defaultTextDirection argument is used for preferredLineHeight in case // textDirection hasn't yet been set. assert(textDirection != null || defaultTextDirection != null, 'TextPainter.textDirection must be set to a non-null value before using the TextPainter.'); - return _text!.style?.getParagraphStyle( + final TextStyle baseStyle = _text?.style ?? const TextStyle(); + final StrutStyle? strutStyle = _strutStyle; + + final bool applyMigration = !kIsWeb && TextPainter.disableStrutHalfLeading + && strutStyle != null && (strutStyle.forceStrutHeight ?? false) + && strutStyle.leadingDistribution == TextLeadingDistribution.even; + final StrutStyle? strutStyleForMigration = !applyMigration + ? strutStyle + : StrutStyle( + fontFamily: strutStyle.fontFamily, + fontFamilyFallback: strutStyle.fontFamilyFallback, + fontSize: strutStyle.fontSize, + height: strutStyle.height, + leadingDistribution: TextLeadingDistribution.proportional, + leading: strutStyle.leading, + fontWeight: strutStyle.fontWeight, + fontStyle: strutStyle.fontStyle, + forceStrutHeight: strutStyle.forceStrutHeight, + ); + + return baseStyle.getParagraphStyle( textAlign: textAlign, textDirection: textDirection ?? defaultTextDirection, textScaler: textScaler, @@ -976,18 +1003,7 @@ class TextPainter { textHeightBehavior: _textHeightBehavior, ellipsis: _ellipsis, locale: _locale, - strutStyle: _strutStyle, - ) ?? ui.ParagraphStyle( - textAlign: textAlign, - textDirection: textDirection ?? defaultTextDirection, - // Use the default font size to multiply by as RichText does not - // perform inheriting [TextStyle]s and would otherwise - // fail to apply textScaler. - fontSize: textScaler.scale(kDefaultFontSize), - maxLines: maxLines, - textHeightBehavior: _textHeightBehavior, - ellipsis: ellipsis, - locale: locale, + strutStyle: strutStyleForMigration, ); } diff --git a/packages/flutter/lib/src/painting/text_style.dart b/packages/flutter/lib/src/painting/text_style.dart index 42844d9bb62..ab41a25b8e1 100644 --- a/packages/flutter/lib/src/painting/text_style.dart +++ b/packages/flutter/lib/src/painting/text_style.dart @@ -1388,6 +1388,7 @@ class TextStyle with Diagnosticable { }, height: strutStyle.height, leading: strutStyle.leading, + leadingDistribution: strutStyle.leadingDistribution, fontWeight: strutStyle.fontWeight, fontStyle: strutStyle.fontStyle, forceStrutHeight: strutStyle.forceStrutHeight, diff --git a/packages/flutter/test/painting/text_painter_test.dart b/packages/flutter/test/painting/text_painter_test.dart index 8e6558d1ec9..00cc50cd8d9 100644 --- a/packages/flutter/test/painting/text_painter_test.dart +++ b/packages/flutter/test/painting/text_painter_test.dart @@ -1546,6 +1546,45 @@ void main() { } }, skip: kIsWeb && !isCanvasKit); // [intended] Browsers seem to always round font/glyph metrics. + group('strut style', () { + test('strut style applies when the span has no style', () { + const StrutStyle strut = StrutStyle(height: 10, fontSize: 10); + final TextPainter painter = TextPainter( + textDirection: TextDirection.ltr, + text: const TextSpan(), + strutStyle: strut, + )..layout(); + expect(painter.height, 100); + }); + + test('strut style leading is a fontSize multiplier', () { + const StrutStyle strut = StrutStyle(height: 10, fontSize: 10, leading: 2); + final TextPainter painter = TextPainter( + textDirection: TextDirection.ltr, + text: const TextSpan(), + strutStyle: strut, + )..layout(); + expect(painter.height, 100 + 20); + // Top leading + scaled ascent. + expect(painter.computeDistanceToActualBaseline(TextBaseline.alphabetic), 10 + 10 * 7.5); + }); + + test('force strut height', () { + const StrutStyle strut = StrutStyle(height: 10, fontSize: 10, forceStrutHeight: true); + final TextPainter painter = TextPainter( + textDirection: TextDirection.ltr, + text: const TextSpan(text: 'A', style: TextStyle(fontSize: 20)), + strutStyle: strut, + )..layout(); + expect(painter.height, 100); + const double baseline = 75; + expect( + painter.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: 1)), + const [TextBox.fromLTRBD(0, baseline - 15, 20, baseline + 5, TextDirection.ltr)], + ); + }); + }, skip: kIsWeb && !isCanvasKit); // [intended] strut spport for HTML renderer https://github.com/flutter/flutter/issues/32243. + test('TextPainter dispatches memory events', () async { await expectLater( await memoryEvents(() => TextPainter().dispose(), TextPainter),