Flutter 通过Clipper实现各种自定义形状的示例代码
作者:蓝色微笑ing 发布时间:2023-06-19 14:25:11
标签:Flutter,自定义形状
本文介绍了Flutter 通过Clipper实现各种自定义形状的示例代码,分享给大家,具体如下:
ClipOval 圆形裁剪
ClipOval(
child: SizedBox(
width: 120.0,
height: 120.0,
child: Image.asset(
Config.assets_avatar_1,
),
),
);
CircleAvatar 圆形头像
CircleAvatar(
radius: 60.0,
backgroundImage: AssetImage(
Config.assets_avatar_1,
),
);
Container Decoration 装饰形状
通过BoxShape.circle实现圆形图片
Container(
width: 120.0,
height: 120.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: AssetImage(
Config.assets_avatar_1,
),
),
)
);
通过BorderRadius实现圆形图片
Container(
width: 120.0,
height: 120.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(60.0)),
image: DecorationImage(
image: AssetImage(
Config.assets_avatar_1,
),
),
),
)
ClipPath 路径剪裁
ClipPath(
clipper: TriangleClipper(ClipperPosition.LeftTop),
child: Container(
width: 16.0,
height: 16.0,
decoration: BoxDecoration(
color: Colors.blue,
),
),
);
enum ClipperPosition {
LeftTop,
RightTop,
}
class TriangleClipper extends CustomClipper<Path> {
final ClipperPosition position;
TriangleClipper(this.position);
@override
Path getClip(Size size) {
final path = Path();
path.lineTo(0.0, 0.0);
if (position == ClipperPosition.LeftTop) {
path.lineTo(size.width, 0.0);
path.lineTo(size.width, size.height);
} else if (position == ClipperPosition.RightTop) {
path.lineTo(size.width, 0.0);
path.lineTo(0.0, size.height);
}
path.close();
return path;
}
@override
bool shouldReclip(CustomClipper oldClipper) {
return false;
}
}
ClipRect 矩形剪裁
Container(
alignment: Alignment.topCenter,
color: Colors.transparent,
child: Container(
color: Colors.green,
child: ClipRect(
clipper: _RectClipper(20.0),
child: Image.asset(
Config.assets_avatar_1,
width: 160.0,
height: 160.0,
fit: BoxFit.fill,
),
),
),
);
class _RectClipper extends CustomClipper<Rect> {
/// Remove side of size
final double removeSize;
_RectClipper(this.removeSize);
@override
Rect getClip(Size size) {
return new Rect.fromLTRB(
removeSize,
removeSize,
size.width - removeSize,
size.height - removeSize,
);
}
@override
bool shouldReclip(CustomClipper<Rect> oldClipper) {
return false;
}
}
ClipRRect 圆角矩形剪裁
ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(16.0)),
child: Image.asset(
Config.assets_avatar_1,
fit: BoxFit.fill,
width: 120.0,
height: 120.0,
),
);
Star Rating(CustomPaint) 评分控件
评分控件 UI图
实现方案
使用CustomPaint结合ClipPath画出单个五角星;
使用Stack渲染两层画面
背景层,一排灰色五角星 前景层,一排亮色五角星,并使用ClipRect截取一定Width
实现代码
class StarRatingDemo extends StatefulWidget {
@override
_StarRatingDemoState createState() => _StarRatingDemoState();
}
class _StarRatingDemoState extends State<StarRatingDemo> {
/// ClipPath Star Rating
_buildClipPathStarRating(double rate, int count) {
return Container(
padding: EdgeInsets.fromLTRB(24.0, 16.0, 24.0, 0.0),
child: StaticRatingBar(
size: 50.0,
rate: rate,
count: count,
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Star Rating'),
),
body: ListView(
physics: BouncingScrollPhysics(),
children: <Widget>[
// _buildClipPathStarRating(1.0, 1),
_buildClipPathStarRating(0.5, 5),
_buildClipPathStarRating(2.0, 5),
_buildClipPathStarRating(3.0, 5),
_buildClipPathStarRating(4.0, 5),
_buildClipPathStarRating(5.0, 5),
_buildClipPathStarRating(5.5, 6),
SizedBox(height: 16.0),
],
),
);
}
}
class StaticRatingBar extends StatelessWidget {
/// Number of stars
final int count;
/// Init rate
final double rate;
/// Size of the starts
final double size;
final Color colorLight;
final Color colorDark;
StaticRatingBar({
this.rate = 5,
this.colorLight = const Color(0xFF1E88E5),
this.colorDark = const Color(0xFFEEEEEE),
this.count = 5,
this.size = 60,
});
Widget buildDarkStar() {
return SizedBox(
width: size * count,
height: size,
child: CustomPaint(
painter: _PainterStars(
count: count,
color: colorDark,
strokeWidth: 0.0,
size: this.size / 2,
style: PaintingStyle.fill,
),
),
);
}
Widget buildLightStar() {
return ClipRect(
clipper: _RatingBarClipper(rate * size),
child: SizedBox(
height: size,
width: size * count,
child: CustomPaint(
painter: _PainterStars(
count: count,
strokeWidth: 0.0,
color: colorLight,
size: this.size / 2,
style: PaintingStyle.fill,
),
),
),
);
}
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
buildDarkStar(),
buildLightStar(),
],
);
}
}
class _RatingBarClipper extends CustomClipper<Rect> {
final double width;
_RatingBarClipper(this.width);
@override
Rect getClip(Size size) {
return Rect.fromLTRB(0.0, 0.0, width, size.height);
}
@override
bool shouldReclip(_RatingBarClipper oldClipper) {
return false;
}
}
class _PainterStars extends CustomPainter {
final double size;
final int count;
final Color color;
final PaintingStyle style;
final double strokeWidth;
_PainterStars({
this.size,
this.count,
this.color,
this.strokeWidth,
this.style,
});
double degree2Radian(int degree) {
return (pi * degree / 180);
}
Path createStarPath(double radius, Path path) {
double radian = degree2Radian(36);
double radiusIn = (radius * sin(radian / 2) / cos(radian)) * 1.1;
path.moveTo((radius * cos(radian / 2)), 0.0);
path.lineTo(
(radius * cos(radian / 2) + radiusIn * sin(radian)),
(radius - radius * sin(radian / 2)),
);
path.lineTo(
(radius * cos(radian / 2) * 2),
(radius - radius * sin(radian / 2)),
);
path.lineTo(
(radius * cos(radian / 2) + radiusIn * cos(radian / 2)),
(radius + radiusIn * sin(radian / 2)),
);
path.lineTo(
(radius * cos(radian / 2) + radius * sin(radian)),
(radius + radius * cos(radian)),
);
path.lineTo((radius * cos(radian / 2)), (radius + radiusIn));
path.lineTo(
(radius * cos(radian / 2) - radius * sin(radian)),
(radius + radius * cos(radian)),
);
path.lineTo(
(radius * cos(radian / 2) - radiusIn * cos(radian / 2)),
(radius + radiusIn * sin(radian / 2)),
);
path.lineTo(0.0, (radius - radius * sin(radian / 2)));
path.lineTo(
(radius * cos(radian / 2) - radiusIn * sin(radian)),
(radius - radius * sin(radian / 2)),
);
path.lineTo((radius * cos(radian / 2)), 0.0);
return path;
}
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint();
paint.strokeWidth = strokeWidth;
paint.color = color;
paint.style = style;
Path path = Path();
double offset = strokeWidth > 0 ? strokeWidth + 2 : 0.0;
path = createStarPath(this.size - offset, path);
for (int i = 0; i < count - 1; i++) {
path = path.shift(Offset(this.size * 2, 0.0));
path = createStarPath(this.size - offset, path);
}
if (offset > 0) {
path = path.shift(Offset(offset, offset));
}
path.close();
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(_PainterStars oldDelegate) {
return oldDelegate.size != this.size;
}
}
代码地址
https://github.com/smiling1990/FlutterClipper
来源:https://juejin.im/post/5de65ee76fb9a0164a10a65e
0
投稿
猜你喜欢
- 第一种方法这种方法需要配置 hibernate.cfg.xml 的属性 hibernate.hbm2ddl.auto,该属性值的具体说明如下
- 上篇博客我们了解了请求参数的获取,那么获取到请求参数之后,需要对参数进行出来,然后进行数据响应。那么这篇博客我们就来了解 Controlle
- FloatingActionButton项目在github上的主页:https://github.com/futuresimple/andr
- 背景众所周知,所有被打开的系统资源,比如流、文件或者Socket连接等,都需要被开发者手动关闭,否则随着程序的不断运行,资源泄露将会累积成重
- 前言我们平时在开发的时候,发起网络请求前,会需要显示一个Loading,一般的做法都是在xml布局上添加好Loading,然后在Activi
- 本文章主要讲二维数组定义,用法。1.什么是二维数组在二维数组多个元素为一维数组的数组就称为二维数组2.定义格式格式一:元素的数据类型[][]
- 本文实例讲述了JAVA设计模式之备忘录模式。分享给大家供大家参考,具体如下:备忘录模式:又叫做快照模式,指在不破坏封装性的前提下,获取到一个
- 一、什么是网关限流:在微服务架构中,网关层可以屏蔽外部服务直接对内部服务进行调用,对内部服务起到隔离保护的作用,网关限流,顾名思义,就是通过
- 开始用springboot2+hession4实现RPC服务时,发现第一个服务可以调用成功,但第二个就一直报'<'is
- Dubbo作为国内最出名的分布式服务框架,是Java程序员必备必会的框架之一,更是中高级测试面试过程中经常会问的技术,无论你是否用过,你都必
- LiveData简介在日常安卓开发中,一些耗时的 * 如列网络请求,数据库读写都不能在主线程执行,必须开一条子线程去执行这些耗时操作,但我们往
- 前言前不久遇到一个问题,是公司早期的基础库遇到的,其实很低级,但是还是记录下来。出错点是一个 IO 流的写入bug,我们项目会有一种专有的数
- 1、IndexTagController.java@GetMapping("/tags/{id}") &n
- 关于java中遍历map具体哪四种方式,请看下文详解吧。方式一 这是最常见的并且在大多数情况下也是最可取的遍历方式。在键值都需要时使用。 M
- 引言:序列化是将对象的状态信息转换为可以存储或传输的形式的过程,在序列化期间,对象将其带你过去的状态写入到临时或持储存区,反序列化就是重新创
- 把最近听的写的一些题目做下笔记!1.下列程序的执行,说法错误的是 ( ABC )public class MultiCatch
- 本文实例讲述了C#遍历指定目录下所有文件的方法。分享给大家供大家参考。具体分析如下:先通过DirectoryInfo打开指定的目录,然后通过
- 1. 启用AOPa. 在类上添加@Aspect注解b. 注入该类, 可以使用@Component进行注入到Spring容器中2. 通过Poi
- Android在启动模拟器AVD时,出现下面的异常:“Failed to allocate memory: 8”,怎么办?此错误是我们在允许
- 前言在之前 LiveData 源码浅析的博客中提到了 ViewModel 组件,当时对 ViewModel 的解释是 “