原理:
先画一个三角形再画一个圆角矩形,然后把两个图案重合起来就实现 了一个聊天气泡
实现代码bubble.dart
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
import 'package:flutter/material.dart';
const _ArrowWidth = 7.0; //箭头宽度
const _ArrowHeight = 10.0; //箭头高度
const _MinHeight = 32.0; //内容最小高度
const _MinWidth = 50.0; //内容最小宽度
class Bubble extends StatelessWidget {
final BubbleDirection direction;
final Radius borderRadius;
final Widget child;
final BoxDecoration decoration;
final Color color;
final _left;
final _right;
final EdgeInsetsGeometry padding;
final EdgeInsetsGeometry margin;
final BoxConstraints constraints;
final double width;
final double height;
final Alignment alignment;
const Bubble(
{Key key,
this.direction = BubbleDirection.left,
this.borderRadius,
this.child, this.decoration, this.color, this.padding, this.margin, this.constraints, this.width, this.height, this.alignment})
: _left = direction == BubbleDirection.left ? _ArrowWidth : 0.0,
_right = direction == BubbleDirection.right ? _ArrowWidth : 0.0,
super(key: key);
@override
Widget build(BuildContext context) {
return ClipPath(
clipper:
_BubbleClipper(direction, this.borderRadius ?? Radius.circular(5.0)),
child: Container(
alignment: this.alignment,
width: this.width,
height: this.height,
constraints: (this.constraints??BoxConstraints()).copyWith(minHeight: _MinHeight,minWidth: _MinWidth),
margin: this.margin,
decoration: this.decoration,
color: this.color,
padding: EdgeInsets.fromLTRB(this._left, 0.0, this._right, 0.0).add(this.padding??EdgeInsets.fromLTRB(7.0, 5.0, 7.0, 5.0)),
child: this.child,
),
);
}
}
///方向
enum BubbleDirection { left, right }
class _BubbleClipper extends CustomClipper<Path> {
final BubbleDirection direction;
final Radius radius;
_BubbleClipper(this.direction, this.radius);
@override
Path getClip(Size size) {
final path = Path();
final path2 = Path();
final centerPoint = (size.height / 2).clamp(_MinHeight/2, _MinHeight/2);
print(centerPoint);
if (this.direction == BubbleDirection.left) {
//绘制左边三角形
path.moveTo(0, centerPoint);
path.lineTo(_ArrowWidth, centerPoint - _ArrowHeight/2);
path.lineTo(_ArrowWidth, centerPoint + _ArrowHeight/2);
path.close();
//绘制矩形
path2.addRRect(RRect.fromRectAndRadius(
Rect.fromLTWH(_ArrowWidth, 0, (size.width - _ArrowWidth), size.height), this.radius));
//合并
path.addPath(path2, Offset(0, 0));
} else {
//绘制右边三角形
path.moveTo(size.width, centerPoint);
path.lineTo(size.width - _ArrowWidth, centerPoint - _ArrowHeight/2);
path.lineTo(size.width - _ArrowWidth, centerPoint + _ArrowHeight/2);
path.close();
//绘制矩形
path2.addRRect(RRect.fromRectAndRadius(
Rect.fromLTWH(0, 0, (size.width - _ArrowWidth), size.height), this.radius));
//合并
path.addPath(path2, Offset(0, 0));
}
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return false;
}
}
使用:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
Bubble(
color: Colors.purple,
child: Text(
"你6666",
style: TextStyle(color: Colors.white),
),
)
效果: