近来看源码。看的好爽。但是说句话没多大用,要是碰见难题是不是大家就跟我一样找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.