diff --git a/packages/flutter/lib/painting.dart b/packages/flutter/lib/painting.dart index d3d11ce148..17880920dc 100644 --- a/packages/flutter/lib/painting.dart +++ b/packages/flutter/lib/painting.dart @@ -17,7 +17,7 @@ /// painting boxes. library painting; -export 'dart:ui' show Shadow, PlaceholderAlignment; +export 'dart:ui' show Shadow, PlaceholderAlignment, TextHeightBehavior; export 'src/painting/alignment.dart'; export 'src/painting/basic_types.dart'; diff --git a/packages/flutter/lib/src/painting/text_painter.dart b/packages/flutter/lib/src/painting/text_painter.dart index 35654e85a4..b5d9468520 100644 --- a/packages/flutter/lib/src/painting/text_painter.dart +++ b/packages/flutter/lib/src/painting/text_painter.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. import 'dart:math' show min, max; -import 'dart:ui' as ui show Paragraph, ParagraphBuilder, ParagraphConstraints, ParagraphStyle, PlaceholderAlignment, LineMetrics; +import 'dart:ui' as ui show Paragraph, ParagraphBuilder, ParagraphConstraints, ParagraphStyle, PlaceholderAlignment, LineMetrics, TextHeightBehavior; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; @@ -142,6 +142,7 @@ class TextPainter { Locale locale, StrutStyle strutStyle, TextWidthBasis textWidthBasis = TextWidthBasis.parent, + ui.TextHeightBehavior textHeightBehavior, }) : assert(text == null || text.debugAssertIsValid()), assert(textAlign != null), assert(textScaleFactor != null), @@ -155,7 +156,8 @@ class TextPainter { _ellipsis = ellipsis, _locale = locale, _strutStyle = strutStyle, - _textWidthBasis = textWidthBasis; + _textWidthBasis = textWidthBasis, + _textHeightBehavior = textHeightBehavior; ui.Paragraph _paragraph; bool _needsLayout = true; @@ -340,6 +342,16 @@ class TextPainter { markNeedsLayout(); } + /// {@macro flutter.dart:ui.textHeightBehavior} + ui.TextHeightBehavior get textHeightBehavior => _textHeightBehavior; + ui.TextHeightBehavior _textHeightBehavior; + set textHeightBehavior(ui.TextHeightBehavior value) { + assert(value != null); + if (_textHeightBehavior == value) + return; + _textHeightBehavior = value; + markNeedsLayout(); + } ui.Paragraph _layoutTemplate; @@ -399,6 +411,7 @@ class TextPainter { textDirection: textDirection ?? defaultTextDirection, textScaleFactor: textScaleFactor, maxLines: _maxLines, + textHeightBehavior: _textHeightBehavior, ellipsis: _ellipsis, locale: _locale, strutStyle: _strutStyle, @@ -406,6 +419,7 @@ class TextPainter { textAlign: textAlign, textDirection: textDirection ?? defaultTextDirection, maxLines: maxLines, + textHeightBehavior: _textHeightBehavior, ellipsis: ellipsis, locale: locale, ); diff --git a/packages/flutter/lib/src/painting/text_style.dart b/packages/flutter/lib/src/painting/text_style.dart index be50003f0c..e0bccb6f49 100644 --- a/packages/flutter/lib/src/painting/text_style.dart +++ b/packages/flutter/lib/src/painting/text_style.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' as ui show ParagraphStyle, TextStyle, StrutStyle, lerpDouble, Shadow, FontFeature; +import 'dart:ui' as ui show ParagraphStyle, TextStyle, StrutStyle, lerpDouble, Shadow, FontFeature, TextHeightBehavior; import 'package:flutter/foundation.dart'; @@ -1067,6 +1067,7 @@ class TextStyle extends Diagnosticable { double textScaleFactor = 1.0, String ellipsis, int maxLines, + ui.TextHeightBehavior textHeightBehavior, Locale locale, String fontFamily, double fontSize, @@ -1087,6 +1088,7 @@ class TextStyle extends Diagnosticable { fontFamily: fontFamily ?? this.fontFamily, fontSize: (fontSize ?? this.fontSize ?? _defaultFontSize) * textScaleFactor, height: height ?? this.height, + textHeightBehavior: textHeightBehavior, strutStyle: strutStyle == null ? null : ui.StrutStyle( fontFamily: strutStyle.fontFamily, fontFamilyFallback: strutStyle.fontFamilyFallback, diff --git a/packages/flutter/lib/src/rendering/paragraph.dart b/packages/flutter/lib/src/rendering/paragraph.dart index 5e9d800a99..556f89face 100644 --- a/packages/flutter/lib/src/rendering/paragraph.dart +++ b/packages/flutter/lib/src/rendering/paragraph.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. import 'dart:math' as math; -import 'dart:ui' as ui show Gradient, Shader, TextBox, PlaceholderAlignment; +import 'dart:ui' as ui show Gradient, Shader, TextBox, PlaceholderAlignment, TextHeightBehavior; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; @@ -72,9 +72,10 @@ class RenderParagraph extends RenderBox TextOverflow overflow = TextOverflow.clip, double textScaleFactor = 1.0, int maxLines, - TextWidthBasis textWidthBasis = TextWidthBasis.parent, Locale locale, StrutStyle strutStyle, + TextWidthBasis textWidthBasis = TextWidthBasis.parent, + ui.TextHeightBehavior textHeightBehavior, List children, }) : assert(text != null), assert(text.debugAssertIsValid()), @@ -97,6 +98,7 @@ class RenderParagraph extends RenderBox locale: locale, strutStyle: strutStyle, textWidthBasis: textWidthBasis, + textHeightBehavior: textHeightBehavior ) { addAll(children); _extractPlaceholderSpans(text); @@ -275,6 +277,16 @@ class RenderParagraph extends RenderBox markNeedsLayout(); } + /// {@macro flutter.dart:ui.textHeightBehavior} + ui.TextHeightBehavior get textHeightBehavior => _textPainter.textHeightBehavior; + set textHeightBehavior(ui.TextHeightBehavior value) { + if (_textPainter.textHeightBehavior == value) + return; + _textPainter.textHeightBehavior = value; + _overflowShader = null; + markNeedsLayout(); + } + @override double computeMinIntrinsicWidth(double height) { if (!_canComputeIntrinsics()) { diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart index 8c14b69933..8b380f3bf4 100644 --- a/packages/flutter/lib/src/widgets/basic.dart +++ b/packages/flutter/lib/src/widgets/basic.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' as ui show Image, ImageFilter; +import 'dart:ui' as ui show Image, ImageFilter, TextHeightBehavior; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; @@ -5063,6 +5063,7 @@ class RichText extends MultiChildRenderObjectWidget { this.locale, this.strutStyle, this.textWidthBasis = TextWidthBasis.parent, + this.textHeightBehavior, }) : assert(text != null), assert(textAlign != null), assert(softWrap != null), @@ -5144,6 +5145,9 @@ class RichText extends MultiChildRenderObjectWidget { /// {@macro flutter.widgets.text.DefaultTextStyle.textWidthBasis} final TextWidthBasis textWidthBasis; + /// {@macro flutter.dart:ui.textHeightBehavior} + final ui.TextHeightBehavior textHeightBehavior; + @override RenderParagraph createRenderObject(BuildContext context) { assert(textDirection != null || debugCheckHasDirectionality(context)); @@ -5156,6 +5160,7 @@ class RichText extends MultiChildRenderObjectWidget { maxLines: maxLines, strutStyle: strutStyle, textWidthBasis: textWidthBasis, + textHeightBehavior: textHeightBehavior, locale: locale ?? Localizations.localeOf(context, nullOk: true), ); } @@ -5173,6 +5178,7 @@ class RichText extends MultiChildRenderObjectWidget { ..maxLines = maxLines ..strutStyle = strutStyle ..textWidthBasis = textWidthBasis + ..textHeightBehavior = textHeightBehavior ..locale = locale ?? Localizations.localeOf(context, nullOk: true); } diff --git a/packages/flutter/lib/src/widgets/text.dart b/packages/flutter/lib/src/widgets/text.dart index ca2c3cec86..70cea578cb 100644 --- a/packages/flutter/lib/src/widgets/text.dart +++ b/packages/flutter/lib/src/widgets/text.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:ui' as ui show TextHeightBehavior; + import 'package:flutter/foundation.dart'; import 'package:flutter/painting.dart'; @@ -43,6 +45,7 @@ class DefaultTextStyle extends InheritedTheme { this.overflow = TextOverflow.clip, this.maxLines, this.textWidthBasis = TextWidthBasis.parent, + this.textHeightBehavior, @required Widget child, }) : assert(style != null), assert(softWrap != null), @@ -65,6 +68,7 @@ class DefaultTextStyle extends InheritedTheme { maxLines = null, overflow = TextOverflow.clip, textWidthBasis = TextWidthBasis.parent, + textHeightBehavior = null, super(key: key, child: null); /// Creates a default text style that overrides the text styles in scope at @@ -141,6 +145,9 @@ class DefaultTextStyle extends InheritedTheme { /// See [TextWidthBasis] for possible values and their implications. final TextWidthBasis textWidthBasis; + /// {@macro flutter.dart:ui.textHeightBehavior} + final ui.TextHeightBehavior textHeightBehavior; + /// The closest instance of this class that encloses the given context. /// /// If no such instance exists, returns an instance created by @@ -162,7 +169,8 @@ class DefaultTextStyle extends InheritedTheme { softWrap != oldWidget.softWrap || overflow != oldWidget.overflow || maxLines != oldWidget.maxLines || - textWidthBasis != oldWidget.textWidthBasis; + textWidthBasis != oldWidget.textWidthBasis || + textHeightBehavior != oldWidget.textHeightBehavior; } @override @@ -175,6 +183,7 @@ class DefaultTextStyle extends InheritedTheme { overflow: overflow, maxLines: maxLines, textWidthBasis: textWidthBasis, + textHeightBehavior: textHeightBehavior, child: child, ); } @@ -188,6 +197,7 @@ class DefaultTextStyle extends InheritedTheme { properties.add(EnumProperty('overflow', overflow, defaultValue: null)); properties.add(IntProperty('maxLines', maxLines, defaultValue: null)); properties.add(EnumProperty('textWidthBasis', textWidthBasis, defaultValue: TextWidthBasis.parent)); + properties.add(DiagnosticsProperty('textHeightBehavior', textHeightBehavior, defaultValue: null)); } } @@ -283,6 +293,7 @@ class Text extends StatelessWidget { this.maxLines, this.semanticsLabel, this.textWidthBasis, + this.textHeightBehavior, }) : assert( data != null, 'A non-null String must be provided to a Text widget.', @@ -314,6 +325,7 @@ class Text extends StatelessWidget { this.maxLines, this.semanticsLabel, this.textWidthBasis, + this.textHeightBehavior, }) : assert( textSpan != null, 'A non-null TextSpan must be provided to a Text.rich widget.', @@ -416,6 +428,9 @@ class Text extends StatelessWidget { /// {@macro flutter.painting.textPainter.textWidthBasis} final TextWidthBasis textWidthBasis; + /// {@macro flutter.dart:ui.textHeightBehavior} + final ui.TextHeightBehavior textHeightBehavior; + @override Widget build(BuildContext context) { final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context); @@ -434,6 +449,7 @@ class Text extends StatelessWidget { maxLines: maxLines ?? defaultTextStyle.maxLines, strutStyle: strutStyle, textWidthBasis: textWidthBasis ?? defaultTextStyle.textWidthBasis, + textHeightBehavior: textHeightBehavior ?? defaultTextStyle.textHeightBehavior, text: TextSpan( style: effectiveTextStyle, text: data, @@ -467,6 +483,8 @@ class Text extends StatelessWidget { properties.add(EnumProperty('overflow', overflow, defaultValue: null)); properties.add(DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: null)); properties.add(IntProperty('maxLines', maxLines, defaultValue: null)); + properties.add(EnumProperty('textWidthBasis', textWidthBasis, defaultValue: null)); + properties.add(DiagnosticsProperty('textHeightBehavior', textHeightBehavior, defaultValue: null)); if (semanticsLabel != null) { properties.add(StringProperty('semanticsLabel', semanticsLabel)); } diff --git a/packages/flutter/test/material/theme_test.dart b/packages/flutter/test/material/theme_test.dart index b4e2302bcf..d8e89af442 100644 --- a/packages/flutter/test/material/theme_test.dart +++ b/packages/flutter/test/material/theme_test.dart @@ -829,6 +829,7 @@ class _TextStyleProxy implements TextStyle { double textScaleFactor = 1.0, String ellipsis, int maxLines, + ui.TextHeightBehavior textHeightBehavior, Locale locale, String fontFamily, double fontSize, diff --git a/packages/flutter/test/widgets/text_golden_test.dart b/packages/flutter/test/widgets/text_golden_test.dart index e2f89e5a14..2247518d5e 100644 --- a/packages/flutter/test/widgets/text_golden_test.dart +++ b/packages/flutter/test/widgets/text_golden_test.dart @@ -1316,4 +1316,47 @@ void main() { matchesGoldenFile('text_golden.TextInlineWidgetMiddle.1.png'), ); }); + + testWidgets('Text TextHeightBehavior', (WidgetTester tester) async { + await tester.pumpWidget( + Center( + child: RepaintBoundary( + child: Container( + width: 200.0, + height: 700.0, + decoration: const BoxDecoration( + color: Color(0xff00ff00), + ), + child: Column( + children: const [ + Text('Hello\nLine 2\nLine 3', + textDirection: TextDirection.ltr, + style: TextStyle(height: 5,), + ), + Text('Hello\nLine 2\nLine 3', + textDirection: TextDirection.ltr, + style: TextStyle(height: 5,), + textHeightBehavior: TextHeightBehavior( + applyHeightToFirstAscent: false, + applyHeightToLastDescent: false, + ), + ), + Text('Hello', + textDirection: TextDirection.ltr, + style: TextStyle(height: 5,), + textHeightBehavior: TextHeightBehavior( + applyHeightToFirstAscent: false, + ), + ), + ], + ), + ), + ), + ), + ); + await expectLater( + find.byType(Container), + matchesGoldenFile('text_golden.TextHeightBehavior.1.png'), + ); + }); }