Flutter----实现优惠券外观的绘制

近来看源码。看的好爽。但是说句话没多大用,要是碰见难题是不是大家就跟我一样找UI切图去了。

好了不说别的了 ,先看下面的优惠券。

要是实现这样的优惠券你要怎么办?好吧,如果你问我的话,我的第一反应就是:xx姐/xx哥(UI切图)给我切个图。不知道你们是不是跟我一样。

好吧这次我们就用代码来实现这种样式。

shape

看到这边边框的话,我相信大家能想到shape这个参数吧。他有几个类型的参数值:Border(),CircleBorder(),RoundedRectangleBorder(),OutlineInputBorder(),UnderlineInputBorder()这常用的几种吧。那么你看过他是怎么实现的吗?既然我们为了要实现外边框是我们上图优惠券那样的样式。那么我们就看一下他们函数是怎么形成的把。

我们随便看一个OutlineInputBorder,他是继承了InputBorder。
还是老方法,继续点进去看看。

同理看到了他是一个抽象类等,自然不能实例化(但是在Dart/Flutter中抽象类也有可以实例化的,在这里我不多说你可以去看看,我就列举一下一个 List 它本身就是一个抽象类,但是我们平时也是可以直接去实例化或者去声明使用的),自然我们继续点进去。

到头了!我们可以去判定,这个ShapeBorder就是我们所有border的老祖宗了。你会问为什么吧,按照它继承的方式,我们找到了最顶层的不就是老祖宗吗?

这时候我们可能已经找到了我们想要的方法了。你会说这样我们直接去看源码吗?

对我们的目标是看源码,但是没必要都看啊。你想他的protect标志我们完全不需要看,你看也行。因为在我们的代码书写甚至重写的时候,完全不需要用不到罢了。

ShapeBorder

我们找到了工具了,不说别的,我们一直看没用啊,怎么办?我们来使用一下试试!

我们继承一下试试啊。所以说我们看源码必看的就是这几个部分了,其余可以不看了。

来,看一下。第一个重写的方法dimensions 返回值是EdgeInsetsGeometry。好吧我们应该知道了吧,他就是一个边距。

第二个重写的方法getInnerPath 返回值是Path。按照字面意思就是获取内部路径吧

第三个重写的方法getOuterPath 返回值是Path。按照字面的意思就是获取外部路径

第四个paint,他还有个canvas,还用说?画布呗。

第五个scale,也不用说了,一看就是一个缩放的因子,类型还是double应该是可以的把。

看完了我们就开始看源码了,猜可不行啊!

我就列举一个,其余不给演示了。

内容我也不翻译了,他就是创建了一个边缘的外路径。怎么办?咋用!

别急我们看源码,因为我们应该都比较熟悉paint吧,看见里面有个参数是rect,打印 一下看看!

控制台有了,这就是相对于我们控件的位置。这样好办多了啊。

我们先随便画一个圆环。

看效果:

好嘞,有了!!!!那么画多个呢?我也不多说了,自己继续捣鼓!!!

但是你发现我们要的效果不是在上面绘出一个东西,他是裁剪的,就跟打洞一样!哎怎么办呢!竟然是在外边缘,那么我们就重写getOuterPath方法吧!

你看,外边框有圆角效果了,不出意外就是它了。怎么办啊?继续盘他!!!!

随便试试能打洞不、。、。

哎呀强啊!!!成功了!!!(在这里说一下看文档里面的getInnerPath我加过处理方式,但是没有效果我就放弃了。好想看文档说的是外部路径的path返回就是内部返回的还是我英语不好咋地。我就基本重写getOuterPath方法了。如果有实验过的我的说法有误的可以给我提出)

我们淌水完毕了,怎么办啊。感觉自己贼牛B了 能解决他们了!来实现优惠券了。

实现优惠券

我也不多说了直接上代码!!!大部分的具体实现我都在里面打了备注和注释。看不懂那就没办法了!!!!

