写点什么

【Flutter 专题】56 图解自定义 BubbleWidget 气泡插件

发布于: 2 小时前
【Flutter 专题】56 图解自定义 BubbleWidget 气泡插件

      小菜在学习时需要用到气泡效果,为了更加灵活,小菜封装了一个简单的 flutter_bubble 气泡插件,方便日常的使用;


      小菜准备用 CanvasdrawPath 进行绘制,主要分为三个部分,圆角弧线,普通直线,尖角折线,均可由 drawPath 自带方法绘制;小菜以前整理过关于 Canvas 绘制的小博客,实现很简单;



      小菜绘制了一个简陋的原型图,整体黑框为 Bubble Widget 整体范围;蓝色圆弧为圆角位置;红色尖角可根据上下左右参数进行配置,且只可展示一个,尖角的高度和角度可自由配置,当确定一个尖角位置时,其余三个方向宽高延伸到黑框部分;而橙线则是连接圆角与尖角等直线;中间空余部分为子 Widget 位置;Tips: Child Widget 宽高小于等于 Bubble Widget



绘制圆角

      首先在边角处绘制四个圆弧,直接用 arcTo 即可,需要注意的是:小菜整体以 drawPath 方式实现,准备从左上角开始顺时针绘制,所以绘制圆弧时也是顺时针方向;


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,只需调整矩形位置与起始角度即可;


// 逆时针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),通过三角函数进行计算;


path.lineTo(arrHeight * tan(_angle(arrAngle * 0.5)), 0.0);path.lineTo(arrHeight * tan(_angle(arrAngle * 0.5)) * 2, arrHeight);
复制代码



绘制连线

      最后就是将处理好的连接起来,小菜为了适应更多场景,尖角位置也可自由配置,长度为到圆角的距离,默认为边框中间位置;


  1. 尖角在顶部时,距离为左上圆角结束点边距;

  2. 尖角在右侧时,距离为右上圆角结束点边距;

  3. 尖角在底部时,距离为右下圆角结束点边距;

  4. 尖角在左侧时,距离为左下圆角结束点边距;

整体分析

      小菜将配置逻辑编辑好发布到 Pub 库,基本 BubbleWidget 便完成,简单分析一下可配置项;


BubbleWidget(  this.width,       // 整体高度,并非 Child Widget 宽度  this.height,      // 整体高度,并非 Child Widget 高度  this.color,       // 填充颜色,borderColor==null 时也为边框颜色  this.position, {  // 尖角位置(上下左右)  Key key,  this.length = -1.0,       // 尖角距离圆角结束点边距,默认为中点  this.arrHeight = 12.0,    // 尖角高度  this.arrAngle = 60.0,     // 尖角角度  this.radius = 10.0,       // 圆角弧度大小(半径)  this.strokeWidth = 4.0,   // 边框宽度  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('我现在还不会动态计算高度,只可用作背景!', 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('继续加油!', 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 是小菜发布的第二款 Pub 插件,还有很多不完善的地方,如有错误请多多指导!


来源:阿策小和尚

发布于: 2 小时前阅读数: 2
用户头像

还未添加个人签名 2021.05.13 加入

Android / Flutter 小菜鸟~

评论

发布
暂无评论
【Flutter 专题】56 图解自定义 BubbleWidget 气泡插件