Drawing Basic Shapes, Text, and Images on the Canvas in Flutter

Flutter’s CustomPaint widget, paired with the Canvas API, offers a powerful way to create custom drawings directly within your Flutter applications. By leveraging the canvas, you can draw basic shapes, render text, and display images with a high degree of control. This capability opens the door to creating intricate visualizations, custom UI components, and unique graphical elements that extend beyond the standard Flutter widgets.

What is the Canvas in Flutter?

In Flutter, the Canvas is a drawing surface that you can use to create custom 2D graphics. It’s an object that provides an interface for drawing shapes, text, images, and more. The CustomPaint widget is used to integrate custom drawing into your Flutter UI. By implementing a CustomPainter, you can define exactly how your graphics should be rendered on the canvas.

Why Use Canvas for Drawing in Flutter?

  • Custom UI: Create unique UI elements not available in the standard widget library.
  • Graphics and Visualizations: Render complex data visualizations, graphs, and charts.
  • Game Development: Implement game-specific graphics and animations.
  • Performance: Fine-grained control over rendering, allowing for optimized graphics performance.

How to Draw Basic Shapes on the Canvas in Flutter

Let’s start with the basics of drawing shapes on the canvas. The Canvas API provides methods for drawing lines, circles, rectangles, and other geometric forms.

Step 1: Create a CustomPainter Class

First, create a class that extends CustomPainter. Override the paint method where you’ll perform your drawing instructions.


import 'package:flutter/material.dart';

class ShapePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // Drawing instructions will go here
  }

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

Step 2: Drawing a Line

To draw a line, use the drawLine method. It requires a start point, an end point, and a Paint object that defines the line’s style.


void paint(Canvas canvas, Size size) {
  final paint = Paint()
    ..color = Colors.blue
    ..strokeWidth = 5;

  final start = Offset(0, size.height / 2);
  final end = Offset(size.width, size.height / 2);

  canvas.drawLine(start, end, paint);
}

Step 3: Drawing a Circle

To draw a circle, use the drawCircle method. It requires a center point, a radius, and a Paint object.


void paint(Canvas canvas, Size size) {
  final paint = Paint()
    ..color = Colors.red
    ..style = PaintingStyle.fill;

  final center = Offset(size.width / 2, size.height / 2);
  final radius = size.width / 4;

  canvas.drawCircle(center, radius, paint);
}

Step 4: Drawing a Rectangle

To draw a rectangle, use the drawRect method. It requires a Rect object, which defines the rectangle’s dimensions and position, and a Paint object.


void paint(Canvas canvas, Size size) {
  final paint = Paint()
    ..color = Colors.green
    ..style = PaintingStyle.stroke
    ..strokeWidth = 3;

  final rect = Rect.fromLTWH(size.width / 4, size.height / 4, size.width / 2, size.height / 2);

  canvas.drawRect(rect, paint);
}

Step 5: Using CustomPaint Widget in the UI

Now, use the CustomPaint widget in your Flutter UI to display the drawing.


import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Canvas Drawing')),
        body: Center(
          child: SizedBox(
            width: 300,
            height: 300,
            child: CustomPaint(
              painter: ShapePainter(),
            ),
          ),
        ),
      ),
    ),
  );
}

How to Draw Text on the Canvas in Flutter

Drawing text on the canvas involves using the TextPainter class and the drawParagraph method.

Step 1: Create a TextPainter

First, create a TextPainter and set its text, style, and alignment.


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

class TextPainterExample extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final textStyle = TextStyle(
      color: Colors.black,
      fontSize: 20,
    );

    final textSpan = TextSpan(
      text: 'Hello, Flutter Canvas!',
      style: textStyle,
    );

    final textPainter = TextPainter(
      text: textSpan,
      textDirection: TextDirection.ltr,
    );

    textPainter.layout(
      minWidth: 0,
      maxWidth: size.width,
    );

    final x = size.width / 2 - textPainter.width / 2;
    final y = size.height / 2 - textPainter.height / 2;

    textPainter.paint(canvas, Offset(x, y));
  }

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

Step 2: Render Text Using CustomPaint

Use the TextPainterExample painter with a CustomPaint widget.


import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Canvas Text')),
        body: Center(
          child: SizedBox(
            width: 300,
            height: 300,
            child: CustomPaint(
              painter: TextPainterExample(),
            ),
          ),
        ),
      ),
    ),
  );
}

How to Draw Images on the Canvas in Flutter

Displaying images on the canvas requires loading the image and then using the drawImage method.

Step 1: Load an Image

Load an image using ImageProvider.


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

class ImagePainter extends CustomPainter {
  ui.Image? image;

  ImagePainter(this.image);

  @override
  void paint(Canvas canvas, Size size) {
    if (image != null) {
      final rect = Rect.fromLTWH(0, 0, size.width, size.height);
      canvas.drawImage(image!, Offset.zero, Paint());
    }
  }

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

Step 2: Load Image in Widget

Display image by implementing ImagePainter in stateful widget with Image.


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

class ImageCanvasExample extends StatefulWidget {
  const ImageCanvasExample({super.key});

  @override
  State createState() => _ImageCanvasExampleState();
}

class _ImageCanvasExampleState extends State {
  ui.Image? image;

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

  Future _loadImage() async {
    final AssetImage assetImage = const AssetImage('assets/flutter_logo.png');
    final ImageStream stream = assetImage.resolve(ImageConfiguration.empty);
    final Completer completer = Completer();

    void listener(ImageInfo info, bool synchronousCall) {
      completer.complete(info.image);
    }

    final ImageStreamListener streamListener =
        ImageStreamListener(listener, onError: (Object error, StackTrace? stack) {
      completer.completeError(error, stack);
    });
    stream.addListener(streamListener);
    image = await completer.future;
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Canvas Image'),
      ),
      body: Center(
        child: SizedBox(
          width: 300,
          height: 300,
          child: image != null
              ? CustomPaint(
                  painter: ImagePainter(image),
                )
              : const CircularProgressIndicator(),
        ),
      ),
    );
  }
}

Conclusion

Drawing on the canvas in Flutter provides endless possibilities for creating custom UIs, data visualizations, and more. Whether it’s drawing basic shapes, rendering text, or displaying images, Flutter’s CustomPaint widget and Canvas API offer the flexibility and control needed to bring your creative visions to life. Experiment with different drawing methods, styles, and transformations to unlock the full potential of custom drawing in your Flutter applications. By mastering these techniques, you can create truly unique and engaging user experiences.