使 CircleBorder 封装整个子部件

Make CircleBorder encapsulate the whole child widget

我正在尝试制作一个圆形的 ElevatedButton,但遇到格式问题:边框的大小似乎只考虑了子部件的高度,而不是宽度。为了说明这一点:

return ElevatedButton(
  style: ElevatedButton.styleFrom(
    shape: CircleBorder(),
  ),
  onPressed: () {},
  child: Text(
    "I want the circle border\nto encapsulate all the text"
  ),
);

产生这个结果:

我想弄清楚如何让圆形边框环绕整个文本,无需对任何固定大小进行硬编码或通过使用填充来破解它,因为我想要它能够响应文本内容和大小的变化。我该怎么做?

编辑:圆形边框是一个严格的要求,因此答案已更新。

使用 FloatingActionButton 完成工作

SizedBox(
      height: 200,
      width: 200,
      child: FloatingActionButton(
        onPressed: () {},
        child: const Text("I want the circle border\nto encapsulate all the text"),
      ),
    );

SizedBox 是给按钮尺寸所必需的。

旧答案

添加 StadiumBorder() 将解决您的问题。

ElevatedButton(
      style: ElevatedButton.styleFrom(
        shape: StadiumBorder(),
      ),
      onPressed: () {},
      child: Text(
        "I want the circle border\nto encapsulate all the text"
      ),
    );

感谢@pskink 的评论,解决方案是调整 CircleBorder 以在绘制边界路径时使用最长边而不是最短边。这是:

class EncapsulatingCircularBorder extends OutlinedBorder {
  /// Create a circle border.
  ///
  /// The [side] argument must not be null.
  const EncapsulatingCircularBorder({ BorderSide side = BorderSide.none }) : assert(side != null), super(side: side);

  @override
  EdgeInsetsGeometry get dimensions {
    return EdgeInsets.all(side.width);
  }

  @override
  ShapeBorder scale(double t) => EncapsulatingCircularBorder(side: side.scale(t));

  @override
  ShapeBorder? lerpFrom(ShapeBorder? a, double t) {
    if (a is EncapsulatingCircularBorder)
      return EncapsulatingCircularBorder(side: BorderSide.lerp(a.side, side, t));
    return super.lerpFrom(a, t);
  }

  @override
  ShapeBorder? lerpTo(ShapeBorder? b, double t) {
    if (b is EncapsulatingCircularBorder)
      return EncapsulatingCircularBorder(side: BorderSide.lerp(side, b.side, t));
    return super.lerpTo(b, t);
  }

  @override
  Path getInnerPath(Rect rect, { TextDirection? textDirection }) {

    return Path()
      ..addOval(Rect.fromCircle(
        center: rect.center,
        // Changed this from rect.shortestSide to longestSide
        radius: math.max(0.0, rect.longestSide / 2.0 - side.width),
      ));
  }

  @override
  Path getOuterPath(Rect rect, { TextDirection? textDirection }) {

    return Path()
      ..addOval(Rect.fromCircle(
        center: rect.center,
        // Changed this from rect.shortestSide to longestSide
        radius: rect.longestSide / 2.0,
      ));
  }

  @override
  EncapsulatingCircularBorder copyWith({ BorderSide? side }) {
    return EncapsulatingCircularBorder(side: side ?? this.side);
  }

  @override
  void paint(Canvas canvas, Rect rect, { TextDirection? textDirection }) {
    switch (side.style) {
      case BorderStyle.none:
        break;
      case BorderStyle.solid:
        canvas.drawCircle(rect.center, (rect.shortestSide - side.width) / 2.0, side.toPaint());
    }
  }

  @override
  bool operator ==(Object other) {
    if (other.runtimeType != runtimeType)
      return false;
    return other is EncapsulatingCircularBorder
        && other.side == side;
  }

  @override
  int get hashCode => side.hashCode;

  @override
  String toString() {
    return '${objectRuntimeType(this, 'EncapsulatingCircularBorder')}($side)';
  }
}