42Flutter 自定义Widget

Flutter 自定义Widget

一、相应属性介绍

1.CustomPainter介绍

CustomPaint可以称之为动画鼻祖,它可以实现任何酷炫的动画和效果。CustomPaint本身没有动画属性,仅仅是绘制属性,一般情况下,CustomPaint会和动画控制配合使用,达到理想的效果。
CustomerPainter是真实绘制的基础类,需要绘制的图形和画笔都是在此类中实现,一般会自定义一个类继承此基类,然后重写两个方法:

import 'package:flutter/material.dart';
class LCPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // 绘制
  }
  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return  true; //属性不一致时,需要重新绘制
  }
}

2.CustomPaint

CustomPaint 也是一个 Widget。
你可把它嵌到视图树的任意一个节点位置

属性
painterCustomPainter背景画笔,绘制内容会显示在child子节点后面
foregroundPainterCustomPainter前景画笔,绘制内容会显示在child子节点前面
sizeSize设置绘制区域的大小。如果有child,则忽略该参数,且绘制区域为child的尺寸
isComplexbool是否复杂的绘制,如果是,Flutter会应用一些缓存策略来减少重复渲染的开销。默认false
willChangebool和isComplex配合使用,当启用缓存时,该属性代表在下一帧中绘制是否会改变。默认false
childWidget没错,CustomPaint是可以包含一个子节点的

3.Painter绘制属性

canvas中有多个与绘制相关的方法,如drawLine()、drawRect()、drawOval()、drawOval()、等方法。

属性类型说明
isAntiAliasbool是否开启抗锯齿,开启抗锯齿能够是边缘平滑,当然也更消耗系统资源
colorColor颜色
colorFilterColorFilter会对颜色进行变换
filterQualityFilterQuality设置绘制的图像质量
invertColorsbool是否使用反向颜色。绘制图片时也能够反转图片的颜色
maskFilterMaskFilter设置遮罩效果。比如高斯模糊
shaderShader渐变颜色。会覆盖color
strokeCapStrokeCap设置绘制形状的边缘风格。如圆角、方形等
strokeJoinStrokeJoin设置两个绘制形状衔接处的风格。如圆角、方形等
strokeWidthdouble画笔的宽度
stylePaintingStyle填充方式。PaintingStyle.fill-充满;PaintingStyle.stroke-空心
blendModeBlendMode像素混合模式。当画一个shape或者合成图层的时候会生效。

4.Canvas画布

与Android几乎一摸一样,按照android的自定义View的思想去理解

它包含了很多基础的绘制操作,通过组合这些基础的绘制操作,可以绘制出几乎任何的视图。

Canvas 的操作主要有两类:

  • 针对 Canvas 的变换操作,如平移、旋转、缩放、图层等操作。
  • 绘制基础图形的操作,如线段、路径、图片、几何图形等。

二、api介绍

1.绘制背景
canvas.drawColor(Colors.orange, BlendMode.srcIn);
2.drawPoints 绘制点/线

PointMode 包括 points 点 / lines 线 / polygon 多边形;注意 lines 为每两点之间的连线,若为奇数个点,最后一个没有与之相连的点

该api与Android不同

class LCPainter extends CustomPainter {

  @override
  void paint(Canvas canvas, Size size) {
    // 绘制
    canvas.drawColor(Colors.orange, BlendMode.srcIn);
    // 绘制点
    canvas.drawPoints(
        PointMode.points,
        [
          Offset(30.0, 30.0),
          Offset(60.0, 30.0),
          Offset(60.0, 60.0),
          Offset(30.0, 60.0)
        ],
        Paint()..strokeWidth = 10.0);
    canvas.drawPoints(
        PointMode.points,
        [
          Offset(160.0, 30.0),
          Offset(190.0, 30.0),
          Offset(190.0, 60.0),
          Offset(160.0, 60.0)
        ],
        Paint()
          ..strokeWidth = 10.0
          ..strokeCap = StrokeCap.round);
    // 绘制线
    canvas.drawPoints(
        PointMode.lines,
        [
          Offset(30.0, 100.0),
          Offset(60.0, 100.0),
          Offset(60.0, 130.0),
          Offset(30.0, 130.0)
        ],
        Paint()
          ..strokeWidth = 4.0
          ..strokeCap = StrokeCap.round);
    // 绘制多边形
    canvas.drawPoints(
        PointMode.polygon,
        [
          Offset(160.0, 100.0),
          Offset(190.0, 100.0),
          Offset(190.0, 130.0),
          Offset(160.0, 130.0)
        ],
        Paint()
          ..strokeWidth = 4.0
          ..strokeCap = StrokeCap.round);
  }

3.绘制线
import 'dart:ui';

import 'package:flutter/material.dart';

class LinePainter extends CustomPainter {

