borderRadius property

  1. @override
List<Radius>? get borderRadius
override

Implementation

@override
List<Radius>? get borderRadius {
  final TextDirection currentDirection = direction;
  final CSSWritingMode currentWritingMode = writingMode;

  CSSBorderRadius? tl = _borderTopLeftRadius;
  CSSBorderRadius? tr = _borderTopRightRadius;
  CSSBorderRadius? br = _borderBottomRightRadius;
  CSSBorderRadius? bl = _borderBottomLeftRadius;

  void applyLogical(CSSBorderRadius? logical, int physicalCorner) {
    if (logical == null) return;
    switch (physicalCorner) {
      case 0:
        tl ??= logical;
        break;
      case 1:
        tr ??= logical;
        break;
      case 2:
        br ??= logical;
        break;
      case 3:
        bl ??= logical;
        break;
    }
  }

  applyLogical(_borderStartStartRadius, _resolveStartStartCorner(currentDirection, currentWritingMode));
  applyLogical(_borderStartEndRadius, _resolveStartEndCorner(currentDirection, currentWritingMode));
  applyLogical(_borderEndStartRadius, _resolveEndStartCorner(currentDirection, currentWritingMode));
  applyLogical(_borderEndEndRadius, _resolveEndEndCorner(currentDirection, currentWritingMode));

  // Fast path: if all corners are zero, no radii.
  final CSSBorderRadius tlResolved = tl ?? CSSBorderRadius.zero;
  final CSSBorderRadius trResolved = tr ?? CSSBorderRadius.zero;
  final CSSBorderRadius brResolved = br ?? CSSBorderRadius.zero;
  final CSSBorderRadius blResolved = bl ?? CSSBorderRadius.zero;

  final bool hasAnyRadius = !(tlResolved == CSSBorderRadius.zero &&
      trResolved == CSSBorderRadius.zero &&
      brResolved == CSSBorderRadius.zero &&
      blResolved == CSSBorderRadius.zero);
  if (!hasAnyRadius) return null;

  // Cache to avoid recomputing per paint phase.
  // When any axis uses percentages, the result depends on the border box size.
  final bool tlPct = tlResolved.x.isPercentage || tlResolved.y.isPercentage;
  final bool trPct = trResolved.x.isPercentage || trResolved.y.isPercentage;
  final bool brPct = brResolved.x.isPercentage || brResolved.y.isPercentage;
  final bool blPct = blResolved.x.isPercentage || blResolved.y.isPercentage;
  final bool anyPct = tlPct || trPct || brPct || blPct;

  final double? bw = borderBoxWidth ?? borderBoxLogicalWidth;
  final double? bh = borderBoxHeight ?? borderBoxLogicalHeight;

  // Reuse cached radii when inputs are identical and the size anchor (for %) is unchanged.
  if (_cachedComputedBorderRadius != null &&
      _cachedBorderRadiusDirection == currentDirection &&
      _cachedBorderRadiusWritingMode == currentWritingMode &&
      identical(_cachedTLRef, _borderTopLeftRadius) &&
      identical(_cachedTRRef, _borderTopRightRadius) &&
      identical(_cachedBRRef, _borderBottomRightRadius) &&
      identical(_cachedBLRef, _borderBottomLeftRadius) &&
      identical(_cachedStartStartRef, _borderStartStartRadius) &&
      identical(_cachedStartEndRef, _borderStartEndRadius) &&
      identical(_cachedEndStartRef, _borderEndStartRadius) &&
      identical(_cachedEndEndRef, _borderEndEndRadius) &&
      (!anyPct || (_cachedBorderRadiusW == bw && _cachedBorderRadiusH == bh))) {
    return _cachedComputedBorderRadius;
  }

  final radii = <Radius>[
    tlResolved.computedRadius,
    trResolved.computedRadius,
    brResolved.computedRadius,
    blResolved.computedRadius,
  ];

  _cachedComputedBorderRadius = radii;
  _cachedBorderRadiusW = anyPct ? bw : _cachedBorderRadiusW; // only bind size when needed
  _cachedBorderRadiusH = anyPct ? bh : _cachedBorderRadiusH;
  _cachedTLRef = _borderTopLeftRadius;
  _cachedTRRef = _borderTopRightRadius;
  _cachedBRRef = _borderBottomRightRadius;
  _cachedBLRef = _borderBottomLeftRadius;
  _cachedStartStartRef = _borderStartStartRadius;
  _cachedStartEndRef = _borderStartEndRadius;
  _cachedEndStartRef = _borderEndStartRadius;
  _cachedEndEndRef = _borderEndEndRadius;
  _cachedBorderRadiusDirection = currentDirection;
  _cachedBorderRadiusWritingMode = currentWritingMode;

  if (DebugFlags.enableBorderRadiusLogs) {
    try {
      final el = target;
      renderingLogger.finer('[BorderRadius] compute for <${el.tagName.toLowerCase()}> '
          'borderBox=${(bw)?.toStringAsFixed(2) ?? 'null'}×${(bh)?.toStringAsFixed(2) ?? 'null'} '
          'tl=(${radii[0].x.toStringAsFixed(2)},${radii[0].y.toStringAsFixed(2)}) '
          'tr=(${radii[1].x.toStringAsFixed(2)},${radii[1].y.toStringAsFixed(2)}) '
          'br=(${radii[2].x.toStringAsFixed(2)},${radii[2].y.toStringAsFixed(2)}) '
          'bl=(${radii[3].x.toStringAsFixed(2)},${radii[3].y.toStringAsFixed(2)})');
    } catch (_) {}
  }

  return radii;
}