flutter canvas及實現氣泡
阿新 • • 發佈:2020-12-18
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"),),
)