Custom Painting in Flitter

Flitter provides powerful tools for custom painting, allowing you to create complex and efficient visual elements. This guide will introduce you to the CustomPaint widget and the Path utility, which simplify the process of creating custom graphics for both SVG and Canvas rendering.

CustomPaint Widget

If you only need to customize the painting behavior without altering the layout, the CustomPaint widget is the perfect solution. It allows you to define custom painting logic for both SVG and Canvas renderers.

Using CustomPaint

Here’s a basic example of how to use CustomPaint:

import { CustomPaint, Size, Path } from "@meursyphus/flitter";

const MyCustomPaintWidget = CustomPaint({
  size: Size.infinite,
  painter: {
    svg: {
      createDefaultSvgEl(context) {
        return {
          path: context.createSvgEl("path"),
        };
      },
      paint({ path: pathEl }, size) {
        // SVG painting logic
        const path = new Path();
        // ... path drawing logic ...
        pathEl.setAttribute("d", path.getD());
      },
    },
    canvas: {
      paint({ canvas }, size) {
        // Canvas painting logic
        const path = new Path();
        // ... path drawing logic ...
        canvas.stroke(path.toCanvasPath());
      },
    },
  },
});

Path Utility

The Path utility in Flitter is a powerful tool that allows you to create complex shapes and paths using the same code for both SVG and Canvas rendering. This significantly simplifies the process of creating custom graphics that work across different rendering contexts.

Key Features of Path

  1. Unified API: Use the same methods to define your path, regardless of whether it’s rendered as SVG or Canvas.
  2. Easy Conversion: Convert the path to SVG or Canvas format with simple method calls (getD() for SVG, toCanvasPath() for Canvas).
  3. Support for Complex Shapes: Create lines, curves, arcs, and more with a rich set of methods.

Example: Drawing a Line Chart

Here’s an example of how to use the Path utility to draw a line chart:

import { CustomPaint, Path, Size } from "@meursyphus/flitter";

function LineChart({ values, color, thickness = 2 }) {
  return CustomPaint({
    size: Size.infinite,
    painter: {
      svg: {
        createDefaultSvgEl(context) {
          return { path: context.createSvgEl("path") };
        },
        paint({ path: pathEl }, { width, height }) {
          const path = new Path();
          drawLineChart(path, values, width, height);
          pathEl.setAttribute("stroke", color);
          pathEl.setAttribute("fill", "none");
          pathEl.setAttribute("stroke-width", `${thickness}`);
          pathEl.setAttribute("d", path.getD()); // Convert to SVG path
        },
      },
      canvas: {
        paint({ canvas }, { width, height }) {
          const path = new Path();
          drawLineChart(path, values, width, height);
          canvas.strokeStyle = color;
          canvas.lineWidth = thickness;
          canvas.stroke(path.toCanvasPath()); // Convert to Canvas path
        },
      },
    },
  });
}

function drawLineChart(
  path: Path,
  values: number[],
  width: number,
  height: number,
) {
  // Drawing logic remains the same for both SVG and Canvas
  values.forEach((value, index) => {
    const x = (index / (values.length - 1)) * width;
    const y = height - (value / Math.max(...values)) * height;

    if (index === 0) {
      path.moveTo({ x, y });
    } else {
      path.lineTo({ x, y });
    }
  });
}

In this example, the drawLineChart function uses the Path utility to create the line chart. The same path is then used for both SVG and Canvas rendering, with only the final conversion step being different (getD() for SVG, toCanvasPath() for Canvas).

Conclusion

The CustomPaint widget and Path utility in Flitter provide a powerful and flexible way to create custom graphics. By using these tools, you can:

  1. Separate painting logic from layout logic
  2. Create complex shapes and paths easily
  3. Write code that works seamlessly for both SVG and Canvas rendering

This approach not only simplifies the development process but also ensures consistency across different rendering contexts, making Flitter an excellent choice for creating sophisticated data visualizations and custom UI elements.