  @override
  void paint(Canvas canvas, Size size) {
    // 绘制
    canvas.drawColor(Colors.orange, BlendMode.srcIn);
    // 绘制点
    canvas.drawLine(
        Offset(30.0, 30.0),
        Offset(200 - 30.0, 30.0),
        Paint()
          ..strokeWidth = 10.0
          ..strokeCap = StrokeCap.butt);
    canvas.drawLine(
        Offset(30.0, 60.0),
        Offset(200 - 30.0, 60.0),
        Paint()
          ..strokeWidth = 10.0
          ..strokeCap = StrokeCap.round);
    canvas.drawLine(
        Offset(30.0, 90.0),
        Offset(200 - 30.0, 90.0),
        Paint()
          ..strokeWidth = 10.0
          ..strokeCap = StrokeCap.square);
  }

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

4.绘制弧形

属性

  void drawArc(Rect rect, //矩形范围,即圆弧所在的圆的范围,若非正方形则圆弧所在的圆会拉伸
               double startAngle, //起始角度,0.0 为坐标系 x 轴正向方形
               double sweepAngle, //第三个参数为终止角度,若超过 2PI,则为一个圆
               bool useCenter,//第四个参数为是否由中心出发,false* 时只绘制圆弧,true 时绘制圆饼,也就是中心点将其连接起来
               Paint paint //画笔样式,PaintingStyle.fill,首尾连接起来
              ) {

1.画弧线 PaintingStyle.stroke

 canvas.drawArc(
        Rect.fromCircle(center: Offset(50.0, 50.0), radius: 50.0),
        0.0,
        pi / 2,
        false,
        Paint()
          ..color = Colors.white
          ..strokeCap = StrokeCap.round
          ..strokeWidth = 6.0
          ..style = PaintingStyle.stroke);

2.首位连接起来 PaintingStyle.fill

    canvas.drawArc(
        Rect.fromCircle(center: Offset(150.0, 50.0), radius: 50.0),
        0.0,
        pi / 2,
        false,
        Paint()
          ..color = Colors.white
          ..strokeWidth = 6.0
          ..style = PaintingStyle.fill);

3.画饼

  canvas.drawArc(
        Rect.fromCircle(center: Offset(300.0, 100.0), radius: 50.0),
        -pi,
        pi / 2,
        true,
        Paint()
          ..color = Colors.white
          ..strokeWidth = 6.0
          ..style = PaintingStyle.stroke);

4.其他

 canvas.drawArc(
        Rect.fromLTWH(30.0, 150.0, 80.0, 50.0),
        0.0,
        pi * 2 * 2 / 3,
        true,
        Paint()
          ..color = Colors.white
          ..style = PaintingStyle.fill);
    canvas.drawArc(
        Rect.fromLTWH(150.0, 150.0, 80.0, 50.0),
        0.0,
        pi * 2 * 2 / 3,
        true,
        Paint()
          ..color = Colors.white
          ..strokeWidth = 6.0
          ..style = PaintingStyle.stroke);
    canvas.drawArc(
        Rect.fromPoints(Offset(250.0, 150.0), Offset(300.0, 200.0)),
        0.0,
        5.0,
        true,
        Paint()
          ..color = Colors.white
          ..style = PaintingStyle.fill);
    canvas.drawArc(
        Rect.fromPoints(Offset(350.0, 150.0), Offset(400.0, 200.0)),
        0.0,
        5.0,
        true,
        Paint()
          ..color = Colors.white
          ..strokeWidth = 6.0
          ..style = PaintingStyle.stroke);
5.drawRect 绘制矩形

drawRect 用来绘制矩形,Flutter 提供了多种绘制矩形方法:

Rect.fromPoints 根据两个点(左上角点/右下角点)来绘制;
Rect.fromLTRB 根据以屏幕左上角为坐标系圆点,分别设置上下左右四个方向距离;
Rect.fromLTWH 根据设置左上角的点与矩形宽高来绘制;
Rect.fromCircle 最特殊,根据圆形绘制正方形

    canvas.drawRect(
        Rect.fromPoints(Offset(30.0, 30.0), Offset(120.0, 60.0)),
        Paint()
          ..color = Colors.white
          ..strokeWidth = 6.0
          ..style = PaintingStyle.stroke);
    canvas.drawRect(
        Rect.fromLTWH(30.0, 140.0, 100.0, 70.0),
        Paint()
          ..color = Colors.white
          ..strokeWidth = 6.0
          ..style = PaintingStyle.stroke);

    canvas.drawRect(
        Rect.fromCircle(center: Offset(200.0, 160.0), radius: 40.0),
        Paint()
          ..color = Colors.white
          ..strokeWidth = 6.0
          ..style = PaintingStyle.fill);
6.drawRRect 绘制圆角矩形
  • RRect.fromLTRBXY 前四个参数用来绘制矩形位置,剩余两个参数绘制固定 x/y 弧度; x, y弧度
  • RRect.fromLTRBR 前四个参数用来绘制矩形位置,最后一个参数绘制 Radius 弧度; 统一弧度
  • RRect.fromLTRBAndCorners 前四个参数用来绘制矩形位置,剩余四个可选择参数,根据需求设- - 置四个角 Radius 弧度,可不同; //四个弧度
  • RRect.fromRectXY 第一个参数绘制矩形,可以用上面介绍的多种矩形绘制方式,剩余两个参数绘制固定 x/y 弧度;//
  • RRect.fromRectAndRadius 第一个参数绘制矩形,可以用上面介绍的多种矩形绘制方式,最后一个参数绘制 Radius 弧度;
  • RRect.fromRectAndCorners第一个参数绘制矩形,可以用上面介绍的多种矩形绘制方式,剩余四个可选择参数,根据需求设置四个角 Radius 弧度,最为灵活。

1.两个点fromLTRBXY 和fromLTRBR

    canvas.drawRRect(
        RRect.fromLTRBXY(120.0, 30.0, 220.0, 80.0, 8.0, 18.0),
        Paint()
          ..color = Colors.white
          ..style = PaintingStyle.fill);
    canvas.drawRRect(
        RRect.fromLTRBR(240.0, 30.0, 340.0, 80.0, Radius.circular(8.0)),
        Paint()
          ..color = Colors.white
          ..strokeWidth = 4.0
          ..style = PaintingStyle.stroke);

2.两个点设置四个不同圆角 fromLTRBAndCorners

    canvas.drawRRect(
        RRect.fromLTRBAndCorners(30.0, 120.0, 110.0, 160.0,
            topLeft: Radius.circular(20.0),
            topRight: Radius.circular(20.0),
            bottomRight: Radius.circular(5.0),
            bottomLeft: Radius.circular(5.0)),
        Paint()
          ..color = Colors.white
          ..strokeWidth = 4.0
          ..style = PaintingStyle.stroke);

3.Rect形式设置

   canvas.drawRRect(
        RRect.fromRectXY(
            Rect.fromCircle(center: Offset(70.0, 240.0), radius: 40.0),
            8.0,
            8.0),
        Paint()
          ..color = Colors.white
          ..strokeWidth = 4.0
          ..style = PaintingStyle.stroke);
7 drawDRRect 绘制嵌套矩形

drawDRRect 绘制嵌套矩形,第一个参数为外部矩形,第二个参数为内部矩形

 canvas.drawDRRect(
        RRect.fromRectXY(
            Rect.fromCircle(center: Offset(90.0, 120.0), radius: 60.0),
            8.0,
            8.0),
        RRect.fromRectXY(
            Rect.fromCircle(center: Offset(90.0, 120.0), radius: 50.0),
            8.0,
            8.0),
        Paint()
          ..color = Colors.white
          ..strokeWidth = 4.0
          ..style = PaintingStyle.stroke);
8 绘制圆形
 canvas.drawCircle(
        Offset(90.0, 120.0),
        60.0,
        Paint()
          ..color = Colors.white
          ..strokeWidth = 4.0
          ..style = PaintingStyle.stroke);
    canvas.drawCircle(
        Offset(270.0, 120.0),
        60.0,
        Paint()
          ..color = Colors.white
          ..style = PaintingStyle.fill);
9drawOval 绘制椭圆
  canvas.drawOval(
        Rect.fromLTRB(30.0, 30.0, 150.0, 80.0),
        Paint()
          ..color = Colors.white
          ..strokeWidth = 3.0
          ..style = PaintingStyle.stroke);
    canvas.drawOval(
        Rect.fromLTRB(210.0, 30.0, 330.0, 80.0),
        Paint()
          ..color = Colors.white
          ..strokeWidth = 3.0
          ..style = PaintingStyle.fill);
10path绘制路径,与Android api一致
  • moveTo() 即从当前坐标点开始,不设置时默认为屏幕左上角位置;
  • lineTo() 即从起点绘制到设置的新的点位;
  • close() 即最后的点到起始点连接,但对于中间绘制矩形/弧等时最后不会相连;
  • reset() 即清空连线;
  • addRect() 添加矩形连线;
  • addOval() 添加弧线,即贝塞尔(二阶)曲线;
  • cubicTo() 添加弧线,即贝塞尔(三阶)曲线;
  • arcTo() 画弧线
  • relativeMoveTo() 相对于移动到当前点位,小菜认为与 moveTo 相比整个坐标系移动;
  • relativeLineTo() 相对连接到当前点位,并将坐标系移动到当前点位

1.第一种

  canvas.drawPath(
        Path()
          ..moveTo(170.0, 30.0)
          ..lineTo(220.0, 30.0)
          ..lineTo(170.0, 80.0)
          ..lineTo(220.0, 80.0)
          ..close(),
        Paint()
          ..color = Colors.white
          ..strokeWidth = 3.0
          ..style = PaintingStyle.stroke);

2.第二种,close遇到中间绘制矩形/弧等无法关闭

    canvas.drawPath(
        Path()
          ..moveTo(30.0, 120.0)
          ..lineTo(120.0, 120.0)
          ..lineTo(30.0, 160.0)
          ..lineTo(120.0, 160.0)
          ..addRect(Rect.fromLTWH(180.0, 130.0, 120.0, 70.0))
          ..addOval(Rect.fromLTWH(190.0, 140.0, 100.0, 50.0))
          ..moveTo(30.0, 230.0)
          ..lineTo(160.0, 230.0)
          ..close(),
        Paint()
          ..color = Colors.white
          ..strokeWidth = 3.0
          ..style = PaintingStyle.stroke);

3.画弧线arcTo

  canvas.drawPath(
        Path()
          ..arcTo(Rect.fromCircle(center: Offset(70, 270), radius: 50), 0.0, pi,
              false),
        Paint()
          ..color = Colors.white
          ..strokeWidth = 3.0
          ..style = PaintingStyle.stroke);
11绘制阴影drawShadow

drawShadow 用于绘制阴影,第一个参数时绘制一个图形 Path,第二个是设置阴影颜色,第三个为阴影范围,最后一个阴影范围是否填充满;

    canvas.drawShadow(
        Path()
          ..moveTo(30.0, 30.0)
          ..lineTo(120.0, 30.0)
          ..lineTo(120.0, 60.0)
          ..lineTo(30.0, 60.0)
          ..close(),
        Colors.red,
        4,
        false);
12drawImage 绘制图片

drawImage 用于绘制图片,绘制图片是重点,此时的 Image 并非日常所用的图片加载,而是用的 dart.ui 类中的 ui.Image 并转换成字节流 ImageStream 方式传递,包括本地图片或网络图片

import 'dart:async';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: new ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: DrawImagePage());
  }
}

