image-processingflutterflutter-layoutflutter-testflutter-canvas

How to crop circular(or Square or Hexagon ) area from canvas in Flutter


I want to crop a rectangular image-file and make it circular or square or hexagon shape and save it as image-file locally. I have a bitmap and I want to crop a circular or hexagon region from this bitmap. All pixels outside the circle should be transparent. How can I do this?

enter image description here

I found the similar question in android platform. But unable to find a good resource that really help or direct me to achieve this in flutter. Please share me the idea to achieve this result.


Solution

  • These simple steps get the required result.

    1. Create a PictureRecorder.
    2. Create a Canvas with your PictureRecorder.
    3. Draw circle in canvas using canvas.drawCircle().
    4. Call endRecording() on the PictureRecorder to get a Picture.
    5. Call toImage() on the Picture.
    6. Convert image toByteData() .
    7. Save image locally using getApplicationDocumentsDirectory().

    Canvas Screen

    enter image description here

    Saved Image Canvas as png

    enter image description here

    Full Source Code:

            import 'dart:io';
    
            import 'package:flutter/material.dart';
            import 'dart:ui' as ui;
            import 'package:flutter/services.dart' show rootBundle;
            import 'dart:async';
            import 'dart:typed_data';
            import 'dart:math' as math;
            import 'package:path/path.dart';
            import 'package:path_provider/path_provider.dart';
    
            class CustomImageCanvas extends StatefulWidget {
              CustomImageCanvas({Key key, this.title}) : super(key: key);
    
              final String title;
    
              @override
              _CustomImageCanvasState createState() => _CustomImageCanvasState();
            }
    
            class _CustomImageCanvasState extends State<CustomImageCanvas> {
              ui.Image image;
              bool isImageloaded = false;
              void initState() {
                super.initState();
                init();
              }
    
              Future<Null> init() async {
                final ByteData data = await rootBundle.load('assets/image.jpeg');
                image = await loadImage(Uint8List.view(data.buffer));
              }
    
              Future<ui.Image> loadImage(List<int> img) async {
                final Completer<ui.Image> completer = Completer();
                ui.decodeImageFromList(img, (ui.Image img) {
                  setState(() {
                    isImageloaded = true;
                  });
                  return completer.complete(img);
                });
                return completer.future;
              }
    
              Widget _buildImage() {
                if (this.isImageloaded) {
                  return CustomPaint(
                    painter: PngImagePainter(image: image),
                  );
                } else {
                  return Center(child: Text('loading'));
                }
              }
    
              @override
              Widget build(BuildContext context) {
                return Scaffold(
                    appBar: AppBar(
                      title: Text(widget.title),
                    ),
                    body: Container(
                      child: _buildImage(),
                    ));
              }
            }
    
            class PngImagePainter extends CustomPainter {
              PngImagePainter({
                this.image,
              });
    
              ui.Image image;
    
              @override
              void paint(Canvas canvas, Size size) {
                _drawCanvas(size, canvas);
                _saveCanvas(size);
              }
    
              Canvas _drawCanvas(Size size, Canvas canvas) {
                final center = Offset(150, 50);
                final radius = math.min(size.width, size.height) / 8;
    
                // The circle should be paint before or it will be hidden by the path
                Paint paintCircle = Paint()..color = Colors.black;
                Paint paintBorder = Paint()
                  ..color = Colors.white
                  ..strokeWidth = size.width / 36
                  ..style = PaintingStyle.stroke;
                canvas.drawCircle(center, radius, paintCircle);
                canvas.drawCircle(center, radius, paintBorder);
    
                double drawImageWidth = 0;
                var drawImageHeight = -size.height * 0.8;
    
                Path path = Path()
                  ..addOval(Rect.fromLTWH(drawImageWidth, drawImageHeight,
                      image.width.toDouble(), image.height.toDouble()));
    
                canvas.clipPath(path);
    
                canvas.drawImage(image, Offset(drawImageWidth, drawImageHeight), Paint());
                return canvas;
              }
    
              _saveCanvas(Size size) async {
                var pictureRecorder = ui.PictureRecorder();
                var canvas = Canvas(pictureRecorder);
                var paint = Paint();
                paint.isAntiAlias = true;
    
                _drawCanvas(size, canvas);
    
                var pic = pictureRecorder.endRecording();
                ui.Image img = await pic.toImage(image.width, image.height);
                var byteData = await img.toByteData(format: ui.ImageByteFormat.png);
                var buffer = byteData.buffer.asUint8List();
    
                // var response = await get(imgUrl);
                var documentDirectory = await getApplicationDocumentsDirectory();
                File file = File(join(documentDirectory.path,
                    '${DateTime.now().toUtc().toIso8601String()}.png'));
                file.writeAsBytesSync(buffer);
    
                print(file.path);
              }
    
              @override
              bool shouldRepaint(CustomPainter oldDelegate) {
                return false;
              }
            }