自定义形状的Flutter按钮 - (三角形)

19

我创建了一个圆形按钮,这里使用的是RawMaterialButton,现在我想使用CustomPaint在其中央创建一个三角形,但是它显示ClearButton类未定义ShapesPainter。我尝试过其他按钮,但没有一个可以接受ShapesPainter

RawMaterialButton(
          child: CustomPaint(
            painter: ShapesPainter(),
            child: Container(
              height: 40,
            ),
          ),
          onPressed: onPressed,
          constraints: BoxConstraints.tightFor(
            width: 90.0,
            height: 90.0,
          ),
          shape: RoundedRectangleBorder(),
          fillColor: Colors.transparent,
        )
哪种按钮类型应该与ShapesPainter一起使用,或者我如何创建一个中心带有三角形或其他形状的圆形按钮?
这是我正在尝试创建的按钮,正如您所看到的,它基本上是一个带有三角形的圆形按钮。 enter image description here

尝试使用CurvePainter()函数。 - Tinus Jackson
所以,您想在圆形按钮内创建自定义形状(三角形或其他)吗? - Jigar
@Tinus Jackson 用CurvePainter()画一个三角形?如果它能工作的话,那将会很奇怪。 - Hasen
@Jigar 为什么,有什么问题吗? - Hasen
@Hasen,不是的,没有错,只是想让它更明确。 - Jigar
显示剩余5条评论
5个回答

40

通过创建自己的自定义绘图实现,您可以完成它。

三角形绘图器

class TrianglePainter extends CustomPainter {
  final Color strokeColor;
  final PaintingStyle paintingStyle;
  final double strokeWidth;

  TrianglePainter({this.strokeColor = Colors.black, this.strokeWidth = 3, this.paintingStyle = PaintingStyle.stroke});

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = strokeColor
      ..strokeWidth = strokeWidth
      ..style = paintingStyle;

    canvas.drawPath(getTrianglePath(size.width, size.height), paint);
  }

  Path getTrianglePath(double x, double y) {
    return Path()
      ..moveTo(0, y)
      ..lineTo(x / 2, 0)
      ..lineTo(x, y)
      ..lineTo(0, y);
  }

  @override
  bool shouldRepaint(TrianglePainter oldDelegate) {
    return oldDelegate.strokeColor != strokeColor ||
        oldDelegate.paintingStyle != paintingStyle ||
        oldDelegate.strokeWidth != strokeWidth;
  }
}

使用方法

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RawMaterialButton(
          onPressed: () {},
          child: CustomPaint(
            painter: TrianglePainter(
              strokeColor: Colors.blue,
              strokeWidth: 10,
              paintingStyle: PaintingStyle.fill,
            ),
            child: Container(
              height: 180,
              width: 200,
            ),
          ),
        ),
      ),
    );
  }
}

“strokeWidth” 参数似乎没有改变任何东西? - Hasen
目前只有在将绘画样式更改为“PaintingStyle.stroke”时才能正常工作。 - Ajil O.
好的,只是备注一下,那个参数似乎是不必要的。 - Hasen
1
如果您希望为绘制的每个形状添加手势回调,则可以使用touchable库:https://github.com/nateshmbhat/touchable - Natesh bhat
现在RawMaterialButton()已经过时了,但是可以轻松地用GestureDetector()替换。 - cmaxetom

13

创建自定义剪裁路径

class CustomTriangleClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    final path = Path();
    path.lineTo(size.width, 0);
    path.lineTo(size.width, size.height);
    path.lineTo(0, 0);
    return path;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) {
    return false;
  }
}

然后使用:

ClipPath(
    clipper: CustomTriangleClipper(),
    child: Container(
      width: 50,
      height: 50,
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.bottomLeft,
          end: Alignment.topRight,
          colors: [Color(0xffF25D50), Color(0xffF2BB77)],
        ),
      ),
    ),
  );

