Flutter CustomPaint 使用介绍

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter CustomPaint 使用介绍相关的知识,希望对你有一定的参考价值。

参考技术A CustomPaint class提供了让用户自定义widget的能力,它暴露了一个canvas,可以通过这个canvas来绘制widget,CustomPaint会先调用painter绘制背景,然后再绘制child,最后调用foregroundPainter来绘制前景,CustomPaint的定义如下

CustomPaint的绘制过程都将会交给CustomPainter来完成,CustomPainter是个抽象接口,在子类化CustomPainter的时候必须要重写它的 paint 跟 shouldRepaint 接口,可以根据自己的场景来选择性的重写 hitTest 跟 shouldRebuildSemantics 方法。

canvas--画布,真正的绘制是由canvas跟paint来完成的,画布提供了各种绘制的接口来绘制图形,除此以外画布还提供了平移、缩放、旋转等矩阵变换接口,画布都有固定大小跟形状,还可以使用画布提供的裁剪接口来裁剪画布的大小形状等等。
常用的绘制接口有 更多请查看官方文档

Paint---笔画,是用来设置在画布上面绘制图形时的一些笔画属性,如:颜色、线宽、绘制模式、抗锯齿等等。常用属性有 更多请查看官方文档
color : 设置画笔颜色
isAntiAlias : 设置画笔是否扛锯齿
shader : 着色器,填充形状或者画线时用到,如果没设置将会使用color
strokeWidth : 设置画笔画线宽度
style :绘制模式,画线或充满

下面这个例子来自于官方,通过 CustomPaint 画出了一个蓝天跟太阳出来

效果如下:

如何在 Flutter 中绘制自定义形状卡片

【中文标题】如何在 Flutter 中绘制自定义形状卡片【英文标题】:How to draw a custom shape card in flutter 【发布时间】:2020-12-24 08:07:37 【问题描述】:

我只想制作这样的卡片

【问题讨论】:

使用 CustomPaint : api.flutter.dev/flutter/widgets/CustomPaint-class.html 你需要一个自定义的 ShapeBorder 类 - 像 BeveledRectangleBorder / CircleBorder / ContinuousRectangleBorder / RoundedRectangle / BorderStadiumBorder 这样的类间接扩展该类(你也可以扩展 OutlinedBorder如果你愿意) @pskink 你能寄一份样品吗? paste.ubuntu.com/p/C5QCzsF7gg 【参考方案1】:

代码如下,我使用CustomPaint Widget 来绘制自定义形状,然后在Card Widget 内部使用stack 来正确放置小部件。

我没有把图片改成粉红色来显示图片:


这是 Card Widget 的代码,然后是 CustomPainter 类:

   Card(
        shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(50.0)),
        elevation: 10.0,
        child: Container(
          width: 300.0,
          height: 400.0,
          child: Stack(
            alignment: Alignment.bottomCenter,
            children: [
              // This will hold the Image in the back ground:
              Container(
                decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(50.0),
                    color: Colors.pink[100]),
              ),
              // This is the Custom Shape Container
              Positioned(
                bottom: 0.0,
                left: 0.0,
                child: Container(
                  color: Colors.red,
                  child: CustomPaint(
                    painter: CustomContainerShapeBorder(
                      height: 100.0,
                      width: 300.0,
                      radius: 50.0,
                    ),
                  ),
                ),
              ),
              // This Holds the Widgets Inside the the custom Container;
              Positioned(
                bottom: 10.0,
                child: Container(
                  height: 80.0,
                  width: 260.0,
                  color: Colors.grey.withOpacity(0.6),
                  child: null,
                ),
              ),
            ],
          ),
        ),
      ),

自定义画家类:

/// The CustomContainerShapeBorder should be reactibe with different sizes,
/// If it isn't then chamge the offset values.
class CustomContainerShapeBorder extends CustomPainter 
  final double height;
  final double width;
  final Color fillColor;
  final double radius;

  CustomContainerShapeBorder(
    this.height: 400.0,
    this.width: 300.0,
    this.fillColor: Colors.white,
    this.radius: 50.0,
  );
  @override
  void paint(Canvas canvas, Size size) 
    Path path = new Path();
    path.moveTo(0.0, -radius);
    path.lineTo(0.0, -(height - radius));
    path.conicTo(0.0, -height, radius, -height, 1);
    path.lineTo(width - radius, -height);
    path.conicTo(width, -height, width, -(height + radius), 1);
    path.lineTo(width, -(height - radius));
    path.lineTo(width, -radius);

    path.conicTo(width, 0.0, width - radius, 0.0, 1);
    path.lineTo(radius, 0.0);
    path.conicTo(0.0, 0.0, 0.0, -radius, 1);
    path.close();
    canvas.drawPath(path, Paint()..color = fillColor);
  

  @override
  bool shouldRepaint(CustomPainter oldDelegate) 
    return true;
  

输出: 灰色的容器是用来描绘Custom Shape里面的内容

整个代码:

