Flutter anti-aliasing bug

Flutter anti-aliasing(抗锯齿) bug

问题描述

项目里有一段弧形曲线, 我选择用 Row + Expanded + Container 堆叠的方式实现,但是出现了一些很奇怪的hairline border现象.

Flutter anti-aliasing bug

原因

每个 Wrap 之间没有进行像素对齐, 重复渲染的地方导致了混色(颜色加深), 形成了hairline borderissue.

所以相同颜色,尽量避免分块去渲染,会导致抗锯齿效果不佳.

解决方案

  1. 根据更具体的像素比,去渲染对应的大小. 但是这种方法只适用于 width 和 height 都是明确的情况下, 我们通过像素比去控制实际渲染的大小尽量贴近于整数.
  2. 直接使用 custom paint 进行整体绘制. 这种方法可以避免 Wrap 之间像素对齐, 也能保证整体的抗锯齿效果.

我使用的是第二种方法, 效果如图:
Flutter anti-aliasing solution

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
class CexDexPainter extends CustomPainter {
CexDexPainter(
{super.repaint,
required this.leftToRight,
required this.leftColor,
required this.rightColor});

final bool leftToRight;
final Color leftColor;
final Color rightColor;

@override
void paint(Canvas canvas, Size size) {
Paint paintLeft = Paint()..color = leftColor; // 左边颜色
Paint paintRight = Paint()..color = rightColor; // 右边颜色
final square = 8.w;
final center = 4.w;

Path leftPath = Path()
..moveTo(0, 0)
..lineTo(size.width / 2 - square - center / 2, 0)
..arcToPoint(
Offset(size.width / 2 - center / 2, square),
radius: Radius.circular(8.r),
clockwise: true,
)
..lineTo(size.width / 2 + center / 2, size.height - square)
..arcToPoint(
Offset(size.width / 2 + square + center / 2, size.height),
radius: Radius.circular(8.r),
clockwise: false,
)
..lineTo(0, size.height)
..close();

if (!leftToRight) {
leftPath = Path()
..moveTo(0, 0)
..lineTo(size.width / 2 + square + center / 2, 0)
..arcToPoint(
Offset(size.width / 2 + center / 2, square),
radius: Radius.circular(8.r),
clockwise: false,
)
..lineTo(size.width / 2 - center / 2, size.height - square)
..arcToPoint(
Offset(size.width / 2 - square - center / 2, size.height),
radius: Radius.circular(8.r),
clockwise: true,
)
..lineTo(0, size.height)
..close();
}

Path rightPath = Path()
..moveTo(size.width / 2 - square - center / 2, 0)
..arcToPoint(
Offset(size.width / 2 - center / 2, square),
radius: Radius.circular(8.r),
clockwise: true,
)
..lineTo(size.width / 2 + center / 2, size.height - square)
..arcToPoint(
Offset(size.width / 2 + square + center / 2, size.height),
radius: Radius.circular(8.r),
clockwise: false,
)
..lineTo(size.width, size.height)
..lineTo(size.width, 0)
..close();

if (!leftToRight) {
rightPath = Path()
..moveTo(size.width / 2 + square + center / 2, 0)
..arcToPoint(
Offset(size.width / 2 + center / 2, square),
radius: Radius.circular(8.r),
clockwise: false,
)
..lineTo(size.width / 2 - center / 2, size.height - square)
..arcToPoint(
Offset(size.width / 2 - square - center / 2, size.height),
radius: Radius.circular(8.r),
clockwise: true,
)
..lineTo(size.width, size.height)
..lineTo(size.width, 0)
..close();
}

canvas.drawPath(leftPath, paintLeft);
canvas.drawPath(rightPath, paintRight);
}

@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}