??????小菜在學習時需要用到氣泡效果,為了更加靈活背伴,小菜封裝了一個簡單的 flutter_bubble 氣泡插件,方便日常的使用;
??????小菜準備用 Canvas 的 drawPath 進行繪制烁试,主要分為三個部分,圓角弧線拢肆,普通直線减响,尖角折線,均可由 drawPath 自帶方法繪制郭怪;小菜以前整理過關(guān)于 Canvas 繪制的小博客支示,實現(xiàn)很簡單;
??????小菜繪制了一個簡陋的原型圖鄙才,整體黑框為 Bubble Widget 整體范圍悼院;藍色圓弧為圓角位置;紅色尖角可根據(jù)上下左右參數(shù)進行配置咒循,且只可展示一個据途,尖角的高度和角度可自由配置,當確定一個尖角位置時叙甸,其余三個方向?qū)捀哐由斓胶诳虿糠钟币剑欢染€則是連接圓角與尖角等直線;中間空余部分為子 Widget 位置裆蒸;Tips: Child Widget 寬高小于等于 Bubble Widget熔萧;
繪制圓角
??????首先在邊角處繪制四個圓弧,直接用 arcTo 即可僚祷,需要注意的是:小菜整體以 drawPath 方式實現(xiàn)佛致,準備從左上角開始順時針繪制,所以繪制圓弧時也是順時針方向辙谜;
void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) {
assert(_rectIsValid(rect));
_arcTo(rect.left, rect.top, rect.right, rect.bottom, startAngle, sweepAngle, forceMoveTo);
}
??????小菜理解俺榆,Rect 為繪制圓角的矩形,包括位置及大凶岸摺罐脊;startAngele 為起始角度;sweepAngle 為繪制弧形角度蜕琴;小菜需要的四個圓弧大小均為 pi/2萍桌,只需調(diào)整矩形位置與起始角度即可;
// 逆時針
canvas.drawPath(
Path()
..addArc(Rect.fromCircle(center: Offset(60.0, 60.0), radius: 60.0), 0.0, -pi / 2)
..lineTo(0.0, 0.0), paints);
canvas.drawCircle(Offset(120.0, 60.0), 5, paints..color = Colors.indigoAccent);
canvas.drawCircle(Offset(0.0, 0.0), 5, paints..color = Colors.orange);
// 順時針
canvas.drawPath(
Path()
..addArc(Rect.fromCircle(center: Offset(60.0, 180.0), radius: 60.0), -pi / 2, pi / 2)
..lineTo(0.0, 120.0), paints..color = Colors.green);
canvas.drawCircle(Offset(60.0, 120.0), 5, paints..color = Colors.indigoAccent);
canvas.drawCircle(Offset(0.0, 120.0), 5, paints..color = Colors.orange);
繪制尖角
??????其次繪制尖角凌简,小菜的尖角是由 lineTo 兩段直線拼接起來的上炎,只需要處理起點與終點即可;小菜為了更加靈活雏搂,可以設置尖角高度與尖角角度(0 ~ 180)藕施,通過三角函數(shù)進行計算;
path.lineTo(arrHeight * tan(_angle(arrAngle * 0.5)), 0.0);
path.lineTo(arrHeight * tan(_angle(arrAngle * 0.5)) * 2, arrHeight);
繪制連線
??????最后就是將處理好的連接起來畔派,小菜為了適應更多場景铅碍,尖角位置也可自由配置,長度為到圓角的距離线椰,默認為邊框中間位置胞谈;
- 尖角在頂部時,距離為左上圓角結(jié)束點邊距憨愉;
- 尖角在右側(cè)時烦绳,距離為右上圓角結(jié)束點邊距;
- 尖角在底部時配紫,距離為右下圓角結(jié)束點邊距径密;
- 尖角在左側(cè)時,距離為左下圓角結(jié)束點邊距躺孝;
整體分析
??????小菜將配置邏輯編輯好發(fā)布到 Pub 庫享扔,基本 BubbleWidget 便完成底桂,簡單分析一下可配置項;
BubbleWidget(
this.width, // 整體高度惧眠,并非 Child Widget 寬度
this.height, // 整體高度籽懦,并非 Child Widget 高度
this.color, // 填充顏色,borderColor==null 時也為邊框顏色
this.position, { // 尖角位置(上下左右)
Key key,
this.length = -1.0, // 尖角距離圓角結(jié)束點邊距氛魁,默認為中點
this.arrHeight = 12.0, // 尖角高度
this.arrAngle = 60.0, // 尖角角度
this.radius = 10.0, // 圓角弧度大小(半徑)
this.strokeWidth = 4.0, // 邊框?qū)挾? this.style = PaintingStyle.fill, // 樣式(填充或邊框)
this.borderColor, // 邊框顏色(PaintingStyle.stroke 適用)
this.child, // 子 Widget
this.innerPadding = 6.0, // 子 Widget 距邊框邊距
}) : super(key: key);
import 'package:flutter/material.dart';
import 'package:flutter_bubble/bubble_widget.dart';
class BubblePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(children: <Widget>[
Padding(
padding: EdgeInsets.all(4.0),
child: Container(
alignment: Alignment.centerRight,
child: BubbleWidget(255.0, 60.0, Colors.green.withOpacity(0.7),
BubbleArrowDirection.right,
child: Text('你好暮顺,我是萌新 BubbleWidget!',
style: TextStyle(color: Colors.white, fontSize: 16.0))))),
Padding(
padding: EdgeInsets.all(4.0),
child: Container(
alignment: Alignment.centerLeft,
child: BubbleWidget(205.0, 60.0,
Colors.deepOrange.withOpacity(0.7), BubbleArrowDirection.left,
child: Text('你好秀存,你有什么特性化捶码?',
style: TextStyle(color: Colors.white, fontSize: 16.0))))),
Padding(
padding: EdgeInsets.all(4.0),
child: Container(
alignment: Alignment.centerRight,
child: BubbleWidget(300.0, 90.0, Colors.green.withOpacity(0.7),
BubbleArrowDirection.right,
child: Text('我可以自定義:\n尖角方向,尖角高度或链,尖角角度惫恼,\n距圓角位置,圓角大小株扛,邊框樣式等尤筐!',
style: TextStyle(color: Colors.white, fontSize: 16.0))))),
Padding(
padding: EdgeInsets.all(4.0),
child: Container(
alignment: Alignment.centerLeft,
child: BubbleWidget(140.0, 60.0,
Colors.deepOrange.withOpacity(0.7), BubbleArrowDirection.left,
child: Text('你有什么不足?',
style: TextStyle(color: Colors.white, fontSize: 16.0))))),
Padding(
padding: EdgeInsets.all(4.0),
child: Container(
alignment: Alignment.centerRight,
child: BubbleWidget(350.0, 60.0, Colors.green.withOpacity(0.7),
BubbleArrowDirection.right,
child: Text('我現(xiàn)在還不會動態(tài)計算高度洞就,只可用作背景盆繁!',
style: TextStyle(color: Colors.white, fontSize: 16.0))))),
Padding(
padding: EdgeInsets.all(4.0),
child: Container(
alignment: Alignment.centerLeft,
child: BubbleWidget(105.0, 60.0,
Colors.deepOrange.withOpacity(0.7), BubbleArrowDirection.left,
child: Text('繼續(xù)加油!',
style: TextStyle(color: Colors.white, fontSize: 16.0))))),
Padding(
padding: EdgeInsets.all(4.0),
child: Container(
alignment: Alignment.centerRight,
child: BubbleWidget(150.0, 140.0, Colors.green.withOpacity(0.7),
BubbleArrowDirection.right,
child: Image.asset('images/icon_hzw.jpg'))))
]);
}
}
??????GitHub 地址??????Pub 地址
??????自定義 Bubble Widget 是小菜發(fā)布的第二款 Pub 插件旬蟋,還有很多不完善的地方油昂,如有錯誤請多多指導!
來源:阿策小和尚