flutter canvas及实现气泡

canvas了解:
Flutter -canvas 使用画布和路径绘制自定义形状和线条曲线
Flutter:成为Canvas绘制大师(一)

Flutter:成为Canvas绘制大师(二)

Flutter:成为Canvas绘制大师(三)
Flutter 气泡效果合集(全网最全)

以下是我根据上面链接改写的气泡:项目需要气泡的角度是在底部正中间:
效果:
在这里插入图片描述

思路:四个弯角利用drawArc画圆弧,之间的横竖线利用drawline画线,小尖叫也是利用drawline实现;


import 'dart:math';

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

enum BubbleArrowDirection { top, bottom, right, left, topLeft }

class BubbleWidget extends StatelessWidget {
  // 尖角位置
  final position;

  // 尖角高度
  var arrHeight;

  // 尖角角度
  var arrAngle;

  // 圆角半径
  var radius;

  // 宽度
  final width;

  // 高度
  final height;

  // 边距
  double length;

  // 颜色
  Color color;

  // 边框颜色
  Color borderColor;

  // 边框宽度
  final strokeWidth;

  // 填充样式
  final style;

  // 子 Widget
  final child;

  // 子 Widget 与起泡间距
  var innerPadding;

  BubbleWidget(
      this.width,
      this.height,
      this.color ,
      this.position, {
        Key key,
        this.length = 1,
        this.arrHeight = 12.0,
        this.arrAngle = 60.0,
        this.radius = 10.0,
        this.strokeWidth = 4.0,
        this.style = PaintingStyle.fill,
        this.borderColor,
        this.child,
        this.innerPadding = 6.0,
      }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    if (style == PaintingStyle.stroke && borderColor == null) {
      // borderColor = color;
    }
    if (arrAngle < 0.0 || arrAngle >= 180.0) {
      arrAngle = 60.0;
    }
    if (arrHeight < 0.0) {
      arrHeight = 0.0;
    }
    if (radius < 0.0 || radius > width * 0.5 || radius > height * 0.5) {
      radius = 0.0;
    }
    if (position == BubbleArrowDirection.top ||
        position == BubbleArrowDirection.bottom) {
      if (length < 0.0 || length >= width - 2 * radius) {
        length = width * 0.5 - arrHeight * tan(_angle(arrAngle * 0.5)) - radius;
      }
    } else {
      if (length < 0.0 || length >= height - 2 * radius) {
        length =
            height * 0.5 - arrHeight * tan(_angle(arrAngle * 0.5)) - radius;
      }
    }
    if (innerPadding < 0.0 ||
        innerPadding >= width * 0.5 ||
        innerPadding >= height * 0.5) {
      innerPadding = 2.0;
    }
    Widget bubbleWidget;
    if (style == PaintingStyle.fill) {
      bubbleWidget = Container(
          width: width,
          height: height,
          child: Stack(children: <Widget>[
            CustomPaint(
                painter: BubbleCanvas(context, width, height, color, position,
                    arrHeight, arrAngle, radius, strokeWidth, style, length)),
            CustomPaint(
                painter: BubbleCanvas(
                    context,
                    width,
                    height,
                    borderColor,
                    position,
                    arrHeight,
                    arrAngle,
                    radius,
                    strokeWidth,
                    PaintingStyle.stroke,
                    length)),
            _paddingWidget()
          ]));
    } else {
      bubbleWidget = Container(
          width: width,
          height: height,
          child: Stack(children: <Widget>[
            CustomPaint(
                painter: BubbleCanvas(
                    context,
                    width,
                    height,
                    color,
                    position,
                    arrHeight,
                    arrAngle,
                    radius,
                    strokeWidth,
                    PaintingStyle.fill,
                    length)),
            CustomPaint(
                painter: BubbleCanvas(
                    context,
                    width,
                    height,
                    borderColor,
                    position,
                    arrHeight,
                    arrAngle,
                    radius,
                    strokeWidth,
                    PaintingStyle.stroke,
                    length)),
            _paddingWidget()
          ]));
    }
    return bubbleWidget;
  }

  Widget _paddingWidget() {
    return Padding(
        padding: EdgeInsets.only(
            top: (position == BubbleArrowDirection.top)
                ? arrHeight + innerPadding
                : innerPadding,
            right: (position == BubbleArrowDirection.right)
                ? arrHeight + innerPadding
                : innerPadding,
            bottom: (position == BubbleArrowDirection.bottom)
                ? arrHeight + innerPadding
                : innerPadding,
            left: (position == BubbleArrowDirection.left)
                ? arrHeight + innerPadding
                : innerPadding),
        child: Center(child: this.child));
  }
}

class BubbleCanvas extends CustomPainter {
  BuildContext context;
  final position;
  final arrHeight;
  final arrAngle;
  final radius;
  final width;
  final height;
  final length;
  final color;
  final strokeWidth;
  final style;

