flutterwidgetclip

How to create a widget from a part of image in Flutter?


I have an image (blue rect). I want to cut a part of the image by path (red triangle) and create a smaller widget (green rect) that shows this part of the image and has size which equals bounds of cutting path. How can I do it in Flutter?

Example

I tried to use ClipPath and CustomClipper<Path>, but I was able to create only widget which has size of the image.


Solution

  • I found this solution:

    import 'dart:typed_data';
    import 'dart:ui' as ui;
    
    import 'package:flutter/material.dart';
    
    class MyWidget extends StatelessWidget {
      final ui.Image _uiImage;
      final Path _clipPath;
    
      const MyWidget({
        Key? key,
        required ui.Image uiImage,
        required Path clipPath,
      })  : _uiImage = uiImage,
            _clipPath = clipPath,
            super(key: key);
    
      @override
      Widget build(BuildContext context) {
        final bounds = _clipPath.getBounds();
        
        final translateM = Float64List.fromList([
          1, 0, 0, 0,
          0, 1, 0, 0,
          0, 0, 1, 0,
          -bounds.left, -bounds.top, 0, 1
        ]);
        
        final localClipPath = _clipPath.transform(translateM);
        
        return ClipPath(
          clipper: _MyClipper(localClipPath),
          child: CustomPaint(
            painter: _MyPainter(_uiImage, bounds),
            child: SizedBox(
              width: bounds.width,
              height: bounds.height,
            ),
          ),
        );
      }
    }
    
    class _MyClipper extends CustomClipper<Path> {
      final Path _clipPath;
    
      _MyClipper(this._clipPath);
    
      @override
      Path getClip(Size size) => _clipPath;
    
      @override
      bool shouldReclip(covariant CustomClipper<Path> oldClipper) => true;
    }
    
    class _MyPainter extends CustomPainter {
      final ui.Image _uiImage;
      final Rect _bounds;
    
      final _paint = Paint();
    
      _MyPainter(this._uiImage, this._bounds);
    
      @override
      void paint(Canvas canvas, Size size) {
        canvas.drawAtlas(
          _uiImage,
          [
            RSTransform.fromComponents(
              rotation: 0.0,
              scale: 1.0,
              // Center of the sprite relative to its rect
              anchorX: _bounds.width / 2,
              anchorY: _bounds.height / 2,
              // Location at which to draw the center of the sprite
              translateX: _bounds.width / 2,
              translateY: _bounds.height / 2,
            )
          ],
          [_bounds],
          null,
          null,
          null,
          _paint,
        );
      }
    
      @override
      bool shouldRepaint(_MyPainter oldDelegate) => false;
    }