layout method
Computes the visual position of the glyphs for painting the text.
The text will layout with a width that's as close to its max intrinsic
width (or its longest line, if textWidthBasis is set to
TextWidthBasis.parent) as possible while still being greater than or
equal to minWidth
and less than or equal to maxWidth
.
The text and textDirection properties must be non-null before this is called.
Implementation
void layout({ double minWidth = 0.0, double maxWidth = double.infinity }) {
assert(!maxWidth.isNaN);
assert(!minWidth.isNaN);
assert(() {
_debugNeedsRelayout = false;
return true;
}());
final _TextPainterLayoutCacheWithOffset? cachedLayout = _layoutCache;
if (cachedLayout != null && cachedLayout._resizeToFit(minWidth, maxWidth, textWidthBasis)) {
return;
}
final InlineSpan? text = this.text;
if (text == null) {
throw StateError('TextPainter.text must be set to a non-null value before using the TextPainter.');
}
final TextDirection? textDirection = this.textDirection;
if (textDirection == null) {
throw StateError('TextPainter.textDirection must be set to a non-null value before using the TextPainter.');
}
final double paintOffsetAlignment = _computePaintOffsetFraction(textAlign, textDirection);
// Try to avoid laying out the paragraph with maxWidth=double.infinity
// when the text is not left-aligned, so we don't have to deal with an
// infinite paint offset.
final bool adjustMaxWidth = !maxWidth.isFinite && paintOffsetAlignment != 0;
final double? adjustedMaxWidth = !adjustMaxWidth ? maxWidth : cachedLayout?.layout.maxIntrinsicLineExtent;
final double layoutMaxWidth = adjustedMaxWidth ?? maxWidth;
// Only rebuild the paragraph when there're layout changes, even when
// `_rebuildParagraphForPaint` is true. It's best to not eagerly rebuild
// the paragraph to avoid the extra work, because:
// 1. the text color could change again before `paint` is called (so one of
// the paragraph rebuilds is unnecessary)
// 2. the user could be measuring the text layout so `paint` will never be
// called.
final ui.Paragraph paragraph = (cachedLayout?.paragraph ?? _createParagraph(text))
..layout(ui.ParagraphConstraints(width: layoutMaxWidth));
final _TextLayout layout = _TextLayout._(paragraph, textDirection, this);
final double contentWidth = layout._contentWidthFor(minWidth, maxWidth, textWidthBasis);
final _TextPainterLayoutCacheWithOffset newLayoutCache;
// Call layout again if newLayoutCache had an infinite paint offset.
// This is not as expensive as it seems, line breaking is relatively cheap
// as compared to shaping.
if (adjustedMaxWidth == null && minWidth.isFinite) {
assert(maxWidth.isInfinite);
final double newInputWidth = layout.maxIntrinsicLineExtent;
paragraph.layout(ui.ParagraphConstraints(width: newInputWidth));
newLayoutCache = _TextPainterLayoutCacheWithOffset(layout, paintOffsetAlignment, newInputWidth, contentWidth);
} else {
newLayoutCache = _TextPainterLayoutCacheWithOffset(layout, paintOffsetAlignment, layoutMaxWidth, contentWidth);
}
_layoutCache = newLayoutCache;
}