Commit 75f39789 authored by Matt Perry's avatar Matt Perry Committed by GitHub

Add support for specifying maxLines for Text. (#7493)

Overflow handling works with clipping, adding an ellipsis on the last
line, or fading the last line.

Fixes https://github.com/flutter/flutter/issues/7271
parent 60847a1d
b3ed79122edd7172327ce415688ef674d6a7fa5d 2efc78cc24eac9439a5315ed9333fa8599aab3a1
...@@ -11,6 +11,8 @@ import 'basic_types.dart'; ...@@ -11,6 +11,8 @@ import 'basic_types.dart';
import 'text_editing.dart'; import 'text_editing.dart';
import 'text_span.dart'; import 'text_span.dart';
final String _kZeroWidthSpace = new String.fromCharCode(0x200B);
/// 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:
...@@ -34,8 +36,9 @@ class TextPainter { ...@@ -34,8 +36,9 @@ class TextPainter {
TextSpan text, TextSpan text,
TextAlign textAlign, TextAlign textAlign,
double textScaleFactor: 1.0, double textScaleFactor: 1.0,
int maxLines,
String ellipsis, String ellipsis,
}) : _text = text, _textAlign = textAlign, _textScaleFactor = textScaleFactor, _ellipsis = ellipsis { }) : _text = text, _textAlign = textAlign, _textScaleFactor = textScaleFactor, _maxLines = maxLines, _ellipsis = ellipsis {
assert(text == null || text.debugAssertIsValid()); assert(text == null || text.debugAssertIsValid());
assert(textScaleFactor != null); assert(textScaleFactor != null);
} }
...@@ -50,6 +53,8 @@ class TextPainter { ...@@ -50,6 +53,8 @@ class TextPainter {
assert(value == null || value.debugAssertIsValid()); assert(value == null || value.debugAssertIsValid());
if (_text == value) if (_text == value)
return; return;
if (_text?.style != value?.style)
_layoutTemplate = null;
_text = value; _text = value;
_paragraph = null; _paragraph = null;
_needsLayout = true; _needsLayout = true;
...@@ -95,6 +100,32 @@ class TextPainter { ...@@ -95,6 +100,32 @@ class TextPainter {
_needsLayout = true; _needsLayout = true;
} }
/// An optional maximum number of lines for the text to span, wrapping if necessary.
/// If the text exceeds the given number of lines, it will be truncated according
/// to [overflow].
int get maxLines => _maxLines;
int _maxLines;
set maxLines(int value) {
if (_maxLines == value)
return;
_maxLines = value;
_paragraph = null;
_needsLayout = true;
}
ui.Paragraph _layoutTemplate;
double get preferredLineHeight {
assert(text != null);
if (_layoutTemplate == null) {
ui.ParagraphBuilder builder = new ui.ParagraphBuilder(new ui.ParagraphStyle());
if (text.style != null)
builder.pushStyle(text.style.getTextStyle(textScaleFactor: textScaleFactor));
builder.addText(_kZeroWidthSpace);
_layoutTemplate = builder.build()
..layout(new ui.ParagraphConstraints(width: double.INFINITY));
}
return _layoutTemplate.height;
}
// Unfortunately, using full precision floating point here causes bad layouts // Unfortunately, using full precision floating point here causes bad layouts
// because floating point math isn't associative. If we add and subtract // because floating point math isn't associative. If we add and subtract
...@@ -162,6 +193,11 @@ class TextPainter { ...@@ -162,6 +193,11 @@ class TextPainter {
return null; return null;
} }
bool get didExceedMaxLines {
assert(!_needsLayout);
return _paragraph.didExceedMaxLines;
}
double _lastMinWidth; double _lastMinWidth;
double _lastMaxWidth; double _lastMaxWidth;
...@@ -179,9 +215,14 @@ class TextPainter { ...@@ -179,9 +215,14 @@ class TextPainter {
ui.ParagraphStyle paragraphStyle = _text.style?.getParagraphStyle( ui.ParagraphStyle paragraphStyle = _text.style?.getParagraphStyle(
textAlign: textAlign, textAlign: textAlign,
textScaleFactor: textScaleFactor, textScaleFactor: textScaleFactor,
maxLines: _maxLines,
ellipsis: _ellipsis, ellipsis: _ellipsis,
); );
paragraphStyle ??= new ui.ParagraphStyle(); paragraphStyle ??= new ui.ParagraphStyle(
textAlign: textAlign,
maxLines: maxLines,
ellipsis: ellipsis,
);
ui.ParagraphBuilder builder = new ui.ParagraphBuilder(paragraphStyle); ui.ParagraphBuilder builder = new ui.ParagraphBuilder(paragraphStyle);
_text.build(builder, textScaleFactor: textScaleFactor); _text.build(builder, textScaleFactor: textScaleFactor);
_paragraph = builder.build(); _paragraph = builder.build();
......
...@@ -233,6 +233,7 @@ class TextStyle { ...@@ -233,6 +233,7 @@ class TextStyle {
TextAlign textAlign, TextAlign textAlign,
double textScaleFactor: 1.0, double textScaleFactor: 1.0,
String ellipsis, String ellipsis,
int maxLines,
}) { }) {
return new ui.ParagraphStyle( return new ui.ParagraphStyle(
textAlign: textAlign, textAlign: textAlign,
...@@ -241,6 +242,7 @@ class TextStyle { ...@@ -241,6 +242,7 @@ class TextStyle {
fontFamily: fontFamily, fontFamily: fontFamily,
fontSize: fontSize == null ? null : fontSize * textScaleFactor, fontSize: fontSize == null ? null : fontSize * textScaleFactor,
lineHeight: height, lineHeight: height,
maxLines: maxLines,
ellipsis: ellipsis, ellipsis: ellipsis,
); );
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' as ui show Paragraph, ParagraphBuilder, ParagraphConstraints, ParagraphStyle, TextBox; import 'dart:ui' as ui show TextBox;
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
...@@ -88,9 +88,6 @@ class RenderEditable extends RenderBox { ...@@ -88,9 +88,6 @@ class RenderEditable extends RenderBox {
set text(TextSpan value) { set text(TextSpan value) {
if (_textPainter.text == value) if (_textPainter.text == value)
return; return;
TextSpan oldStyledText = _textPainter.text;
if (oldStyledText.style != value.style)
_layoutTemplate = null;
_textPainter.text = value; _textPainter.text = value;
markNeedsLayout(); markNeedsLayout();
} }
...@@ -115,7 +112,9 @@ class RenderEditable extends RenderBox { ...@@ -115,7 +112,9 @@ class RenderEditable extends RenderBox {
markNeedsPaint(); markNeedsPaint();
} }
/// Whether to paint the cursor. /// The maximum number of lines for the text to span, wrapping if necessary.
/// If this is 1 (the default), the text will not wrap, but will extend
/// indefinitely instead.
int get maxLines => _maxLines; int get maxLines => _maxLines;
int _maxLines; int _maxLines;
set maxLines(int value) { set maxLines(int value) {
...@@ -222,19 +221,7 @@ class RenderEditable extends RenderBox { ...@@ -222,19 +221,7 @@ class RenderEditable extends RenderBox {
Size _contentSize; Size _contentSize;
ui.Paragraph _layoutTemplate; double get _preferredLineHeight => _textPainter.preferredLineHeight;
double get _preferredLineHeight {
if (_layoutTemplate == null) {
ui.ParagraphBuilder builder = new ui.ParagraphBuilder(new ui.ParagraphStyle())
..pushStyle(text.style.getTextStyle(textScaleFactor: textScaleFactor))
..addText(_kZeroWidthSpace);
// TODO(abarth): ParagraphBuilder#build's argument should be optional.
// TODO(abarth): These min/max values should be the default for ui.Paragraph.
_layoutTemplate = builder.build()
..layout(new ui.ParagraphConstraints(width: double.INFINITY));
}
return _layoutTemplate.height;
}
double get _maxContentWidth { double get _maxContentWidth {
return _maxLines > 1 ? return _maxLines > 1 ?
......
...@@ -33,14 +33,16 @@ class RenderParagraph extends RenderBox { ...@@ -33,14 +33,16 @@ class RenderParagraph extends RenderBox {
TextAlign textAlign, TextAlign textAlign,
bool softWrap: true, bool softWrap: true,
TextOverflow overflow: TextOverflow.clip, TextOverflow overflow: TextOverflow.clip,
double textScaleFactor: 1.0 double textScaleFactor: 1.0,
int maxLines,
}) : _softWrap = softWrap, }) : _softWrap = softWrap,
_overflow = overflow, _overflow = overflow,
_textPainter = new TextPainter( _textPainter = new TextPainter(
text: text, text: text,
textAlign: textAlign, textAlign: textAlign,
textScaleFactor: textScaleFactor, textScaleFactor: textScaleFactor,
ellipsis: overflow == TextOverflow.ellipsis ? _kEllipsis : null, maxLines: maxLines,
ellipsis: overflow == TextOverflow.ellipsis ? _kEllipsis : null,
) { ) {
assert(text != null); assert(text != null);
assert(text.debugAssertIsValid()); assert(text.debugAssertIsValid());
...@@ -110,8 +112,20 @@ class RenderParagraph extends RenderBox { ...@@ -110,8 +112,20 @@ class RenderParagraph extends RenderBox {
markNeedsLayout(); markNeedsLayout();
} }
/// An optional maximum number of lines for the text to span, wrapping if necessary.
/// If the text exceeds the given number of lines, it will be truncated according
/// to [overflow].
int get maxLines => _textPainter.maxLines;
set maxLines(int value) {
if (_textPainter.maxLines == value)
return;
_textPainter.maxLines = value;
_overflowShader = null;
markNeedsLayout();
}
void _layoutText({ double minWidth: 0.0, double maxWidth: double.INFINITY }) { void _layoutText({ double minWidth: 0.0, double maxWidth: double.INFINITY }) {
bool wrap = _softWrap || _overflow == TextOverflow.ellipsis; bool wrap = _softWrap || (_overflow == TextOverflow.ellipsis && maxLines == null);
_textPainter.layout(minWidth: minWidth, maxWidth: wrap ? maxWidth : double.INFINITY); _textPainter.layout(minWidth: minWidth, maxWidth: wrap ? maxWidth : double.INFINITY);
} }
...@@ -183,30 +197,40 @@ class RenderParagraph extends RenderBox { ...@@ -183,30 +197,40 @@ class RenderParagraph extends RenderBox {
size = constraints.constrain(textSize); size = constraints.constrain(textSize);
final bool didOverflowWidth = size.width < textSize.width; final bool didOverflowWidth = size.width < textSize.width;
final bool didOverflowHeight = _textPainter.didExceedMaxLines;
// TODO(abarth): We're only measuring the sizes of the line boxes here. If // TODO(abarth): We're only measuring the sizes of the line boxes here. If
// the glyphs draw outside the line boxes, we might think that there isn't // the glyphs draw outside the line boxes, we might think that there isn't
// visual overflow when there actually is visual overflow. This can become // visual overflow when there actually is visual overflow. This can become
// a problem if we start having horizontal overflow and introduce a clip // a problem if we start having horizontal overflow and introduce a clip
// that affects the actual (but undetected) vertical overflow. // that affects the actual (but undetected) vertical overflow.
_hasVisualOverflow = didOverflowWidth || size.height < textSize.height; _hasVisualOverflow = didOverflowWidth || didOverflowHeight;
if (didOverflowWidth) { if (_hasVisualOverflow) {
switch (_overflow) { switch (_overflow) {
case TextOverflow.clip: case TextOverflow.clip:
case TextOverflow.ellipsis: case TextOverflow.ellipsis:
_overflowShader = null; _overflowShader = null;
break; break;
case TextOverflow.fade: case TextOverflow.fade:
TextPainter fadeWidthPainter = new TextPainter( TextPainter fadeSizePainter = new TextPainter(
text: new TextSpan(style: _textPainter.text.style, text: '\u2026'), text: new TextSpan(style: _textPainter.text.style, text: '\u2026'),
textScaleFactor: textScaleFactor textScaleFactor: textScaleFactor
)..layout(); )..layout();
final double fadeEnd = size.width; if (didOverflowWidth) {
final double fadeStart = fadeEnd - fadeWidthPainter.width; final double fadeEnd = size.width;
// TODO(abarth): This shader has an LTR bias. final double fadeStart = fadeEnd - fadeSizePainter.width;
_overflowShader = new ui.Gradient.linear( // TODO(abarth): This shader has an LTR bias.
<Point>[new Point(fadeStart, 0.0), new Point(fadeEnd, 0.0)], _overflowShader = new ui.Gradient.linear(
<Color>[const Color(0xFFFFFFFF), const Color(0x00FFFFFF)] <Point>[new Point(fadeStart, 0.0), new Point(fadeEnd, 0.0)],
); <Color>[const Color(0xFFFFFFFF), const Color(0x00FFFFFF)]
);
} else {
final double fadeEnd = size.height;
final double fadeStart = fadeEnd - fadeSizePainter.height / 2.0;
_overflowShader = new ui.Gradient.linear(
<Point>[new Point(0.0, fadeStart), new Point(0.0, fadeEnd)],
<Color>[const Color(0xFFFFFFFF), const Color(0x00FFFFFF)]
);
}
break; break;
} }
} else { } else {
......
...@@ -2442,7 +2442,8 @@ class RichText extends LeafRenderObjectWidget { ...@@ -2442,7 +2442,8 @@ class RichText extends LeafRenderObjectWidget {
this.textAlign, this.textAlign,
this.softWrap: true, this.softWrap: true,
this.overflow: TextOverflow.clip, this.overflow: TextOverflow.clip,
this.textScaleFactor: 1.0 this.textScaleFactor: 1.0,
this.maxLines,
}) : super(key: key) { }) : super(key: key) {
assert(text != null); assert(text != null);
assert(softWrap != null); assert(softWrap != null);
...@@ -2470,13 +2471,19 @@ class RichText extends LeafRenderObjectWidget { ...@@ -2470,13 +2471,19 @@ class RichText extends LeafRenderObjectWidget {
/// the specified font size. /// the specified font size.
final double textScaleFactor; final double textScaleFactor;
/// An optional maximum number of lines for the text to span, wrapping if necessary.
/// If the text exceeds the given number of lines, it will be truncated according
/// to [overflow].
final int maxLines;
@override @override
RenderParagraph createRenderObject(BuildContext context) { RenderParagraph createRenderObject(BuildContext context) {
return new RenderParagraph(text, return new RenderParagraph(text,
textAlign: textAlign, textAlign: textAlign,
softWrap: softWrap, softWrap: softWrap,
overflow: overflow, overflow: overflow,
textScaleFactor: textScaleFactor textScaleFactor: textScaleFactor,
maxLines: maxLines,
); );
} }
...@@ -2487,7 +2494,8 @@ class RichText extends LeafRenderObjectWidget { ...@@ -2487,7 +2494,8 @@ class RichText extends LeafRenderObjectWidget {
..textAlign = textAlign ..textAlign = textAlign
..softWrap = softWrap ..softWrap = softWrap
..overflow = overflow ..overflow = overflow
..textScaleFactor = textScaleFactor; ..textScaleFactor = textScaleFactor
..maxLines = maxLines;
} }
} }
......
...@@ -20,6 +20,7 @@ class DefaultTextStyle extends InheritedWidget { ...@@ -20,6 +20,7 @@ class DefaultTextStyle extends InheritedWidget {
this.textAlign, this.textAlign,
this.softWrap: true, this.softWrap: true,
this.overflow: TextOverflow.clip, this.overflow: TextOverflow.clip,
this.maxLines,
Widget child Widget child
}) : super(key: key, child: child) { }) : super(key: key, child: child) {
assert(style != null); assert(style != null);
...@@ -35,6 +36,7 @@ class DefaultTextStyle extends InheritedWidget { ...@@ -35,6 +36,7 @@ class DefaultTextStyle extends InheritedWidget {
: style = const TextStyle(), : style = const TextStyle(),
textAlign = null, textAlign = null,
softWrap = true, softWrap = true,
maxLines = null,
overflow = TextOverflow.clip; overflow = TextOverflow.clip;
/// Creates a default text style that inherits from the given [BuildContext]. /// Creates a default text style that inherits from the given [BuildContext].
...@@ -50,6 +52,7 @@ class DefaultTextStyle extends InheritedWidget { ...@@ -50,6 +52,7 @@ class DefaultTextStyle extends InheritedWidget {
TextAlign textAlign, TextAlign textAlign,
bool softWrap, bool softWrap,
TextOverflow overflow, TextOverflow overflow,
int maxLines,
Widget child Widget child
}) { }) {
assert(context != null); assert(context != null);
...@@ -61,6 +64,7 @@ class DefaultTextStyle extends InheritedWidget { ...@@ -61,6 +64,7 @@ class DefaultTextStyle extends InheritedWidget {
textAlign: textAlign ?? parent.textAlign, textAlign: textAlign ?? parent.textAlign,
softWrap: softWrap ?? parent.softWrap, softWrap: softWrap ?? parent.softWrap,
overflow: overflow ?? parent.overflow, overflow: overflow ?? parent.overflow,
maxLines: maxLines ?? parent.maxLines,
child: child child: child
); );
} }
...@@ -79,6 +83,11 @@ class DefaultTextStyle extends InheritedWidget { ...@@ -79,6 +83,11 @@ class DefaultTextStyle extends InheritedWidget {
/// How visual overflow should be handled. /// How visual overflow should be handled.
final TextOverflow overflow; final TextOverflow overflow;
/// An optional maximum number of lines for the text to span, wrapping if necessary.
/// If the text exceeds the given number of lines, it will be truncated according
/// to [overflow].
final int maxLines;
/// The closest instance of this class that encloses the given context. /// The closest instance of this class that encloses the given context.
/// ///
/// If no such instance exists, returns an instance created by /// If no such instance exists, returns an instance created by
...@@ -134,7 +143,8 @@ class Text extends StatelessWidget { ...@@ -134,7 +143,8 @@ class Text extends StatelessWidget {
this.textAlign, this.textAlign,
this.softWrap, this.softWrap,
this.overflow, this.overflow,
this.textScaleFactor this.textScaleFactor,
this.maxLines,
}) : super(key: key) { }) : super(key: key) {
assert(data != null); assert(data != null);
} }
...@@ -168,6 +178,11 @@ class Text extends StatelessWidget { ...@@ -168,6 +178,11 @@ class Text extends StatelessWidget {
/// Defaults to [MediaQuery.textScaleFactor]. /// Defaults to [MediaQuery.textScaleFactor].
final double textScaleFactor; final double textScaleFactor;
/// An optional maximum number of lines the text is allowed to take up.
/// If the text exceeds the given number of lines, it will be truncated according
/// to [overflow].
final int maxLines;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context); DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context);
...@@ -179,6 +194,7 @@ class Text extends StatelessWidget { ...@@ -179,6 +194,7 @@ class Text extends StatelessWidget {
softWrap: softWrap ?? defaultTextStyle.softWrap, softWrap: softWrap ?? defaultTextStyle.softWrap,
overflow: overflow ?? defaultTextStyle.overflow, overflow: overflow ?? defaultTextStyle.overflow,
textScaleFactor: textScaleFactor ?? MediaQuery.of(context).textScaleFactor, textScaleFactor: textScaleFactor ?? MediaQuery.of(context).textScaleFactor,
maxLines: maxLines ?? defaultTextStyle.maxLines,
text: new TextSpan( text: new TextSpan(
style: effectiveTextStyle, style: effectiveTextStyle,
text: data text: data
......
...@@ -98,9 +98,9 @@ void main() { ...@@ -98,9 +98,9 @@ void main() {
ui.ParagraphStyle ps2 = s2.getParagraphStyle(textAlign: TextAlign.center); ui.ParagraphStyle ps2 = s2.getParagraphStyle(textAlign: TextAlign.center);
expect(ps2, equals(new ui.ParagraphStyle(textAlign: TextAlign.center, fontWeight: FontWeight.w800, fontSize: 10.0, lineHeight: 100.0))); expect(ps2, equals(new ui.ParagraphStyle(textAlign: TextAlign.center, fontWeight: FontWeight.w800, fontSize: 10.0, lineHeight: 100.0)));
expect(ps2.toString(), 'ParagraphStyle(textAlign: TextAlign.center, fontWeight: FontWeight.w800, fontStyle: unspecified, lineCount: unspecified, fontFamily: unspecified, fontSize: 10.0, lineHeight: 100.0x, ellipsis: unspecified)'); expect(ps2.toString(), 'ParagraphStyle(textAlign: TextAlign.center, fontWeight: FontWeight.w800, fontStyle: unspecified, lineCount: unspecified, fontFamily: unspecified, fontSize: 10.0, lineHeight: 100.0x, maxLines: unspecified, ellipsis: unspecified)');
ui.ParagraphStyle ps5 = s5.getParagraphStyle(); ui.ParagraphStyle ps5 = s5.getParagraphStyle();
expect(ps5, equals(new ui.ParagraphStyle(fontWeight: FontWeight.w700, fontSize: 12.0, lineHeight: 123.0))); expect(ps5, equals(new ui.ParagraphStyle(fontWeight: FontWeight.w700, fontSize: 12.0, lineHeight: 123.0)));
expect(ps5.toString(), 'ParagraphStyle(textAlign: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, lineCount: unspecified, fontFamily: unspecified, fontSize: 12.0, lineHeight: 123.0x, ellipsis: unspecified)'); expect(ps5.toString(), 'ParagraphStyle(textAlign: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, lineCount: unspecified, fontFamily: unspecified, fontSize: 12.0, lineHeight: 123.0x, maxLines: unspecified, ellipsis: unspecified)');
}); });
} }
...@@ -72,4 +72,64 @@ void main() { ...@@ -72,4 +72,64 @@ void main() {
TextRange range85 = paragraph.getWordBoundary(new TextPosition(offset: 75)); TextRange range85 = paragraph.getWordBoundary(new TextPosition(offset: 75));
expect(range85.textInside(_kText), equals('Queen\'s')); expect(range85.textInside(_kText), equals('Queen\'s'));
}); });
test('overflow test', () {
RenderParagraph paragraph = new RenderParagraph(
new TextSpan(text: 'This is\na wrapping test. It should wrap at manual newlines, and if softWrap is true, also at spaces.'),
maxLines: 1,
softWrap: true,
);
void relayoutWith({int maxLines, bool softWrap, TextOverflow overflow}) {
paragraph
..maxLines = maxLines
..softWrap = softWrap
..overflow = overflow;
pumpFrame();
}
// Lay out in a narrow box to force wrapping.
layout(paragraph, constraints: new BoxConstraints(maxWidth: 50.0));
double lineHeight = paragraph.size.height;
relayoutWith(maxLines: 3, softWrap: true, overflow: TextOverflow.clip);
expect(paragraph.size.height, equals(3 * lineHeight));
relayoutWith(maxLines: null, softWrap: true, overflow: TextOverflow.clip);
expect(paragraph.size.height, greaterThan(5 * lineHeight));
// Try again with ellipsis overflow. We can't test that the ellipsis are
// drawn, but we can test the sizing.
relayoutWith(maxLines: 1, softWrap: true, overflow: TextOverflow.ellipsis);
expect(paragraph.size.height, equals(lineHeight));
relayoutWith(maxLines: 3, softWrap: true, overflow: TextOverflow.ellipsis);
expect(paragraph.size.height, equals(3 * lineHeight));
// This is the one weird case. If maxLines is null, we would expect to allow
// infinite wrapping. However, if we did, we'd never know when to append an
// ellipsis, so this really means "append ellipsis as soon as we exceed the
// width".
relayoutWith(maxLines: null, softWrap: true, overflow: TextOverflow.ellipsis);
expect(paragraph.size.height, equals(2 * lineHeight));
// Now with no soft wrapping.
relayoutWith(maxLines: 1, softWrap: false, overflow: TextOverflow.clip);
expect(paragraph.size.height, equals(lineHeight));
relayoutWith(maxLines: 3, softWrap: false, overflow: TextOverflow.clip);
expect(paragraph.size.height, equals(2 * lineHeight));
relayoutWith(maxLines: null, softWrap: false, overflow: TextOverflow.clip);
expect(paragraph.size.height, equals(2 * lineHeight));
relayoutWith(maxLines: 1, softWrap: false, overflow: TextOverflow.ellipsis);
expect(paragraph.size.height, equals(lineHeight));
relayoutWith(maxLines: 3, softWrap: false, overflow: TextOverflow.ellipsis);
expect(paragraph.size.height, equals(2 * lineHeight));
relayoutWith(maxLines: null, softWrap: false, overflow: TextOverflow.ellipsis);
expect(paragraph.size.height, equals(2 * lineHeight));
});
} }
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