??????小菜在學(xué)習(xí)時(shí)需要用到氣泡效果,為了更加靈活,小菜封裝了一個(gè)簡(jiǎn)單的 flutter_bubble 氣泡插件,方便日常的使用;
??????小菜準(zhǔn)備用 Canvas 的 drawPath 進(jìn)行繪制,主要分為三個(gè)部分,圓角弧線,普通直線,尖角折線,均可由 drawPath 自帶方法繪制;小菜以前整理過(guò)關(guān)于 Canvas 繪制的小博客,實(shí)現(xiàn)很簡(jiǎn)單;
??????小菜繪制了一個(gè)簡(jiǎn)陋的原型圖,整體黑框?yàn)?Bubble Widget 整體范圍;藍(lán)色圓弧為圓角位置;紅色尖角可根據(jù)上下左右參數(shù)進(jìn)行配置,且只可展示一個(gè),尖角的高度和角度可自由配置,當(dāng)確定一個(gè)尖角位置時(shí),其余三個(gè)方向?qū)捀哐由斓胶诳虿糠郑欢染€則是連接圓角與尖角等直線;中間空余部分為子 Widget 位置;Tips: Child Widget 寬高小于等于 Bubble Widget;
繪制圓角
??????首先在邊角處繪制四個(gè)圓弧,直接用 arcTo 即可,需要注意的是:小菜整體以 drawPath 方式實(shí)現(xiàn),準(zhǔn)備從左上角開(kāi)始順時(shí)針繪制,所以繪制圓弧時(shí)也是順時(shí)針?lè)较颍?/p>
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 為繪制弧形角度;小菜需要的四個(gè)圓弧大小均為 pi/2,只需調(diào)整矩形位置與起始角度即可;
// 逆時(shí)針
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);
// 順時(shí)針
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 兩段直線拼接起來(lái)的,只需要處理起點(diǎn)與終點(diǎn)即可;小菜為了更加靈活,可以設(shè)置尖角高度與尖角角度(0 ~ 180),通過(guò)三角函數(shù)進(jìn)行計(jì)算;
path.lineTo(arrHeight * tan(_angle(arrAngle * 0.5)), 0.0);
path.lineTo(arrHeight * tan(_angle(arrAngle * 0.5)) * 2, arrHeight);
繪制連線
??????最后就是將處理好的連接起來(lái),小菜為了適應(yīng)更多場(chǎng)景,尖角位置也可自由配置,長(zhǎng)度為到圓角的距離,默認(rèn)為邊框中間位置;
- 尖角在頂部時(shí),距離為左上圓角結(jié)束點(diǎn)邊距;
- 尖角在右側(cè)時(shí),距離為右上圓角結(jié)束點(diǎn)邊距;
- 尖角在底部時(shí),距離為右下圓角結(jié)束點(diǎn)邊距;
- 尖角在左側(cè)時(shí),距離為左下圓角結(jié)束點(diǎn)邊距;
整體分析
??????小菜將配置邏輯編輯好發(fā)布到 Pub 庫(kù),基本 BubbleWidget 便完成,簡(jiǎn)單分析一下可配置項(xiàng);
BubbleWidget(
this.width, // 整體高度,并非 Child Widget 寬度
this.height, // 整體高度,并非 Child Widget 高度
this.color, // 填充顏色,borderColor==null 時(shí)也為邊框顏色
this.position, { // 尖角位置(上下左右)
Key key,
this.length = -1.0, // 尖角距離圓角結(jié)束點(diǎn)邊距,默認(rèn)為中點(diǎn)
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)在還不會(huì)動(dòng)態(tài)計(jì)算高度,只可用作背景!',
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 插件,還有很多不完善的地方,如有錯(cuò)誤請(qǐng)多多指導(dǎo)!
來(lái)源:阿策小和尚