
Flutter 提供了一些可供裁剪的 Widget,这可比 android 动不动就得自定义方便多了,这些 Widget 大多以 Clip 开头,可以裁剪图片、布局成圆形、圆角,矩形
ClipOvalCircleAvatarClipRRectClipPathCustomClipper- ``
CustomClipper 是自定义裁剪,是一个接口,在 ClipPath 中已经使用过来,同样适用于其他 Clip 打头的 Widget
ClipOval
ClipOval 看名字大家应该能猜到,对 -> 就是圆形裁剪
ClipOval 的属性很简单,大家看一下:
ClipOval({
Key key,
this.clipper, //裁剪路径
this.clipBehavior = Clip.antiAlias,
Widget child
})
代码:
class DD extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ClipOval(
child: Container(
child: Image(
image: AssetImage("assets/icons/icon_2.jpg"),
height: 300,
width: 300,
fit: BoxFit.cover,
),
),
),
],
);
}
}

CircleAvatar
CircleAvatar其实和 ClipOval 一样,都是裁剪成圆形的,区别是 CircleAvatar 可以裁剪复杂布局,不过我觉得这名字起的真是蛋疼,怎么叫别人记啊
class FF extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CircleAvatar(
backgroundImage: AssetImage(
"assets/icons/icon_3.png",
),
maxRadius: 200,
child: Text(
"AAAAAAAAAA",
style: TextStyle(
fontSize: 30,
color: Colors.lightGreenAccent,
),
),
);
}
}

ClipRRect
ClipRRect 裁剪圆角矩形,矩形也可以,不过单纯矩形的话也不用他了
class FF extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image(
image: AssetImage("assets/icons/icon_3.png"),
height: 300,
width: 300,
fit: BoxFit.cover,
),
);
}
}

ClipPath
ClipPath 哈哈,熟悉 path 的同学是不是又找到感觉啦,android 里 path 可是虐的我不要不要的,Flutter 这里也是一样啊,好在基本的 path 操作都 OK 了
先看下 ClipPath 的属性:
const ClipPath({
Key key,
this.clipper,
this.clipBehavior = Clip.antiAlias,
Widget child,
}) : super(key: key, child: child);
其实上面几个 clip widget 属性都一样,clipper 这个属性可以让我们自己提供裁剪区域,具体到 ClipPath 这里就是提供一个 path 路径了
clipper 属性需要我们自己实现一个 CustomClipper 的实现类:
abstract class CustomClipper<T> {
T getClip(Size size);
Rect getApproximateClipRect(Size size) => Offset.zero & size;
bool shouldReclip(covariant CustomClipper<T> oldClipper);
}
我们需要重写 CustomClipper 里面的2个方法:
CustomClipper接收一个泛型,泛型类型表示返回图形的形状,比如我们我们返回 path 路径getClip方法里size我们可以拿到 widget 的大小,饭后根据这个尺寸去裁剪图形并返回shouldReclip方法是询问我们要不要重新裁剪
这里我提供2个例子:
1. ClipPath 实现复杂图形背景
先看图:

这个常用吧,产品不这么设计就不舒服斯基.......这个其实就是先用Stack实现重叠布局,然后背景 Widget 用ClipPath做的裁剪
先看怎么裁剪的:
class HeaderColor extends CustomClipper<Path> {
@override
Path getClip(Size size) {
//x坐标为0.0 y坐标为手机高度一半
//到x坐标为手机宽度 到 手机宽度的一半减去100 达到斜线的结果
//到x坐标为手机宽度 到 y坐标为手机宽度
//完成
var path = Path()
..lineTo(0.0, size.height / 2)
..lineTo(size.width, size.height / 2 - 100)
..lineTo(size.width, 0)
..close();
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
然后是综合布局:
Widget build(BuildContext context) {
return new Scaffold(
body: ClipPath(//剪切区域
child: Stack(
fit: StackFit.expand,
children: [
Container(
decoration: BoxDecoration(
color: Color(0xff622F74),
gradient: LinearGradient(//渐变色
colors: [Colors.blue, Colors.deepOrangeAccent],// blue deepOrangeAccent
begin: Alignment.centerRight, //起点
end: Alignment(-1.0, -1.0))),//终点
),
Column(
children: [
Padding(
padding: EdgeInsets.only(top: 50.0),
child: Container(
width: 100.0,
height: 100.0,
decoration: new BoxDecoration(
border: Border.all(color: Colors.amber, width: 2.0),
color: const Color(0xFFFFFFFF), // border color
shape: BoxShape.circle,
image: DecorationImage(
image: AssetImage("images/avatar.jpeg"))))),
],
),
],
),
clipper: HeaderColor(),//主要部分
));
2. ClipPath 实现图片五角星
五角星 path 来源:flutter使用剪裁制作评分控件
继续看图:

直接看怎么裁剪的吧:
import 'package:flutter/material.dart';
import 'dart:math' as Math;
class StarCliper extends CustomClipper<Path>{
final double radius;
StarCliper({this.radius});
/// 角度转弧度公式
double degree2Radian(int degree) {
return (Math.pi * degree / 180);
}
@override
Path getClip(Size size) {
double radius = this.radius;
Path path = new Path();
double radian = degree2Radian(36);// 36为五角星的角度
double radius_in = (radius * Math.sin(radian / 2) / Math
.cos(radian)); // 中间五边形的半径
path.moveTo((radius * Math.cos(radian / 2)), 0.0);// 此点为多边形的起点
path.lineTo((radius * Math.cos(radian / 2) + radius_in
* Math.sin(radian)),
(radius - radius * Math.sin(radian / 2)));
path.lineTo((radius * Math.cos(radian / 2) * 2),
(radius - radius * Math.sin(radian / 2)));
path.lineTo((radius * Math.cos(radian / 2) + radius_in
* Math.cos(radian / 2)),
(radius + radius_in * Math.sin(radian / 2)));
path.lineTo(
(radius * Math.cos(radian / 2) + radius
* Math.sin(radian)), (radius + radius
* Math.cos(radian)));
path.lineTo((radius * Math.cos(radian / 2)),
(radius + radius_in));
path.lineTo(
(radius * Math.cos(radian / 2) - radius
* Math.sin(radian)), (radius + radius
* Math.cos(radian)));
path.lineTo((radius * Math.cos(radian / 2) - radius_in
* Math.cos(radian / 2)),
(radius + radius_in * Math.sin(radian / 2)));
path.lineTo(0.0, (radius - radius * Math.sin(radian / 2)));
path.lineTo((radius * Math.cos(radian / 2) - radius_in
* Math.sin(radian)),
(radius - radius * Math.sin(radian / 2)));
path.close();// 使这些点构成封闭的多边形
return path;
}
@override
bool shouldReclip(StarCliper oldClipper) {
return this.radius != oldClipper.radius;
}
}
综合布局:
class FF extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ClipPath(
clipper: StarCliper(radius: 150.0),
child: Image(
image: AssetImage("assets/icons/icon_3.png"),
height: 300,
width: 300,
fit: BoxFit.cover,
),
);
}
}