paint method
Implementation
@override
String paint() {
if (children.isEmpty) return '';
final child = children.first;
final content = child.paint();
// Use the layout-allocated child size for positioning, not the painted
// output dimensions. The painted output's visible width can vary per
// line (shorter text lines, ANSI escapes, etc.) which causes the
// scrollbar to shift left/right. The layout size is stable.
final layoutW = child.size.width.toInt();
final layoutH = child.size.height.toInt();
// Fall back to painted dimensions only when layout reports zero.
final paintedSize = Layout.getSize(content);
final stableW = layoutW > 0 ? layoutW : paintedSize.width;
final stableH = layoutH > 0 ? layoutH : paintedSize.height;
if (stableW <= 0 || stableH <= 0) {
return content;
}
var thumbWidth = math.max(1, thickness);
final trackWidth = math.max(thumbWidth, gutterWidth ?? thumbWidth);
final isHovered = hovered || _hoveredScrollbars.contains(zoneId);
final effectiveTrackStyle = isHovered
? (hoverTrackStyle ?? trackStyle)
: trackStyle;
final effectiveThumbStyle = isHovered
? (hoverThumbStyle ?? thumbStyle)
: thumbStyle;
final effectiveTrackGradient = isHovered
? (hoverTrackGradient ?? trackGradient)
: trackGradient;
final effectiveThumbGradient = isHovered
? (hoverThumbGradient ?? thumbGradient)
: thumbGradient;
final effectiveTrackChar = isHovered
? (hoverTrackChar ?? trackChar)
: trackChar;
final effectiveThumbChar = isHovered
? (hoverThumbChar ?? thumbChar)
: thumbChar;
final expandThumbToTrack =
thumbUsesBackground && effectiveThumbChar.trim().isEmpty;
if (expandThumbToTrack && trackWidth > thumbWidth) {
thumbWidth = trackWidth;
}
final allowCaps =
roundedCaps && !thumbUsesBackground && effectiveThumbChar != ' ';
final capTop = thumbCapTopChar ?? (allowCaps ? '▀' : null);
final capBottom = thumbCapBottomChar ?? (allowCaps ? '▄' : null);
final bar = _renderScrollbar(
controller: controller,
height: stableH,
trackWidth: trackWidth,
thumbWidth: thumbWidth,
trackStyle: effectiveTrackStyle,
thumbStyle: effectiveThumbStyle,
trackGradient: effectiveTrackGradient,
thumbGradient: effectiveThumbGradient,
trackUsesBackground: trackUsesBackground,
thumbUsesBackground: thumbUsesBackground,
trackChar: effectiveTrackChar,
thumbChar: effectiveThumbChar,
thumbCapTopChar: capTop,
thumbCapBottomChar: capBottom,
zoneId: zoneId,
);
if (bar.isEmpty) return content;
if (!overlay) {
// Use a canvas to position the scrollbar at a stable offset based on
// the layout-allocated width rather than the painted content width.
final gapW = math.max(0, gap);
final totalW = stableW + gapW + trackWidth;
final canvas = Canvas(totalW, stableH);
_drawStyledContent(canvas, content, 0, 0);
final barX = stableW + gapW;
_drawStyledContent(canvas, bar, barX, 0);
return canvas.render();
}
final canvas = Canvas(stableW, stableH);
_drawStyledContent(canvas, content, 0, 0);
final barX = math.max(0, stableW - trackWidth);
final barOverwritesSpaces =
trackUsesBackground ||
thumbUsesBackground ||
(effectiveTrackGradient?.useBackground ?? false) ||
(effectiveThumbGradient?.useBackground ?? false);
_drawStyledContent(
canvas,
bar,
barX,
0,
treatSpacesAsTransparent: !barOverwritesSpaces,
);
return canvas.render();
}