Flutter 56: 圖解自定義 BubbleWidget 氣泡插件

??????小菜在學(xué)習(xí)時(shí)需要用到氣泡效果,為了更加靈活,小菜封裝了一個(gè)簡(jiǎn)單的 flutter_bubble 氣泡插件,方便日常的使用;

??????小菜準(zhǔn)備用 CanvasdrawPath 進(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)為邊框中間位置;

  1. 尖角在頂部時(shí),距離為左上圓角結(jié)束點(diǎn)邊距;
  2. 尖角在右側(cè)時(shí),距離為右上圓角結(jié)束點(diǎn)邊距;
  3. 尖角在底部時(shí),距離為右下圓角結(jié)束點(diǎn)邊距;
  4. 尖角在左側(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)源:阿策小和尚

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容