performLayout method

  1. @override
void performLayout()
override

Do the work of computing the layout for this render object.

Do not call this function directly: call layout instead. This function is called by layout when there is actually work to be done by this render object during layout. The layout constraints provided by your parent are available via the constraints getter.

If sizedByParent is true, then this function should not actually change the dimensions of this render object. Instead, that work should be done by performResize. If sizedByParent is false, then this function should both change the dimensions of this render object and instruct its children to layout.

In implementing this function, you must call layout on each of your children, passing true for parentUsesSize if your layout information is dependent on your child's layout information. Passing true for parentUsesSize ensures that this render object will undergo layout if the child undergoes layout. Otherwise, the child can change its layout information without informing this render object.

Implementation

@override
void performLayout() {
  _layoutChildren(constraints);
  _layoutTextWithConstraints(constraints);
  _setParentData();

  // We grab _textPainter.size and _textPainter.didExceedMaxLines here because
  // assigning to `size` will trigger us to validate our intrinsic sizes,
  // which will change _textPainter's layout because the intrinsic size
  // calculations are destructive. Other _textPainter state will also be
  // affected. See also RenderEditable which has a similar issue.
  final Size textSize = _textPainter.size;
  size = constraints.constrain(textSize);

  // This is for 1 line only, so it should never overflow the height.
  const bool didOverflowHeight = false;

  final bool didOverflowWidth = size.width < textSize.width;
  // 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
  // visual overflow when there actually is visual overflow. This can become
  // a problem if we start having horizontal overflow and introduce a clip
  // that affects the actual (but undetected) vertical overflow.
  final bool hasVisualOverflow = didOverflowWidth || didOverflowHeight;
  if (hasVisualOverflow) {
    switch (_overflow) {
      //
      // VISIBLE: ---------------------------
      case TextOverflow.visible:
        throw UnsupportedError("Don't use TextOverflow.visible.");

      // CLIP: ---------------------------
      case TextOverflow.clip:
        _needsClipping = true;
        _overflowShader = null;
        break;
      //

      // ELLIPSIS: ---------------------------
      case TextOverflow.ellipsis:
        _needsClipping = true;
        final TextPainter fadeSizePainter = TextPainter(
          text: TextSpan(style: _textPainter.text!.style, text: '\u2026'),
          textDirection: textDirection,
          textScaleFactor: textScaleFactor,
          locale: locale,
        )..layout();

        if (didOverflowWidth) {
          double fadeEnd, fadeStart;
          switch (textDirection) {
            case TextDirection.rtl:
              // Very short fade, a little bit before the ellipsis.
              fadeEnd = fadeSizePainter.width * 0.9;
              fadeStart = fadeSizePainter.width * 4 / 3;
              break;

            case TextDirection.ltr:
              // Short fade, right before the ellipsis.
              fadeEnd = size.width - fadeSizePainter.width * 0.9;
              fadeStart = fadeEnd - fadeSizePainter.width / 3;
              break;
          }
          _overflowShader = ui.Gradient.linear(
            Offset(fadeStart, 0.0),
            Offset(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 = ui.Gradient.linear(
            Offset(0.0, fadeStart),
            Offset(0.0, fadeEnd),
            <Color>[const Color(0xFFFFFFFF), const Color(0x00FFFFFF)],
          );
        }
        break;

      // FADE: --------------------------------
      case TextOverflow.fade:
        _needsClipping = true;
        final TextPainter fadeSizePainter = TextPainter(
          text: TextSpan(style: _textPainter.text!.style, text: '\u2026'),
          textDirection: textDirection,
          textScaleFactor: textScaleFactor,
          locale: locale,
        )..layout();
        if (didOverflowWidth) {
          double fadeEnd, fadeStart;
          switch (textDirection) {
            case TextDirection.rtl:
              fadeEnd = 0.0;
              fadeStart = fadeSizePainter.width;
              break;
            case TextDirection.ltr:
              fadeEnd = size.width;
              fadeStart = fadeEnd - fadeSizePainter.width;
              break;
          }
          _overflowShader = ui.Gradient.linear(
            Offset(fadeStart, 0.0),
            Offset(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 = ui.Gradient.linear(
            Offset(0.0, fadeStart),
            Offset(0.0, fadeEnd),
            <Color>[const Color(0xFFFFFFFF), const Color(0x00FFFFFF)],
          );
        }
        break;
    }
  } else {
    _needsClipping = false;
    _overflowShader = null;
  }
}