class DrawImagePage extends StatefulWidget {
  DrawImagePage({Key key}) : super(key: key);

  @override
  _DrawImagePageState createState() => _DrawImagePageState();
}

class _DrawImagePageState extends State<DrawImagePage> {
  ui.Image _image1;
  ui.Image _image2;

  @override
  void initState() {
    super.initState();
    _prepareImg();
  }

  @override
  Widget build(BuildContext context) {
    var winH = MediaQuery.of(context).size.height;
    var winW = MediaQuery.of(context).size.width;

    return Scaffold(
      appBar: AppBar(),
      body: CustomPaint(
        size: Size(winW, winH),
        painter: LCPainter(_image1, _image2),
      ),
    );
  }

  Future<ui.Image> load(String asset) async {
    ByteData data = await rootBundle.load(asset);
    ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
    ui.FrameInfo fi = await codec.getNextFrame();
    return fi.image;
  }

  // 获取图片 本地为false 网络为true
  Future<ui.Image> _loadImage(var path, bool isUrl) async {
    ImageStream stream;
    if (isUrl) {
      stream = NetworkImage(path).resolve(ImageConfiguration.empty);
    } else {
      stream = AssetImage(path, bundle: rootBundle)
          .resolve(ImageConfiguration.empty);
    }
    Completer<ui.Image> completer = Completer<ui.Image>();
    var listener = ImageStreamListener((ImageInfo info, bool syncCall) {
      final ui.Image image = info.image;
      completer.complete(image);
      // stream.removeListener(listener);
    });

    stream.addListener(listener);
    return completer.future;
  }

  var _value = 0;

// 加载图片
  _prepareImg() {
    _loadImage('images/a.jpeg', false).then((image1) {
      setState(() {
        _image1 = image1;
        print("xxxxxxx1");
      });
    }).whenComplete(() {
      _loadImage('https://www.itying.com/images/flutter/3.png', true)
          .then((image2) {
        setState(() {
          _image2 = image2;
        });
      }).whenComplete(() {
        if (this.mounted) {
          setState(() {});
        }
      });
    });
  }
}

class LCPainter extends CustomPainter {
  final ui.Image image1;
  final ui.Image image2;

  LCPainter(this.image1, this.image2);

  @override
  void paint(Canvas canvas, Size size) {
    print("xxxxxxx3");
    canvas.drawColor(Colors.orange, BlendMode.srcIn);
    if (image1 != null) {
      canvas.drawImage(image1, Offset(20.0, 240.0), Paint());
    }
    if (image2 != null) {
      canvas.drawImage(image2, Offset(60.0, 60.0), Paint());
    }
  }

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

参考文章:
原文链接:https://blog.csdn.net/tianzhilan0/article/details/107692508

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值