import 'dart:math';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class CouponShapeBorder extends ShapeBorder{
  final int holeCount;  //我们打洞的个数
  final double lineRate;  //确定白线在控件中的位置 按照0 - 1 来算
  final bool dash;  //判断是否是虚线
  final Color color;  //线的颜色

  CouponShapeBorder({
    this.holeCount = 6,
    this.lineRate = 0.718,
    this.dash = true,
    this.color = Colors.white
  });

  @override
  // TODO: implement dimensions
  EdgeInsetsGeometry get dimensions => null;

  @override
  Path getInnerPath(Rect rect, {TextDirection textDirection}) {
    // TODO: implement getInnerPath
    return null;
  }

  @override
  Path getOuterPath(Rect rect, {TextDirection textDirection}) {
    // TODO: implement getOuterPath
    var path = Path();
    var w = rect.width;
    var h = rect.height;
    var d = h / (1 + 2 * holeCount);
    ///这个路径就是直接吧我们的rect放上去就是我们的控件本身的path  不需要进行任何裁剪和其他操作
    path.addRect(rect);

    _formHoldLeft(path, d);
    _formHoldRight(path, w, d);
    _formHoleTop(path, rect);
    _formHoleBottom(path, rect);

    ///这个是官方文档需要我们填的  但是也需要我们的path是从getInnerPath中返回的(这个重写函数感觉写不写没多大影响!!!有兴趣的可以试试)
    path.fillType = PathFillType.evenOdd;
    return path;
  }

  _formHoldLeft(Path path, double d) {
    for (int i = 0; i < holeCount; i++) {
      var left = -d / 2;
      var top = 0.0 + d + 2 * d * (i);
      var right = left + d;
      var bottom = top + d;
      path.addArc(Rect.fromLTRB(left, top, right, bottom), -pi / 2, pi);
    }
  }

  _formHoldRight(Path path, double w, double d) {
    for (int i = 0; i < holeCount; i++) {
      var left = -d / 2 + w;
      var top = 0.0 + d + 2 * d * (i);
      var right = left + d;
      var bottom = top + d;
      path.addArc(Rect.fromLTRB(left, top, right, bottom), pi / 2, pi);
    }
  }

  void _formHoleBottom(Path path, Rect rect) {
    path.addArc(Rect.fromCenter(center: Offset(lineRate * rect.width, rect.height),width: 13.0,height: 13.0),pi,pi);
  }

  void _formHoleTop(Path path, Rect rect) {
    path.addArc(Rect.fromCenter(center: Offset(lineRate * rect.width, 0),width: 13.0,height: 13.0),0,pi);
  }

  @override
  void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {
    // TODO: implement paint
    var paint = Paint()
        ..color = color
        ..strokeWidth = 1.5
        ..style = PaintingStyle.stroke
        ..strokeJoin = StrokeJoin.round;
    var d = rect.height / (1 + 2 * holeCount);
    if(dash){  //虚线
      _drawDashLine(canvas,Offset(lineRate * rect.width,d / 2),rect.height / 16,rect.height - 13,paint);
    }else{  //实线
      canvas.drawLine(Offset(lineRate * rect.width,d / 2), Offset(lineRate * rect.width,rect.height - d / 2), paint);
    }
  }

  _drawDashLine(Canvas canvas,Offset start,double count,double length,Paint paint){
    var step = length / count / 2;
    for(int i = 0;i < count;i++){
      var offset = start + Offset(0,2 * step * i);
      canvas.drawLine(offset, offset + Offset(0,step), paint);
    }
  }

  @override
  ShapeBorder scale(double t) {
    // TODO: implement scale
    return null;
  }

}

但是你必须要知道,打洞在getOuterPath方法里面,绘线就在paint里面!!!

然后我们码完了!!!看效果吧!

好了成功了,你可别再说我得把里面的内容给砸进去,那样就不好玩了宝贝!!!

这样我们的优惠券就完成了。

经过这次事件,我们可以知道。Flutter一切都是Widget是不错的,那么你还要知道其实大部分的效果包括Widget都是经过绘制完成的。所以说我们要学会canvas和clip是非常重要的!!!

最后上一下我的布局代码省的自己都懵了!!!!

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('demo'),),
      body: Center(
        child: Material(
          color: Colors.orangeAccent,
          elevation: 2,
          shape: CouponShapeBorder(),
          child: Container(
            alignment: Alignment.center,
            padding: EdgeInsets.all(10),
            height: 150,
            width: 400,
            child: Text('自定义ShapeBorder的使用',style: TextStyle(fontSize: 20,color: Colors.white),),
          ),
        ),
      ),
    );
  }
}

最后在这里感激“编程之王”大佬 ===>这是大佬文章的tp地址,你要是想学可以进去看看

膜拜大佬!!!!

大佬文章地址!: 点我TP.

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值