layout method
void
layout(
- Context context,
- BoxConstraints constraints, {
- bool parentUsesSize = false,
override
First widget pass to calculate the children layout and bounding box
Implementation
@override
void layout(Context context, BoxConstraints constraints,
{bool parentUsesSize = false}) {
_spans.clear();
_decorations.clear();
final theme = Theme.of(context);
final _softWrap = softWrap ?? theme.softWrap;
final _maxLines = maxLines ?? theme.maxLines;
final _textDirection = textDirection ?? Directionality.of(context);
_textAlign = textAlign ?? theme.textAlign ?? TextAlign.start;
final _overflow = this.overflow ?? theme.overflow;
final constraintWidth = constraints.hasBoundedWidth
? constraints.maxWidth
: constraints.constrainWidth();
final constraintHeight = constraints.hasBoundedHeight
? constraints.maxHeight
: constraints.constrainHeight();
var offsetX = 0.0;
var offsetY = _context.startOffset;
var top = 0.0;
var bottom = 0.0;
final lines = <_Line>[];
var spanCount = 0;
var spanStart = 0;
var overflow = false;
_preprocessed ??= _preProcessSpans(context);
void _buildLines() {
for (final span in _preprocessed!) {
final style = span.style;
final annotation = span.annotation;
if (span is TextSpan) {
if (span.text == null) {
continue;
}
final font = style!.font!.getFont(context);
final space =
font.stringMetrics(' ') * (style.fontSize! * textScaleFactor);
final spanLines = (useArabic && _textDirection == TextDirection.rtl
? arabic.convert(span.text!)
: useBidi && _textDirection == TextDirection.rtl
? bidi.logicalToVisual(span.text!)
: span.text)!
.split('\n');
for (var line = 0; line < spanLines.length; line++) {
final words = spanLines[line].split(RegExp(r'\s'));
for (var index = 0; index < words.length; index++) {
final word = words[index];
if (word.isEmpty) {
offsetX += space.advanceWidth * style.wordSpacing! +
style.letterSpacing!;
continue;
}
final metrics = font.stringMetrics(word,
letterSpacing: style.letterSpacing! /
(style.fontSize! * textScaleFactor)) *
(style.fontSize! * textScaleFactor);
if (_softWrap &&
offsetX + metrics.width > constraintWidth + 0.00001) {
if (hyphenation != null) {
final syllables = hyphenation!(word);
if (syllables.length > 1) {
var fits = '';
for (var syllable in syllables) {
if (offsetX +
((font.stringMetrics('$fits$syllable-',
letterSpacing: style.letterSpacing! /
(style.fontSize! *
textScaleFactor)) *
(style.fontSize! * textScaleFactor))
.width) >
constraintWidth + 0.00001) {
break;
}
fits += syllable;
}
if (fits.isNotEmpty) {
words[index] = '$fits-';
words.insert(index + 1, word.substring(fits.length));
index--;
continue;
}
}
}
if (spanCount > 0 && metrics.width <= constraintWidth) {
overflow = true;
lines.add(_Line(
this,
spanStart,
spanCount,
bottom,
offsetX -
space.advanceWidth * style.wordSpacing! -
style.letterSpacing!,
_textDirection,
true,
));
spanStart += spanCount;
spanCount = 0;
offsetX = 0.0;
offsetY += bottom - top;
top = 0;
bottom = 0;
if (_maxLines != null && lines.length >= _maxLines) {
return;
}
if (offsetY > constraintHeight) {
return;
}
offsetY += style.lineSpacing! * textScaleFactor;
} else {
// One word Overflow, try to split it.
final pos = _splitWord(word, font, style, constraintWidth);
if (pos < word.length) {
words[index] = word.substring(0, pos);
words.insert(index + 1, word.substring(pos));
// Try again
index--;
continue;
}
}
}
final baseline = span.baseline * textScaleFactor;
final mt = tightBounds ? metrics.top : metrics.descent;
final mb = tightBounds ? metrics.bottom : metrics.ascent;
top = math.min(top, mt + baseline);
bottom = math.max(bottom, mb + baseline);
final wd = _Word(
word,
style,
metrics,
);
wd.offset = PdfPoint(offsetX, -offsetY + baseline);
_spans.add(wd);
spanCount++;
_appendDecoration(
spanCount > 1,
_TextDecoration(
style,
annotation,
_spans.length - 1,
_spans.length - 1,
),
);
offsetX += metrics.advanceWidth +
space.advanceWidth * style.wordSpacing! +
style.letterSpacing!;
}
if (line < spanLines.length - 1) {
lines.add(_Line(
this,
spanStart,
spanCount,
bottom,
offsetX -
space.advanceWidth * style.wordSpacing! -
style.letterSpacing!,
_textDirection,
false,
));
spanStart += spanCount;
offsetX = 0.0;
if (spanCount > 0) {
offsetY += bottom - top;
} else {
offsetY +=
font.emptyLineHeight * style.fontSize! * textScaleFactor;
}
top = 0;
bottom = 0;
spanCount = 0;
if (_maxLines != null && lines.length >= _maxLines) {
return;
}
if (offsetY > constraintHeight) {
return;
}
offsetY += style.lineSpacing! * textScaleFactor;
}
}
offsetX -=
space.advanceWidth * style.wordSpacing! - style.letterSpacing!;
} else if (span is WidgetSpan) {
span.child.layout(
context,
BoxConstraints(
maxWidth: constraintWidth,
maxHeight: constraintHeight,
));
final ws = _WidgetSpan(
span.child,
style!,
span.baseline,
);
if (offsetX + ws.width > constraintWidth && spanCount > 0) {
overflow = true;
lines.add(_Line(
this,
spanStart,
spanCount,
bottom,
offsetX,
_textDirection,
true,
));
spanStart += spanCount;
spanCount = 0;
if (_maxLines != null && lines.length > _maxLines) {
return;
}
offsetX = 0.0;
offsetY += bottom - top;
top = 0;
bottom = 0;
if (offsetY > constraintHeight) {
return;
}
offsetY += style.lineSpacing! * textScaleFactor;
}
final baseline = span.baseline * textScaleFactor;
top = math.min(top, baseline);
bottom = math.max(
bottom,
ws.height + baseline,
);
ws.offset = PdfPoint(offsetX, -offsetY + baseline);
_spans.add(ws);
spanCount++;
_appendDecoration(
spanCount > 1,
_TextDecoration(
style,
annotation,
_spans.length - 1,
_spans.length - 1,
),
);
offsetX += ws.left + ws.width;
}
}
}
_buildLines();
if (spanCount > 0) {
lines.add(_Line(
this,
spanStart,
spanCount,
bottom,
offsetX,
_textDirection,
false,
));
offsetY += bottom - top;
}
assert(!overflow || constraintWidth.isFinite);
var width = overflow ? constraintWidth : constraints.minWidth;
if (lines.isNotEmpty) {
if (!overflow) {
// Calculate the final width
for (final line in lines) {
width = math.max(width, line.wordsWidth);
}
}
// Realign all the lines
for (final line in lines) {
line.realign(width);
}
}
box = PdfRect(0, 0, constraints.constrainWidth(width),
constraints.constrainHeight(offsetY));
_context
..endOffset = offsetY - _context.startOffset
..spanEnd = _spans.length;
if (_overflow != TextOverflow.span) {
if (_overflow != TextOverflow.visible) {
_mustClip = true;
}
return;
}
if (offsetY > constraintHeight + 0.0001) {
_context.spanEnd -= lines.last.countSpan;
_context.endOffset -= lines.last.height;
}
for (var index = 0; index < _decorations.length; index++) {
final decoration = _decorations[index];
if (decoration.startSpan >= _context.spanEnd ||
decoration.endSpan < _context.spanStart) {
_decorations.removeAt(index);
index--;
}
}
}