import 'package:flutter/material.dart';
    
    void main() 
      runApp(MyApp());
    
    
    class MyApp extends StatelessWidget 
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) 
        return MaterialApp(
          title: 'Custom Card Design',
          theme: ThemeData(
            primarySwatch: Colors.amber,
          ),
          home: MyHomePage(),
        );
      
    
    
    class MyHomePage extends StatefulWidget 
      @override
      _MyHomePageState createState() => _MyHomePageState();
    
    
    class _MyHomePageState extends State<MyHomePage> 
      @override
      Widget build(BuildContext context) 
        return Container(
            color: Colors.amber,
            child: Center(
              child: Card(
                shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(50.0)),
                elevation: 10.0,
                child: Container(
                  width: 300.0,
                  height: 400.0,
                  child: Stack(
                    alignment: Alignment.bottomCenter,
                    children: [
                      // This will hold the Image in the back ground:
                      Container(
                        decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(50.0),
                            color: Colors.pink[100]),
                      ),
                      // This is the Custom Shape Container
                      Positioned(
                        bottom: 0.0,
                        left: 0.0,
                        child: Container(
                          color: Colors.red,
                          child: CustomPaint(
                            painter: CustomContainerShapeBorder(
                              height: 100.0,
                              width: 300.0,
                              radius: 50.0,
                            ),
                          ),
                        ),
                      ),
                      // This Holds the Widgets Inside the the custom Container;
                      Positioned(
                        bottom: 10.0,
                        child: Container(
                          height: 80.0,
                          width: 260.0,
                          color: Colors.grey.withOpacity(0.6),
                          child: null,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ));
      
    
    
    /// The CustomContainerShapeBorder should be reactibe with different sizes,
    /// If it isn't then chamge the offset values.
    class CustomContainerShapeBorder extends CustomPainter 
      final double height;
      final double width;
      final Color fillColor;
      final double radius;
    
      CustomContainerShapeBorder(
        this.height: 400.0,
        this.width: 300.0,
        this.fillColor: Colors.white,
        this.radius: 50.0,
      );
      @override
      void paint(Canvas canvas, Size size) 
        Path path = new Path();
        path.moveTo(0.0, -radius);
        path.lineTo(0.0, -(height - radius));
        path.conicTo(0.0, -height, radius, -height, 1);
        path.lineTo(width - radius, -height);
        path.conicTo(width, -height, width, -(height + radius), 1);
        path.lineTo(width, -(height - radius));
        path.lineTo(width, -radius);
    
        path.conicTo(width, 0.0, width - radius, 0.0, 1);
        path.lineTo(radius, 0.0);
        path.conicTo(0.0, 0.0, 0.0, -radius, 1);
        path.close();
        canvas.drawPath(path, Paint()..color = fillColor);
      
    
      @override
      bool shouldRepaint(CustomPainter oldDelegate) 
        return true;
      
    

【讨论】:

谢谢,但我使用自定义 ShapeBorder 。这是更好的选择 在形状边框类内使用相同的Path设计【参考方案2】:

The output screen我希望这会有所帮助。 代码:

import 'package:flutter/material.dart';

main() 
  runApp(MyApp());


class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'Drawing Paths',
      home: Container(
        color: Colors.white,
        child: CustomPaint(
          painter: CurvePainter(),
        ),
      ),
    );
  


class CurvePainter extends CustomPainter 
  @override
  void paint(Canvas canvas, Size size) 
    var paint = Paint();
    paint.color = Colors.blueAccent;
    paint.style = PaintingStyle.fill;

    var path = Path();

    path.moveTo(size.width, size.height * 0.7);
    path.quadraticBezierTo(size.width * 0.99, size.height * 0.79,
        size.width * 0.8, size.height * 0.8);
    path.lineTo(size.width * 0.08, size.height * 0.8);
    path.quadraticBezierTo(size.width * 0.001, size.height * 0.81,
        0, size.height * 0.86);
    path.lineTo(0, size.height * 0.95);
    path.quadraticBezierTo(size.width * 0.001 , size.height * 0.98,
        size.width * 0.08, size.height * 0.99);
    path.lineTo(size.width * 0.8, size.height * 0.99);
    path.quadraticBezierTo(size.width * 0.99, size.height * 0.99,
        size.width, size.height * 0.89);
    canvas.drawPath(path, paint);
  

  @override
  bool shouldRepaint(CustomPainter oldDelegate) 
    return true;
  

【讨论】:

【参考方案3】:

对于任何类型的自定义设计,您都可以使用此工具创建https://shapemaker.web.app/#/

【讨论】:

以上是关于Flutter CustomPaint 使用介绍的主要内容,如果未能解决你的问题,请参考以下文章

用Flutter做桌上弹球?聊聊绘图(Canvas&CustomPaint)API

用Flutter做桌上弹球?聊聊绘图(Canvas&CustomPaint)API

用Flutter做桌上弹球?聊聊绘图(Canvas&CustomPaint)API

Flutter自定义绘制组件

是否可以在 Flutter 中使用手写笔输入?

Flutter:如何画星星