Flutter框架跨平台鸿蒙开发——Container自定义绘制

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

相关推荐
yingzicat2 小时前
华为和华三交换机和路由器时间配置
网络·华为
IT陈图图2 小时前
Flutter × OpenHarmony 实战:从 0 构建视频播放器的分类导航模块
flutter·华为·音视频·openharmony
小风呼呼吹儿2 小时前
Flutter 框架跨平台鸿蒙开发 - 简易字幕制作器:打造专业级字幕编辑工具
flutter·华为·harmonyos
时光慢煮3 小时前
Flutter × OpenHarmony 文件管家-构建文件管理器主界面与存储设备卡片
flutter·华为·开源·openharmony
ITUnicorn3 小时前
【HarmomyOS6】ArkTS入门(三)
华为·harmonyos·arkts·鸿蒙·harmonyos6
[H*]3 小时前
Flutter框架跨平台鸿蒙开发——ListView Widget基础用法
flutter·华为·harmonyos
A懿轩A3 小时前
【2026 最新】Kuikly 编译开发 OpenHarmony 项目逐步详细教程带图操作Android Studio编译(Windows)
windows·harmonyos·鸿蒙·openharmony·kuikly
[H*]3 小时前
Flutter框架跨平台鸿蒙开发——Button样式定制
flutter·华为·harmonyos
不会写代码0003 小时前
Flutter 框架跨平台鸿蒙开发 - 全国图书馆查询:探索知识的殿堂
flutter·华为·harmonyos