
Container组件不仅可以使用BoxDecoration进行装饰,还可以通过CustomPaint实现更加复杂的自定义绘制效果。本文将深入探讨Container的自定义绘制能力,包括基础绘制、图形绘制、文字绘制、路径绘制以及高级绘图技巧。
一、CustomPaint基础
CustomPaint是一个强大的自定义绘制组件,可以将其作为Container的子组件,实现各种绘制效果。
1.1 CustomPaint工作原理
CustomPaint
painter属性
size属性
child属性
CustomPainter
paint方法
shouldRepaint
指定尺寸
默认child尺寸
Size.expand
背景绘制
前景绘制
叠加绘制
1.2 CustomPaint与Container结合
dart
class CustomPaintContainer extends StatelessWidget {
const CustomPaintContainer({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: 200,
height: 200,
padding: const EdgeInsets.all(16),
child: CustomPaint(
size: const Size(150, 150),
painter: _MyPainter(),
child: const Center(
child: Text(
'自定义绘制',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
),
);
}
}
class _MyPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue
..style = PaintingStyle.fill;
// 绘制圆形背景
canvas.drawCircle(
Offset(size.width / 2, size.height / 2),
size.width / 2,
paint,
);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
二、图形绘制
2.1 基本图形
dart
class BasicShapesPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// 圆形
final circlePaint = Paint()
..color = Colors.red
..style = PaintingStyle.fill;
canvas.drawCircle(
const Offset(50, 50),
30,
circlePaint,
);
// 矩形
final rectPaint = Paint()
..color = Colors.green
..style = PaintingStyle.fill;
canvas.drawRect(
const Rect.fromLTWH(100, 30, 60, 40),
rectPaint,
);
// 圆角矩形
final roundedRectPaint = Paint()
..color = Colors.blue
..style = PaintingStyle.fill;
canvas.drawRRect(
RRect.fromRectAndRadius(
const Rect.fromLTWH(40, 100, 80, 50),
const Radius.circular(10),
),
roundedRectPaint,
);
// 椭圆
final ovalPaint = Paint()
..color = Colors.orange
..style = PaintingStyle.fill;
canvas.drawOval(
const Rect.fromLTWH(140, 100, 80, 50),
ovalPaint,
);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
2.2 多边形
dart
class PolygonPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.purple
..style = PaintingStyle.fill;
// 三角形
final path1 = Path()
..moveTo(50, 30)
..lineTo(30, 70)
..lineTo(70, 70)
..close();
canvas.drawPath(path1, paint);
// 五边形
final path2 = Path();
final center = const Offset(150, 50);
final radius = 30;
for (int i = 0; i < 5; i++) {
final angle = (i * 2 * 3.14159) / 5 - 3.14159 / 2;
final x = center.dx + radius * cos(angle);
final y = center.dy + radius * sin(angle);
if (i == 0) {
path2.moveTo(x, y);
} else {
path2.lineTo(x, y);
}
}
path2.close();
final polygonPaint = Paint()
..color = Colors.teal
..style = PaintingStyle.fill;
canvas.drawPath(path2, polygonPaint);
// 六边形
final path3 = Path();
final center2 = const Offset(100, 120);
final radius2 = 30;
for (int i = 0; i < 6; i++) {
final angle = (i * 2 * 3.14159) / 6;
final x = center2.dx + radius2 * cos(angle);
final y = center2.dy + radius2 * sin(angle);
if (i == 0) {
path3.moveTo(x, y);
} else {
path3.lineTo(x, y);
}
}
path3.close();
final hexPaint = Paint()
..color = Colors.orange
..style = PaintingStyle.fill;
canvas.drawPath(path3, hexPaint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
2.3 星形
dart
class StarPainter extends CustomPainter {
final int points;
final Color color;
const StarPainter({
this.points = 5,
this.color = Colors.yellow,
});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..style = PaintingStyle.fill;
final path = Path();
final center = Offset(size.width / 2, size.height / 2);
final outerRadius = size.width / 2;
final innerRadius = outerRadius * 0.4;
for (int i = 0; i < points * 2; i++) {
final radius = i % 2 == 0 ? outerRadius : innerRadius;
final angle = (i * 3.14159) / points - 3.14159 / 2;
final x = center.dx + radius * cos(angle);
final y = center.dy + radius * sin(angle);
if (i == 0) {
path.moveTo(x, y);
} else {
path.lineTo(x, y);
}
}
path.close();
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant StarPainter oldDelegate) {
return points != oldDelegate.points || color != oldDelegate.color;
}
}
三、文字绘制
3.1 基础文字
dart
class TextPainterExample extends StatelessWidget {
const TextPainterExample({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: 200,
height: 150,
color: Colors.grey.shade100,
child: CustomPaint(
painter: _TextPainter(),
),
);
}
}
class _TextPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final textPainter = TextPainter(
text: const TextSpan(
text: '自定义绘制文字',
style: TextStyle(
fontSize: 20,
color: Colors.blue,
fontWeight: FontWeight.bold,
),
),
textDirection: TextDirection.ltr,
);
textPainter.layout();
// 居中绘制
final x = (size.width - textPainter.width) / 2;
final y = (size.height - textPainter.height) / 2;
textPainter.paint(canvas, Offset(x, y));
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
3.2 多行文字
dart
class MultilineTextPainter extends CustomPainter {
final String text;
final double maxWidth;
final TextStyle style;
MultilineTextPainter({
required this.text,
this.maxWidth = 180,
this.style = const TextStyle(
fontSize: 16,
color: Colors.black87,
),
});
@override
void paint(Canvas canvas, Size size) {
final textPainter = TextPainter(
text: TextSpan(text: text, style: style),
textDirection: TextDirection.ltr,
maxLines: null,
);
textPainter.layout(maxWidth: maxWidth);
// 绘制多行文字
textPainter.paint(canvas, Offset(10, 10));
// 绘制边框
final borderPaint = Paint()
..color = Colors.blue
..style = PaintingStyle.stroke
..strokeWidth = 2;
canvas.drawRect(
Rect.fromLTWH(10, 10, maxWidth, textPainter.height),
borderPaint,
);
}
@override
bool shouldRepaint(covariant MultilineTextPainter oldDelegate) {
return text != oldDelegate.text ||
maxWidth != oldDelegate.maxWidth ||
style != oldDelegate.style;
}
}
3.3 旋转文字
dart
class RotatedTextPainter extends CustomPainter {
final String text;
final double angle;
final Color color;
const RotatedTextPainter({
required this.text,
this.angle = 0,
this.color = Colors.blue,
});
@override
void paint(Canvas canvas, Size size) {
final textPainter = TextPainter(
text: TextSpan(
text: text,
style: TextStyle(
fontSize: 18,
color: color,
fontWeight: FontWeight.bold,
),
),
textDirection: TextDirection.ltr,
);
textPainter.layout();
canvas.save();
canvas.translate(size.width / 2, size.height / 2);
canvas.rotate(angle);
canvas.translate(-textPainter.width / 2, -textPainter.height / 2);
textPainter.paint(canvas, Offset.zero);
canvas.restore();
}
@override
bool shouldRepaint(covariant RotatedTextPainter oldDelegate) {
return text != oldDelegate.text ||
angle != oldDelegate.angle ||
color != oldDelegate.color;
}
}
四、渐变绘制
4.1 线性渐变
dart
class LinearGradientPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final rect = Rect.fromLTWH(0, 0, size.width, size.height);
final gradient = LinearGradient(
colors: [Colors.blue, Colors.purple],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
);
final paint = Paint()
..shader = gradient.createShader(rect)
..style = PaintingStyle.fill;
canvas.drawRect(rect, paint);
// 绘制文字
final textPainter = TextPainter(
text: const TextSpan(
text: '线性渐变',
style: TextStyle(
fontSize: 24,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(
(size.width - textPainter.width) / 2,
(size.height - textPainter.height) / 2,
),
);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
4.2 径向渐变
dart
class RadialGradientPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = size.width / 2;
final gradient = RadialGradient(
colors: [
Colors.orange.shade300,
Colors.orange.shade600,
Colors.orange.shade900,
],
stops: const [0.0, 0.5, 1.0],
);
final paint = Paint()
..shader = gradient.createShader(
Rect.fromCircle(center: center, radius: radius),
)
..style = PaintingStyle.fill;
canvas.drawCircle(center, radius, paint);
// 绘制文字
final textPainter = TextPainter(
text: const TextSpan(
text: '径向渐变',
style: TextStyle(
fontSize: 24,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(
(size.width - textPainter.width) / 2,
(size.height - textPainter.height) / 2,
),
);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
4.3 扫描渐变
dart
class SweepGradientPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = size.width / 2;
final gradient = SweepGradient(
colors: [
Colors.red,
Colors.yellow,
Colors.green,
Colors.blue,
Colors.red,
],
stops: const [0.0, 0.25, 0.5, 0.75, 1.0],
);
final paint = Paint()
..shader = gradient.createShader(
Rect.fromCircle(center: center, radius: radius),
)
..style = PaintingStyle.fill;
canvas.drawCircle(center, radius, paint);
// 绘制文字
final textPainter = TextPainter(
text: const TextSpan(
text: '扫描渐变',
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold,
shadows: [
Shadow(
color: Colors.black,
offset: Offset(2, 2),
blurRadius: 3,
),
],
),
),
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(
(size.width - textPainter.width) / 2,
(size.height - textPainter.height) / 2,
),
);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
五、路径绘制
5.1 贝塞尔曲线
dart
class BezierCurvePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue
..style = PaintingStyle.stroke
..strokeWidth = 3;
final path = Path()
..moveTo(20, 150)
..cubicTo(50, 50, 150, 50, 180, 150);
canvas.drawPath(path, paint);
// 绘制控制点
final pointPaint = Paint()
..color = Colors.red
..style = PaintingStyle.fill;
canvas.drawCircle(const Offset(50, 50), 5, pointPaint);
canvas.drawCircle(const Offset(150, 50), 5, pointPaint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
5.2 波浪线
dart
class WavePainter extends CustomPainter {
final double animation;
const WavePainter({this.animation = 0.0});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue.withOpacity(0.6)
..style = PaintingStyle.fill;
final path = Path();
final waveHeight = 20.0;
final waveLength = size.width / 2;
path.moveTo(0, size.height);
for (double x = 0; x <= size.width; x++) {
final y = size.height / 2 +
sin((x + animation * 100) * 2 * 3.14159 / waveLength) *
waveHeight;
if (x == 0) {
path.moveTo(x, y);
} else {
path.lineTo(x, y);
}
}
path.lineTo(size.width, size.height);
path.close();
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant WavePainter oldDelegate) {
return animation != oldDelegate.animation;
}
}
5.3 复杂路径
dart
class ComplexPathPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.purple
..style = PaintingStyle.stroke
..strokeWidth = 2
..strokeCap = StrokeCap.round;
final path = Path()
..moveTo(50, 50)
..lineTo(150, 50)
..lineTo(150, 100)
..arcToPoint(
const Offset(100, 150),
radius: const Radius.circular(50),
clockwise: true,
)
..arcToPoint(
const Offset(50, 100),
radius: const Radius.circular(50),
clockwise: true,
)
..close();
canvas.drawPath(path, paint);
// 填充
final fillPaint = Paint()
..color = Colors.purple.withOpacity(0.3)
..style = PaintingStyle.fill;
canvas.drawPath(path, fillPaint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
六、阴影效果
6.1 基础阴影
dart
class ShadowPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// 绘制阴影
final shadowPaint = Paint()
..color = Colors.black.withOpacity(0.2)
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 10);
canvas.drawCircle(
const Offset(size.width / 2 + 5, size.height / 2 + 5),
40,
shadowPaint,
);
// 绘制主体
final mainPaint = Paint()
..color = Colors.blue
..style = PaintingStyle.fill;
canvas.drawCircle(
Offset(size.width / 2, size.height / 2),
40,
mainPaint,
);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
6.2 多层阴影
dart
class MultiShadowPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final rect = Rect.fromLTWH(
(size.width - 100) / 2,
(size.height - 60) / 2,
100,
60,
);
// 阴影层1
final shadowPaint1 = Paint()
..color = Colors.black.withOpacity(0.1)
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 5);
canvas.drawRect(
rect.translate(0, 2),
shadowPaint1,
);
// 阴影层2
final shadowPaint2 = Paint()
..color = Colors.black.withOpacity(0.15)
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 10);
canvas.drawRect(
rect.translate(0, 4),
shadowPaint2,
);
// 主体
final mainPaint = Paint()
..color = Colors.white
..style = PaintingStyle.fill;
canvas.drawRect(rect, mainPaint);
// 边框
final borderPaint = Paint()
..color = Colors.blue
..style = PaintingStyle.stroke
..strokeWidth = 2;
canvas.drawRect(rect, borderPaint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
七、完整自定义绘制示例
dart
class CustomPaintingExample extends StatelessWidget {
const CustomPaintingExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Container自定义绘制'),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
const Text(
'基本图形',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
Container(
height: 180,
color: Colors.grey.shade100,
child: const CustomPaint(
painter: BasicShapesPainter(),
),
),
const SizedBox(height: 24),
const Text(
'多边形',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
Container(
height: 180,
color: Colors.grey.shade100,
child: const CustomPaint(
painter: PolygonPainter(),
),
),
const SizedBox(height: 24),
const Text(
'星形',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: Container(
height: 150,
color: Colors.grey.shade100,
child: const CustomPaint(
painter: StarPainter(points: 5),
),
),
),
const SizedBox(width: 12),
Expanded(
child: Container(
height: 150,
color: Colors.grey.shade100,
child: const CustomPaint(
painter: StarPainter(points: 6, color: Colors.orange),
),
),
),
],
),
const SizedBox(height: 24),
const Text(
'文字绘制',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
Container(
height: 120,
color: Colors.grey.shade100,
child: const CustomPaint(
painter: TextPainterExample(),
),
),
const SizedBox(height: 24),
const Text(
'渐变效果',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: Container(
height: 120,
child: const CustomPaint(
painter: LinearGradientPainter(),
),
),
),
const SizedBox(width: 12),
Expanded(
child: Container(
height: 120,
child: const CustomPaint(
painter: RadialGradientPainter(),
),
),
),
const SizedBox(width: 12),
Expanded(
child: Container(
height: 120,
child: const CustomPaint(
painter: SweepGradientPainter(),
),
),
),
],
),
const SizedBox(height: 24),
const Text(
'贝塞尔曲线',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
Container(
height: 200,
color: Colors.grey.shade100,
child: const CustomPaint(
painter: BezierCurvePainter(),
),
),
const SizedBox(height: 24),
const Text(
'阴影效果',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: Container(
height: 120,
color: Colors.grey.shade100,
child: const CustomPaint(
painter: ShadowPainter(),
),
),
),
const SizedBox(width: 12),
Expanded(
child: Container(
height: 120,
color: Colors.grey.shade100,
child: const CustomPaint(
painter: MultiShadowPainter(),
),
),
),
],
),
],
),
);
}
}
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net