Flutter 绘图
时间:2023-02-03 13:00:00
文章目录
- Flutter 绘图
-
- 概述
- 绘制线
- 绘制点
- 绘制矩形
- 画圆角矩形
- 绘制圆形
- 绘制椭圆
- 绘制弧
- 绘制路径
- 绘制文本
- 绘制图片
Flutter 绘图
概述
- Canvas(画布)用于在上面画图形,如画点、线、矩形、圆形、路径、图像等。
- Paint(画笔)用于设置画布上画图的颜色、厚度、抗锯齿等绘图风格。
- 在Flutter需要使用绘图
CustomPaint
和CustomPainter
,CustomPaint
用来承载画布的画板,CustomPainter
可理解为画布,承载绘制内容。
绘制线
Container( width: MediaQuery.of(context).size.width, height: 200, color: Colors.white, child: CustomPaint( //定义画板大小 size: const Size(200, 200), ///配置画布 painter: LinePainter(), //子节点,RepaintBoundary创建新的图层layer,避免重复绘制 child: const RepaintBoundary( child: Center(child: Text("hello world")), ), ), )
class LinePainter extends CustomPainter { //定义画笔:画笔颜色,画笔宽度 final Paint _paint = Paint() ..color = Colors.blue ..strokeWidth = 4; /// 绘制流程 @override void paint(Canvas canvas, Size size) { canvas.drawLine(const Offset(20, 20), const Offset(100, 100), _paint); } /// 刷新是否重绘 @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } }
绘制点
Container( width: MediaQuery.of(context).size.width, height: 200, color: Colors.white, child: CustomPaint( size: const Size(200, 200), painter: PointPainter(), ), )
class PointPainter extends CustomPainter { final Paint _paint = Paint() ///画笔颜色 ..color = Colors.blue ///画笔笔头风格 ..strokeCap = StrokeCap.round //抗锯齿 ..isAntiAlias = true //填充样式 ..style = PaintingStyle.fill ///画笔宽度 ..strokeWidth = 20; @override void paint(Canvas canvas, Size size) { canvas.drawPoints( ui.PointMode.points, [ const Offset(50, 50), const Offset(100, 150), const Offset(150, 150), ], _paint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return true; } }
绘制矩形
Container( width: MediaQuery.of(context).size.width, height: 200, color: Colors.white, child: CustomPaint( size: const Size(200, 200), painter: RectPainter(), ), )
class RectPainter extends CustomPainter { final Paint _paint = Paint() ..color = Colors.blueAccent ..strokeCap = StrokeCap.round ..isAntiAlias = true ..style = PaintingStyle.fill ..strokeWidth = 2; @override void paint(Canvas canvas, Size size) { //方法1:左上右下设置4个顶点 // Rect rect = const Rect.fromLTRB(20, 40, 150, 100); //方二:设置2左上两个顶点和宽度 // Rect rect = const Rect.fromLTWH(20, 40, 150, 100); //方法三:设置中心和半径 // Rect rect = Rect.fromCircle(center: const Offset(100, 100), radius: 50); //方法四:设置圆心和宽高 Rect rect = Rect.fromCenter(center: const Offset(100, 100), width: 100, height: 100); canvas.drawRect(rect, _paint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return true; } }
画圆角矩形
Container( width: MediaQuery.of(context).size.width, height: 200, color: Colors.white, child: CustomPaint( size: const Size(200, 200), painter: RRectPainter(), ), )
class RRectPainter extends CustomPainter { final Paint _paint = Paint() ..color = Colors.blueAccent ..strokeCap = StrokeCap.round ..isAntiAlias = true ..style = PaintingStyle.fill ..strokeWidth = 2; @override
void paint(Canvas canvas, Size size) {
Rect rect = Rect.fromCenter(center: const Offset(100, 100), width: 100, height: 100);
RRect rRect = RRect.fromRectAndRadius(rect, const Radius.circular(20));
canvas.drawRRect(rRect, _paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
绘制圆形
Container(
width: MediaQuery.of(context).size.width,
height: 200,
color: Colors.white,
child: CustomPaint(
size: const Size(200, 200),
painter: CirclePainter(),
),
)
class CirclePainter extends CustomPainter {
final Paint _paint = Paint()
..color = Colors.blueAccent
..strokeCap = StrokeCap.round
..isAntiAlias = true
..style = PaintingStyle.stroke
..strokeWidth = 2;
@override
void paint(Canvas canvas, Size size) {
canvas.drawCircle(const Offset(100, 100), 40, _paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
绘制椭圆
Container(
width: MediaQuery.of(context).size.width,
height: 200,
color: Colors.white,
child: CustomPaint(
size: const Size(200, 200),
painter: OvalPainter(),
),
)
class OvalPainter extends CustomPainter {
final Paint _paint = Paint()
..color = Colors.blueAccent
..strokeCap = StrokeCap.round
..isAntiAlias = true
..style = PaintingStyle.stroke
..strokeWidth = 2;
@override
void paint(Canvas canvas, Size size) {
Rect rect = Rect.fromCenter(center: const Offset(150, 100), width: 200, height: 100);
canvas.drawOval(rect, _paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
绘制弧
Container(
width: MediaQuery.of(context).size.width,
height: 200,
color: Colors.white,
child: CustomPaint(
size: const Size(200, 200),
painter: ArcPainter(),
),
)
class ArcPainter extends CustomPainter {
final Paint _paint = Paint()
..color = Colors.blueAccent
..style = PaintingStyle.stroke
..strokeWidth = 2;
@override
void paint(Canvas canvas, Size size) {
const Rect rect = Rect.fromLTRB(50, 50, 150, 150);
//参数依次是:矩形范围、开始弧度、结束弧度、是否连接圆心、画笔
canvas.drawArc(rect, 0, 2, false, _paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
绘制路径
Container(
width: double.infinity,
height: 200,
color: Colors.white,
child: CustomPaint(
size: const Size(200, 200),
painter: PathPainter(),
),
)
class PathPainter extends CustomPainter {
final Paint _paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..isAntiAlias = true
..style = PaintingStyle.stroke
..strokeWidth = 2;
@override
void paint(Canvas canvas, Size size) {
final Path path = Path();
path.moveTo(100, 80);
path.lineTo(100, 150);
path.lineTo(160, 150);
path.close();
canvas.drawPath(path, _paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
Container(
width: MediaQuery.of(context).size.width,
height: 200,
color: Colors.white,
child: CustomPaint(
size: const Size(200, 200),
painter: PathPainter2(),
),
)
class PathPainter2 extends CustomPainter {
final Paint _paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..isAntiAlias = true
..style = PaintingStyle.stroke
..strokeWidth = 2;
@override
void paint(Canvas canvas, Size size) {
final Path path = Path();
path.moveTo(100, 80);
path.lineTo(200, 80);
path.addArc(
Rect.fromCenter(center: const Offset(150, 80), width: 100, height: 100),
0,
0.5 * pi,
);
canvas.drawPath(path, _paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
绘制文本
Container(
width: MediaQuery.of(context).size.width,
height: 200,
color: Colors.white,
child: CustomPaint(
size: const Size(200, 200),
painter: TextPainter(),
),
)
class TextPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
ui.ParagraphBuilder builder = ui.ParagraphBuilder(
ui.ParagraphStyle(
//文字方向
textDirection: TextDirection.ltr,
//最大行数
maxLines: 2,
//文本居中
textAlign: TextAlign.center,
//字体加粗
fontWeight: FontWeight.bold,
//字体样式
fontStyle: FontStyle.normal,
//字体大小
fontSize: 24,
//超出范围显示内容
ellipsis: "...",
//行间距
height: 1.2,
textHeightBehavior: const TextHeightBehavior(
applyHeightToFirstAscent: true,
applyHeightToLastDescent: true,
),
),
);
//设置文字样式
builder.pushStyle(ui.TextStyle(
color: Colors.red,
fontSize: 20,
height: 1,
fontWeight: FontWeight.w500,
));
String text = "床前明月光,疑是地上霜;举头望明月,低头思故乡。";
builder.addText(text);
//文本绘制最大宽度
ui.ParagraphConstraints constraints = const ui.ParagraphConstraints(width: 300);
ui.Paragraph paragraph = builder.build()..layout(constraints);
//绘制文字
canvas.drawParagraph(paragraph, const Offset(40, 40));
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
绘制图片
class ImagePaintPage extends StatefulWidget {
const ImagePaintPage({Key? key}) : super(key: key);
@override
State createState() => _ImagePaintPageState();
}
class _ImagePaintPageState extends State {
ui.Image? _image;
@override
void initState() {
super.initState();
loadImage();
}
loadImage() async {
// _image = await loadImageByAssets();
_image = await loadImageByNet();
if (!mounted) return;
setState(() {});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
const Text("绘制图片"),
Container(
width: MediaQuery.of(context).size.width,
height: 200,
color: Colors.white,
child: Center(
child: Stack(
children: [
_image == null
? const CircularProgressIndicator()
: CustomPaint(
size: const Size(200, 200),
painter: ImagePainter(_image!),
),
],
),
),
),
],
);
}
/// 加载Assets图片
Future loadImageByAssets() async {
AssetImage assetImage = const AssetImage("assets/images/bird.jpg");
Completer completer = Completer();
ImageStream imageStream = assetImage.resolve(ImageConfiguration.empty);
late ImageStreamListener listener;
listener = ImageStreamListener((ImageInfo imageInfo, bool synchronousCall) {
final ui.Image image = imageInfo.image;
completer.complete(image);
imageStream.removeListener(listener);
});
imageStream.addListener(listener);
return completer.future;
}
/// 加载网络图片
Future loadImageByNet() async {
String imageUrl = "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png";
NetworkImage networkImage = NetworkImage(imageUrl);
Completer completer = Completer();
ImageStream imageStream = networkImage.resolve(ImageConfiguration.empty);
late ImageStreamListener listener;
listener = ImageStreamListener((ImageInfo imageInfo, bool synchronousCall) {
final ui.Image image = imageInfo.image;
completer.complete(image);
imageStream.removeListener(listener);
});
imageStream.addListener(listener); //添加监听
return completer.future;
}
}
class ImagePainter extends CustomPainter {
final Paint _paint = Paint()..isAntiAlias = true;
final ui.Image _image;
ImagePainter(this._image);
@override
void paint(Canvas canvas, Size size) {
//原图区域,一般传原图的宽高
Rect src = const Offset(0.0, 0.0) & Size(_image.width.toDouble(), _image.height.toDouble());
//目标显示区域
Rect dst = const Offset(0.0, 0.0) & const Size(200, 200);
canvas.drawImageRect(_image, src, dst, _paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}