paint method

  1. @override
void paint(
  1. Canvas canvas,
  2. Offset offset,
  3. ImageConfiguration configuration
)
override

Paint the box decoration into the given location on the given canvas

Implementation

@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
  assert(configuration.size != null);

  final Rect rect = offset & configuration.size!;
  final TextDirection? textDirection = configuration.textDirection;

  // When this element participates in an inline formatting context, backgrounds and borders
  // for inline-level boxes are painted by the paragraph path (InlineFormattingContext).
  // Skip BoxDecoration painting here to avoid double painting and mismatched joins.
  bool skipForInlineIFC() {
    // Only inline-level boxes are painted via paragraph IFC.
    if (renderStyle.effectiveDisplay != CSSDisplay.inline) return false;
    final RenderBoxModel? self = renderStyle.attachedRenderBoxModel;
    if (self == null) return false;
    RenderObject? p = self.parent;
    while (p != null) {
      if (p is RenderFlowLayout) {
        return p.establishIFC;
      }
      p = (p).parent;
    }
    return false;
  }

  if (skipForInlineIFC()) {
    return;
  }

  bool hasLocalAttachment = _hasLocalBackgroundImage();
  if (!hasLocalAttachment) {
    if (renderStyle.backgroundClip != CSSBackgroundBoundary.text) {
      final bool hasGradients = _hasGradientLayers();
      final bool hasImages = _hasImageLayers();
      Rect backgroundClipRect = _getBackgroundClipRect(offset, configuration);
      Rect backgroundOriginRect = _getBackgroundOriginRect(offset, configuration);

      if (hasImages) {
        // Paint layered images (and gradients if present) in proper order.
        _paintLayeredMixedBackgrounds(canvas, backgroundClipRect, backgroundOriginRect, configuration, textDirection);
      } else if (hasGradients) {
        _paintBackgroundColor(canvas, backgroundClipRect, textDirection);
      } else {
        _paintBackgroundColor(canvas, backgroundClipRect, textDirection);
      }
    }
  }

  // Check for custom border styles (dashed/double)
  bool hasDashedBorder = false;
  bool hasDoubleBorder = false;

  if (_decoration.border != null) {
    final Border border = _decoration.border as Border;

    bool topDashed = border.top is ExtendedBorderSide &&
        (border.top as ExtendedBorderSide).extendBorderStyle == CSSBorderStyleType.dashed;
    bool rightDashed = border.right is ExtendedBorderSide &&
        (border.right as ExtendedBorderSide).extendBorderStyle == CSSBorderStyleType.dashed;
    bool bottomDashed = border.bottom is ExtendedBorderSide &&
        (border.bottom as ExtendedBorderSide).extendBorderStyle == CSSBorderStyleType.dashed;
    bool leftDashed = border.left is ExtendedBorderSide &&
        (border.left as ExtendedBorderSide).extendBorderStyle == CSSBorderStyleType.dashed;

    hasDashedBorder = topDashed || rightDashed || bottomDashed || leftDashed;

    bool topDouble = border.top is ExtendedBorderSide &&
        (border.top as ExtendedBorderSide).extendBorderStyle == CSSBorderStyleType.double;
    bool rightDouble = border.right is ExtendedBorderSide &&
        (border.right as ExtendedBorderSide).extendBorderStyle == CSSBorderStyleType.double;
    bool bottomDouble = border.bottom is ExtendedBorderSide &&
        (border.bottom as ExtendedBorderSide).extendBorderStyle == CSSBorderStyleType.double;
    bool leftDouble = border.left is ExtendedBorderSide &&
        (border.left as ExtendedBorderSide).extendBorderStyle == CSSBorderStyleType.double;

    hasDoubleBorder = topDouble || rightDouble || bottomDouble || leftDouble;
  }

  // Prefer dashed painter, then double painter, then general border painting
  // (with a special-case path for solid non-uniform widths + radius).
  if (hasDashedBorder) {
    _paintDashedBorder(canvas, rect, textDirection);
    renderStyle.target.ownerDocument.controller.reportFP();
  } else if (hasDoubleBorder) {
    _paintDoubleBorder(canvas, rect, textDirection);
    renderStyle.target.ownerDocument.controller.reportFP();
  } else if (_decoration.border != null) {
    final b = _decoration.border as Border;

    // Special-case: solid, uniform color, non-uniform widths with radius -> paint ring ourselves.
    if (_decoration.hasBorderRadius && _decoration.borderRadius != null) {
      final bool allSolid = b.top is ExtendedBorderSide &&
          b.right is ExtendedBorderSide &&
          b.bottom is ExtendedBorderSide &&
          b.left is ExtendedBorderSide &&
          (b.top as ExtendedBorderSide).extendBorderStyle == CSSBorderStyleType.solid &&
          (b.right as ExtendedBorderSide).extendBorderStyle == CSSBorderStyleType.solid &&
          (b.bottom as ExtendedBorderSide).extendBorderStyle == CSSBorderStyleType.solid &&
          (b.left as ExtendedBorderSide).extendBorderStyle == CSSBorderStyleType.solid;
      final bool sameColor = b.top.color == b.right.color && b.top.color == b.bottom.color && b.top.color == b.left.color;
      final bool nonUniformWidth = !(b.isUniform);
      final bool uniformWidthCheck = b.top.width == b.right.width && b.top.width == b.bottom.width && b.top.width == b.left.width;

      if (DebugFlags.enableBorderRadiusLogs) {
        try {
          final el = renderStyle.target;
          renderingLogger.finer('[BorderRadius] border solid/uniform checks on <${el.tagName.toLowerCase()}> '
              'allSolid=$allSolid sameColor=$sameColor b.isUniform=${b.isUniform} uniformWidth=$uniformWidthCheck '
              'w=[${b.left.width},${b.top.width},${b.right.width},${b.bottom.width}]');
        } catch (_) {}
      }
      if (allSolid && sameColor && nonUniformWidth) {
        _paintSolidNonUniformBorderWithRadius(canvas, rect, b);
        renderStyle.target.ownerDocument.controller.reportFP();
        return;
      }

      // New special-cases for solid borders with uniform width (possibly different colors):
      // - Circle (rounded-full): paint per-side centered 90° arcs.
      // - General rounded-rect (non-circle): paint each side with half-corner arcs + straight segment.
      if (allSolid && uniformWidthCheck && b.top.width > 0.0) {
        // Treat as a circle when the box is square and all corner radii are
        // at least half of the side (handles "rounded-full" like 9999px).
        bool isCircleByBorderRadius(BorderRadius br, Rect r, {double tol = 0.5}) {
          final double w = r.width;
          final double h = r.height;
          if ((w - h).abs() > tol) return false;
          final double half = w / 2.0;
          final List<Radius> corners = [br.topLeft, br.topRight, br.bottomRight, br.bottomLeft];
          for (final c in corners) {
            if (c.x + tol < half || c.y + tol < half) return false;
          }
          return true;
        }

        final BorderRadius br = _decoration.borderRadius!;
        final bool isCircle = isCircleByBorderRadius(br, rect);

        if (DebugFlags.enableBorderRadiusLogs) {
          try {
            final el = renderStyle.target;
            final r0 = br.topLeft; final r1 = br.topRight; final r2 = br.bottomRight; final r3 = br.bottomLeft;
            renderingLogger.finer('[BorderRadius] circle detect(solid) <${el.tagName.toLowerCase()}> '
                'w=${rect.width.toStringAsFixed(2)} h=${rect.height.toStringAsFixed(2)} '
                'tl=(${r0.x.toStringAsFixed(2)},${r0.y.toStringAsFixed(2)}) '
                'tr=(${r1.x.toStringAsFixed(2)},${r1.y.toStringAsFixed(2)}) '
                'br=(${r2.x.toStringAsFixed(2)},${r2.y.toStringAsFixed(2)}) '
                'bl=(${r3.x.toStringAsFixed(2)},${r3.y.toStringAsFixed(2)}) isCircle=$isCircle');
          } catch (_) {}
        }

        if (isCircle) {
          if (DebugFlags.enableBorderRadiusLogs) {
            try {
              final el = renderStyle.target;
              renderingLogger.finer('[BorderRadius] solid per-side circle border painter for <${el.tagName.toLowerCase()}>');
            } catch (_) {}
          }
          _paintSolidPerSideCircleBorder(canvas, rect, b);
          renderStyle.target.ownerDocument.controller.reportFP();
          return;
        } else if (!sameColor) {
          if (DebugFlags.enableBorderRadiusLogs) {
            try {
              final el = renderStyle.target;
              renderingLogger.finer('[BorderRadius] solid per-side rounded-rect painter for <${el.tagName.toLowerCase()}>');
            } catch (_) {}
          }
          _paintSolidPerSideRoundedRect(canvas, rect, b);
          renderStyle.target.ownerDocument.controller.reportFP();
          return;
        }
      }
    }

    // Fallback: use Flutter's Border.paint. Only pass radius when border is uniform.
    final BorderRadius? borderRadiusForPaint = b.isUniform ? _decoration.borderRadius : null;
    if (!b.isUniform && _decoration.borderRadius != null && DebugFlags.enableBorderRadiusLogs) {
      try {
        final el = renderStyle.target;
        renderingLogger.finer('[BorderRadius] skip passing radius to border painter for <${el.tagName.toLowerCase()}> '
            'due to non-uniform border');
      } catch (_) {}
    }

    if (DebugFlags.enableBorderRadiusLogs) {
      try {
        final el = renderStyle.target;
        renderingLogger.finer('[BorderRadius] call Flutter Border.paint for <${el.tagName.toLowerCase()}> '
            'uniform=${b.isUniform} passRadius=${borderRadiusForPaint != null}');
      } catch (_) {}
    }
    _decoration.border?.paint(
      canvas,
      rect,
      shape: _decoration.shape,
      borderRadius: borderRadiusForPaint,
      textDirection: configuration.textDirection,
    );
    renderStyle.target.ownerDocument.controller.reportFP();
  }

  _paintShadows(canvas, rect, textDirection);
}