flutter 绘画
效果:
实现:
import 'dart:math'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; class CustomPaintPainterPage extends StatefulWidget { const CustomPaintPainterPage({Key? key}) : super(key: key); @override State<CustomPaintPainterPage> createState() => _CustomPaintPainterPageState(); } class _CustomPaintPainterPageState extends State<CustomPaintPainterPage> { @override Widget build(BuildContext context) { return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('基础理解', style: TextStyle(color: Colors.red)), const Text(" Paint:在画布上绘制时要使用的样式的描述(可描述为画笔)"), const Text(" Canvas:用于记录图形操作的界面(可描述为画布)"), const Text( " Path:可以使用Canvas.drawPath在画布上绘制路径。并可用于使用Canvas.clipPath创建剪辑区域"), const SizedBox(height: 20), const Text("在flutter中并不是直接使用Paint、Canvas,而是使用进一步封装的widget,如下"), const Text( '官方https://api.flutter.dev/flutter/widgets/CustomPaint-class.html'), const Text('CustomPaint:', style: TextStyle(color: Colors.red)), const Text(' 提供在绘制阶段绘制的画布,并利用CustomPainter来运行绘制指令的组件'), const Text(' 它通常会将painter和child内嵌在其构造函数内:'), const Text(' painter是CustomPainter类的实例'), const Text('CustomPainter:', style: TextStyle(color: Colors.red)), const Text( ' CustomPaint(在widgets库中)和RenderCustomPaint(在渲染库中)使用的接口'), const Text(' 画布绘制发生的地方'), const Text(' CustomPainter基类化,需要重写两个功能paint和shouldRepaint:'), const Text(' CustomPainter基类化,需要重写两个功能paint和shouldRepaint:'), const Text( ' paint方法是用于绘制的,它有两个参数Canvas和Size,所有的绘制指令应在指定大小的范围中发生;shouldRepaint它控制painter重绘的时间'), const SizedBox( height: 50, ), CustomPaint( foregroundPainter: MyPainter(), child: Container( color: Colors.blue, width: 600, height: 200, ), ), const SizedBox( height: 50, ), CustomPaint( foregroundPainter: SmilePainter(), child: Container( color: Colors.blue, width: 600, height: 200, ), ), const SizedBox( height: 50, ), CustomPaint( foregroundPainter: Sky(), child: Container( color: Colors.white, width: 600, height: 200, ), ), const SizedBox( height: 50, ), CustomPaint( painter: Sky(), child: const Center( child: Text( 'Once upon a time...', style: TextStyle( fontSize: 40.0, fontWeight: FontWeight.w900, color: Color(0xFFFFFFFF), ), ), ), ), const SizedBox( height: 50, ), CustomPaint( foregroundPainter: Pentagram(Colors.black), child: Container( color: Colors.blue, width: 200, height: 200, ), ), ], ), ); } } class Pentagram extends CustomPainter { Pentagram(this.color); //画笔的颜色 final Color color; @override void paint(Canvas canvas, Size size) { Offset getOffsetPosition(int degreen, double radius) { //角度转成弧度 var radian = degreen * pi / 180; var dx = sin(radian) * radius; var dy = cos(radian) * radius; return Offset(dx + radius, dy + radius); } final paint = Paint() ..color = Colors.black ..style = PaintingStyle.fill; var initDegreen = 180; double radius = 100; // 连接五角星的五个顶点,360/5,每个是72度 final path = Path(); var posOne = getOffsetPosition(initDegreen, radius); var posTwo = getOffsetPosition(72 + initDegreen, radius); var posThree = getOffsetPosition(144 + initDegreen, radius); var posfour = getOffsetPosition(216 + initDegreen, radius); var posFive = getOffsetPosition(288 + initDegreen, radius); path.moveTo(posOne.dx, posOne.dy); path.lineTo(posfour.dx, posfour.dy); path.lineTo(posTwo.dx, posTwo.dy); path.lineTo(posFive.dx, posFive.dy); path.lineTo(posThree.dx, posThree.dy); //最后用close的方式把path封闭起来 path.close(); canvas.drawPath(path, paint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { // TODO: implement shouldRepaint return false; } } class SmilePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { const radius = 100.0; final center = Offset(size.width / 2.0, size.height / 2.0); final paint = Paint()..color = Colors.yellow; canvas.drawCircle(center, radius, paint); final smilePaint = Paint() ..style = PaintingStyle.stroke ..strokeWidth = 10; canvas.drawArc(Rect.fromCircle(center: center, radius: radius / 2), 0, pi, false, smilePaint); canvas.drawCircle( Offset(center.dx - radius / 2.0, center.dy - radius / 2.0), 10, Paint()); canvas.drawCircle( Offset(center.dx + radius / 2.0, center.dy - radius / 2.0), 10, Paint()); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { // TODO: implement shouldRepaint return false; } } class Sky extends CustomPainter { @override void paint(Canvas canvas, Size size) { final Rect rect = Offset.zero & size; const RadialGradient gradient = RadialGradient( center: Alignment(0.7, -0.6), radius: 0.2, colors: <Color>[Color(0xFFFFFF00), Color(0xFF0099FF)], stops: <double>[0.4, 1.0], ); canvas.drawRect( rect, Paint()..shader = gradient.createShader(rect), ); } @override SemanticsBuilderCallback get semanticsBuilder { return (Size size) { // Annotate a rectangle containing the picture of the sun // with the label "Sun". When text to speech feature is enabled on the // device, a user will be able to locate the sun on this picture by // touch. Rect rect = Offset.zero & size; final double width = size.shortestSide * 0.4; rect = const Alignment(0.8, -0.9).inscribe(Size(width, width), rect); return <CustomPainterSemantics>[ CustomPainterSemantics( rect: rect, properties: const SemanticsProperties( label: 'Sun', textDirection: TextDirection.rtl, ), ), ]; }; } @override bool shouldRepaint(Sky oldDelegate) => false; @override bool shouldRebuildSemantics(Sky oldDelegate) => false; } class MyPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { Paint onePaint = Paint() ..style = PaintingStyle.fill // 是否填充 ..color = Colors.red ..strokeWidth = 3; Paint twoPaint = Paint() ..style = PaintingStyle.stroke // 是否填充 ..color = Colors.white ..strokeWidth = 3; // canvas.drawPaint(newPaint); canvas.drawCircle(const Offset(75, 95), 50, onePaint); canvas.drawLine(const Offset(70, 30), const Offset(170, 30), onePaint); canvas.drawRect( Rect.fromPoints(const Offset(140, 80), const Offset(190, 60)), onePaint); canvas.drawCircle(const Offset(275, 95), 50, twoPaint); canvas.drawLine(const Offset(270, 30), const Offset(370, 30), twoPaint); canvas.drawRect( Rect.fromPoints(const Offset(340, 80), const Offset(390, 60)), twoPaint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { // TODO: implement shouldRepaint return false; } }