1
以下是您可以参考的代码。
class MyButton extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Offset centerPoint = Offset(100, 100);
    double radius = 60;
    double triangleA = 35;   // this the dimension of triangle's side
    double triangleR = triangleA / sqrt(3.0);   // this the distance between the center of triangle/circle to corner of triangle

    // I am drawing here circle, while you can draw your shape as per your convenience.
    canvas.drawCircle(
        centerPoint,
        radius,
        Paint()
          ..color = Colors.grey[700]
          ..style = PaintingStyle.fill);

    Path path = Path();

    double x1Point = centerPoint.dx + triangleR * cos(3 * pi / 2);
    double y1Point = centerPoint.dy + triangleR * sin(3 * pi / 2);
    path.moveTo(x1Point, y1Point);

    double x2Point = centerPoint.dx +
        triangleR * cos((3 * pi / 2) - Angle.fromDegrees(120).radians);
    double y2Point = centerPoint.dy +
        triangleR * sin((3 * pi / 2) - Angle.fromDegrees(120).radians);
    path.lineTo(x2Point, y2Point);

    double x3Point = centerPoint.dx +
        triangleR * cos((3 * pi / 2) + Angle.fromDegrees(120).radians);
    double y3Point = centerPoint.dy +
        triangleR * sin((3 * pi / 2) + Angle.fromDegrees(120).radians);
    path.lineTo(x3Point, y3Point);

    path.close();

    canvas.drawPath(
        path,
        Paint()
          ..color = Colors.deepOrange
          ..style = PaintingStyle.fill);

    canvas.save();
    canvas.restore();
  }

RawMaterialButton(
 child: CustomPaint(
   painter: MyButton(),
   child: GestureDetector(
     onTap: () {
        print('Here, you can handle button click event');
     },
   ),
 ),
 onPressed: () {
 },
)

1
看起来需要导入 import 'dart:math';,还需要安装 angles 包?另外我收到了一个错误信息 “Missing concrete implementation of CustomPainter.shouldRepaint.”。 - Hasen
@Hasen,你不需要这些包。人们在复制代码之前应该更多地考虑代码本身,这个数学问题很简单,完全可以不用计算器来解决:cosine of 3π/2 = 0,因此 double x1Point = centerPoint.dx,而sine of 3π/2 = -1,则得到 double y1Point = centerPoint.dy - triangleR - Dewaeq

0

这是我的版本,可以指定旋转和颜色

class TrianglePainter extends CustomPainter {
  double sideSize;

  Color color;
  TrianglePainter({required this.sideSize, required this.color});

  @override
  void paint(Canvas canvas, Size size) {
    double ySize = sideSize * cos(30 * pi / 180);
    double xSize = sideSize;

    double point0x = xSize / 2;
    double point0y = ySize / 2;

    double point1x = -xSize / 2;
    double point1y = ySize / 2;

    double point2x = 0;
    double point2y = -ySize / 2;

    Path path = Path();
    path.moveTo(point0x, point0y);
    path.lineTo(point1x, point1y);
    path.lineTo(point2x, point2y);
    path.lineTo(point0x, point0y);
    path.close();
    canvas.drawPath(
        path,
        Paint()
          ..color = color
          ..style = PaintingStyle.fill);

    canvas.save();
    canvas.restore();
  }

  @override
  bool shouldRepaint(TrianglePainter oldDelegate) {
    return oldDelegate.color != color || oldDelegate.sideSize != sideSize;
  }
}

Widget triangle(double sideSize, Color color, double angle) {
  return Transform.rotate(
      child: CustomPaint(
        painter: TrianglePainter(
          color: color,
          sideSize: sideSize,
        ),
      ),
      angle: angle * pi / 180);
}

0

这个星形是使用ClipPath创建的

import 'dart:math';
import 'package:flutter/material.dart';

class MyClipPath extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ClipPath(
        clipper: MyClipper(MediaQuery.of(context).size),
        child: Container(
          color: Colors.green,
        ),
      ),
    );
  }
}

class MyClipper extends CustomClipper<Path> {
  MyClipper(this.containerSize);
  Size containerSize;
  @override
  Path getClip(Size size) {
    var path = Path();

    double x = containerSize.width / 2;
    double y = 0;
    double radian = 0;
    double step = min(containerSize.width, containerSize.height / cos(0.1 * pi)) / pow(2 * sin(.3 * pi), 2);
    path.moveTo(x, y);
    for (var i = 0; i < 10; i++) {
      if (i == 0) {
        radian = .6 * pi;
        print(50 * 4 * pow(sin(0.3 * pi), 2) * cos(0.1 * pi));
      } else if (i.isEven) {
        radian += 1.2 * pi;
      } else {
        radian += 0.4 * pi;
      }
      x += step * cos(radian);
      y += step * sin(radian);
      path.lineTo(x, y);
    }
    return path;
  }

  @override
  bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
    return false;
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接