玖叶教程网

前端编程开发入门

Flutter自定义一个环形菜单(flutter 自定义按钮)

功能不完善,只是单纯的实现功能,作为记录,仅供参考:


import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:lifeline/utils/hex_color_utils.dart';

class RingComponent extends StatelessWidget {
  final double radius;
  final Function onTap;

  RingComponent({ required this.radius, required this.onTap });

  @override
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.center,
      children: [
        SizedBox(
          width: radius * 2,
          height: radius * 2,
          child: CustomPaint(
            painter: RingPainter(width: radius*0.6, radius: radius),
          ),
        ),
        SizedBox(
          width: radius * 0.9,
          height: radius * 0.9,
          child: FloatingActionButton(
            onPressed: () {onTap();},
            mini: false,
            materialTapTargetSize: MaterialTapTargetSize.padded,
            child: const Icon(
              Icons.sticky_note_2,
              color: Colors.white,
            ),
          ),
        ),
      ],
    );
  }
}

class RingPainter extends CustomPainter {
  final double width;
  final double radius;

  RingPainter({required this.width, required this.radius});

  @override
  void paint(Canvas canvas, Size size) {
    final rect = Rect.fromCircle(
        center: size.center(Offset.zero), radius: size.width / 2);
    canvas.translate(20.w, -15.w);

    canvas.save();
    canvas.rotate(math.pi / 9);

    const startAngle = -math.pi / 2;
    const sweepAngle = math.pi * 2 / 3;

    final innerRadius = size.width / 2 - width / 2;
    final outerRadius = size.width / 2 + width / 2;

    final innerPoint1 =
        calculatePointOnRing(size, startAngle + sweepAngle, innerRadius);
    final innerPoint2 =
        calculatePointOnRing(size, startAngle + 2 * sweepAngle, innerRadius);
    final innerPoint3 =
        calculatePointOnRing(size, startAngle + 3 * sweepAngle, innerRadius);

    final outerPoint1 =
        calculatePointOnRing(size, startAngle + sweepAngle, outerRadius);
    final outerPoint2 =
        calculatePointOnRing(size, startAngle + 2 * sweepAngle, outerRadius);
    final outerPoint3 =
        calculatePointOnRing(size, startAngle + 3 * sweepAngle, outerRadius);

    final colors = [HexColor("516fd5"), HexColor("f8df2d"), HexColor("f2eeeb")];
    final texts = ['Intervention', 'Plan', 'Reflection'];

    final paint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = width;

    for (int i = 0; i < colors.length; i++) {
      paint.color = colors[i];
      final start = startAngle + i * sweepAngle;
      const sweep = sweepAngle;
      canvas.drawArc(rect, start, sweep, false, paint);
    }

    final arrowBluePaint = Paint()
      ..style = PaintingStyle.fill
      ..isAntiAlias = true
      ..color = HexColor("516fd5");

    final arrowBluePath = Path();
    arrowBluePath.moveTo(innerPoint1.dx + 0.6, innerPoint1.dy);
    arrowBluePath.lineTo(outerPoint1.dx, outerPoint1.dy - 0.4);
    arrowBluePath.lineTo(
        innerPoint1.dx, innerPoint1.dy + math.sqrt(1 / 2) * width + 5);
    arrowBluePath.close();
    canvas.drawPath(arrowBluePath, arrowBluePaint);

    final arrowYellowPaint = Paint()
      ..style = PaintingStyle.fill
      ..isAntiAlias = true
      ..color = HexColor("f8df2d");

    final arrowYellowPath = Path();
    arrowYellowPath.moveTo(innerPoint2.dx + 0.2, innerPoint2.dy);
    arrowYellowPath.lineTo(outerPoint2.dx, outerPoint2.dy + 0.4);
    arrowYellowPath.lineTo(
        outerPoint2.dx, outerPoint2.dy - math.sqrt(1 / 2) * width - 5);
    arrowYellowPath.close();
    canvas.drawPath(arrowYellowPath, arrowYellowPaint);

    final arrowGrayPaint = Paint()
      ..style = PaintingStyle.fill
      ..isAntiAlias = true
      ..color = HexColor("f2eeeb");

    final arrowGrayPath = Path();
    arrowGrayPath.moveTo(innerPoint3.dx, innerPoint3.dy);
    arrowGrayPath.lineTo(outerPoint3.dx, outerPoint3.dy);
    arrowGrayPath.lineTo(
        0.6 * width + outerPoint3.dx, outerPoint3.dy + 1 / 2 * width + 5);
    arrowGrayPath.close();
    canvas.drawPath(arrowGrayPath, arrowGrayPaint);

    // draw text
    canvas.restore();
    final textPainter = TextPainter(
      textAlign: TextAlign.center,
      textDirection: TextDirection.ltr,
    );
    for (int i = 0; i < texts.length; i++) {
      final textSpan = TextSpan(
        text: texts[i],
        style: TextStyle(fontSize: 10.sp, color: Colors.grey),
      );

      textPainter.text = textSpan;
      textPainter.layout();
      double x;
      double y;
      if (i == 2) {
        //reflection
        x = (outerPoint1.dx + outerPoint2.dx) / 2 -
            textPainter.width / 2 -
            radius * 0.8;
        y = (outerPoint1.dy + outerPoint2.dy) / 2 +
            radius * 0.5 -
            textPainter.height / 2;
      } else if (i == 0) {
        //intervention
        x = (outerPoint2.dx + outerPoint3.dx) / 2 -
            textPainter.width / 2 +
            radius * 0.1;
        y = (outerPoint2.dy + outerPoint3.dy) / 2 +
            radius * 0.5 -
            textPainter.height / 2 -
            radius * 0.8;
      } else {
        //plan
        x = (outerPoint3.dx + outerPoint1.dx) / 2 - textPainter.width / 2;
        y = (outerPoint3.dy + outerPoint1.dy) / 2 +
            radius * 0.7 -
            textPainter.height / 2;
      }

      final textOffset = Offset(x, y);

      textPainter.paint(canvas, textOffset);
    }
  }

  Offset calculatePointOnRing(Size size, double angle, double radius) {
    final centerX = size.width / 2;
    final centerY = size.height / 2;

    final x = centerX + math.cos(angle) * radius;
    final y = centerY + math.sin(angle) * radius;

    return Offset(x, y);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言