  BubbleCanvas(
      this.context,
      this.width,
      this.height,
      this.color,
      this.position,
      this.arrHeight,
      this.arrAngle,
      this.radius,
      this.strokeWidth,
      this.style,
      this.length);

  @override
  void paint(Canvas canvas, Size size) {
    Path path = Path();
    path.arcTo(
        Rect.fromCircle(
            center: Offset(
                (position == BubbleArrowDirection.left)
                    ? radius + arrHeight
                    : radius,
                (position == BubbleArrowDirection.top)
                    ? radius + arrHeight
                    : radius),
            radius: radius),
        pi,
        pi * 0.5,
        false);//画左上半弧
    if (position == BubbleArrowDirection.top) {
      path.lineTo(length + radius, arrHeight);
      path.lineTo(
          length + radius + arrHeight * tan(_angle(arrAngle * 0.5)), 0.0);
      path.lineTo(length + radius + arrHeight * tan(_angle(arrAngle * 0.5)) * 2,
          arrHeight);
    }
    path.lineTo(
        (position == BubbleArrowDirection.right)
            ? width - radius - arrHeight
            : width - radius,
        (position == BubbleArrowDirection.top) ? arrHeight : 0.0);//画top横线
    path.arcTo(
        Rect.fromCircle(
            center: Offset(
                (position == BubbleArrowDirection.right)
                    ? width - radius - arrHeight
                    : width - radius,
                (position == BubbleArrowDirection.top)
                    ? radius + arrHeight
                    : radius),
            radius: radius),
        -pi * 0.5,
        pi * 0.5,
        false);//画右上弧度
    if (position == BubbleArrowDirection.right) {
      path.lineTo(width - arrHeight, length + radius);
      path.lineTo(
          width, length + radius + arrHeight * tan(_angle(arrAngle * 0.5)));
      path.lineTo(width - arrHeight,
          length + radius + arrHeight * tan(_angle(arrAngle * 0.5)) * 2);
    }
    path.lineTo(
        (position == BubbleArrowDirection.right) ? width - arrHeight : width,
        (position == BubbleArrowDirection.bottom)
            ? height - radius - arrHeight
            : height - radius);//画右横线
    path.arcTo(
        Rect.fromCircle(
            center: Offset(
                (position == BubbleArrowDirection.right)
                    ? width - radius - arrHeight
                    : width - radius,
                (position == BubbleArrowDirection.bottom)
                    ? height - radius - arrHeight
                    : height - radius),
            radius: radius),
        pi * 0,
        pi * 0.5,
        false);//画右下弧
    if (position == BubbleArrowDirection.bottom) {
      path.lineTo(width/2+arrHeight*tan(_angle(arrAngle * 0.5)), height - arrHeight);//画右下斜边
      path.lineTo(
          width/2,
          height);//最低点
      path.lineTo(
          width/2-arrHeight*tan(_angle(arrAngle * 0.5)),
          height - arrHeight);//左下斜边
    }
    path.lineTo(
        (position == BubbleArrowDirection.left) ? radius + arrHeight : radius,
        (position == BubbleArrowDirection.bottom)
            ? height - arrHeight
            : height);
    path.arcTo(
        Rect.fromCircle(
            center: Offset(
                (position == BubbleArrowDirection.left)
                    ? radius + arrHeight
                    : radius,
                (position == BubbleArrowDirection.bottom)
                    ? height - radius - arrHeight
                    : height - radius),
            radius: radius),
        pi * 0.5,
        pi * 0.5,
        false);//画左下弧
    if (position == BubbleArrowDirection.left) {
      path.lineTo(arrHeight, height - radius - length);
      path.lineTo(0.0,
          height - radius - length - arrHeight * tan(_angle(arrAngle * 0.5)));
      path.lineTo(
          arrHeight,
          height -
              radius -
              length -
              arrHeight * tan(_angle(arrAngle * 0.5)) * 2);
    }
    path.lineTo((position == BubbleArrowDirection.left) ? arrHeight : 0.0,
        (position == BubbleArrowDirection.top) ? radius + arrHeight : radius);//画左横线
    // path.fillType();
    path.close();//闭合
    // Paint paint1=new Paint();
    canvas.drawPath(
        path,
        Paint()
        ..color=color
          ..style = style
          ..strokeCap = StrokeCap.round
          ..strokeWidth = strokeWidth);
  }

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

double _angle(angle) {
  return angle * pi / 180;
}

使用:

Center(
          child: BubbleWidget(100.0,40.0,Colors.orange,BubbleArrowDirection.bottom,radius: 5.0,strokeWidth: 4.0,borderColor: Colors.blueAccent
            ,child: Text("dddd"),),
        )
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值