Draw an image with custom Size and Rotation into a CustomPaint in Flutter

4,117

Solution 1

It looks like you can rotate the canvas itself in your paint function. My code is below.

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class ParticlePainter extends CustomPainter {
  // final List<GameObject> objects;

  @override
  void paint(Canvas canvas, Size size) {
    canvas.save();

    final degrees = 120;
    final radians = degrees * math.pi / 180;

    canvas.rotate(radians);

    canvas.drawRect(
        Rect.fromCenter(
          height: 20,
          width: 20,
          center: Offset(
            0.0,
            0.0,
          ),
        ),
        Paint()
          ..color = Colors.red
          ..blendMode = BlendMode.darken);

    canvas.restore();
  }

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

  // ParticlePainter(this.objects);
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Yay"),
      ),
      body: CustomPaint(
        child: Container(),
        painter: ParticlePainter(),
      ),
    );
  }
}

For me, this causes the box to go off screen a bit, but I hope it is enough for you to move forward. If you're doing something easy, you might be able to use either Transform.rotate or RotatedBox and wrap your CustomPaint in it.

Solution 2

It's a long time since the question was asked but I want to fulfill the answer for future readers, as the above answer does not answer it for multiple objects.

There is only canvas.rotate() and canvas.transform() to rotate anything in the flutter canvas and there is canvas.scale() to scale them.

now if you want to rotate one object 120, and another 40 degrees you need to draw them inside a canvas.save() ... canvas.restore() block. then your objects will be rotated at a different angles. look at the below code

import 'dart:async';
import 'dart:typed_data';

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

const kCanvasSize = 300.0;

class ImageInsideRectPage extends StatefulWidget {
  const ImageInsideRectPage({Key? key}) : super(key: key);

  @override
  _ImageInsideRectPageState createState() => _ImageInsideRectPageState();
}

class _ImageInsideRectPageState extends State<ImageInsideRectPage> {
  ui.Image? image;

  @override
  void initState() {
    _load('assets/img.png');
    super.initState();
  }

  void _load(String path) async {
    var bytes = await rootBundle.load(path);
    image = await decodeImageFromList(bytes.buffer.asUint8List());
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          decoration: BoxDecoration(
              border: Border.all(color: Colors.greenAccent, width: 2)),
          height: kCanvasSize,
          width: kCanvasSize,
          child: CustomPaint(
            painter: ImageInsideRectangle(context: context, image: image),
            child: SizedBox.expand(),
          ),
        ),
      ),
    );
  }
}

class ImageInsideRectangle extends CustomPainter {
  ImageInsideRectangle({required this.context, required this.image});
  ui.Image? image;
  final BuildContext context;

  @override
  void paint(Canvas canvas, Size size) async {
    canvas.clipRRect(ui.RRect.fromRectXY(
      Rect.fromPoints(Offset(0, 0), Offset(kCanvasSize - 4, kCanvasSize - 4)),
      0,
      0,
    ));
    Paint greenBrush = Paint()..color = Colors.greenAccent;
    if (image != null) {
      canvas.save();
      rotate(
          canvas: canvas,
          cx: image!.width.toDouble() / 2,
          cy: image!.height.toDouble() / 2,
          angle: -0.3);
      canvas.scale(kCanvasSize / image!.height);
      canvas.drawImage(image!, Offset(0, 0), greenBrush);
      canvas.restore();
    }
    canvas.save();
    rotate(canvas: canvas, cx: 200 + 50, cy: 100 + 50, angle: 0.5);
    canvas.drawRect(Rect.fromLTWH(200, 100, 100, 100), greenBrush);
    canvas.restore();
  }

  void rotate(
      {required Canvas canvas,
      required double cx,
      required double cy,
      required double angle}) {
    canvas.translate(cx, cy);
    canvas.rotate(angle);
    canvas.translate(-cx, -cy);
  }

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

Future<ui.Image> loadUiImage(String imageAssetPath) async {
  final ByteData data = await rootBundle.load(imageAssetPath);
  final Completer<ui.Image> completer = Completer();
  ui.decodeImageFromList(Uint8List.view(data.buffer), (ui.Image img) {
    return completer.complete(img);
  });
  return completer.future;
}

This way you can rotate multiple objects in multiple directions. also, there is an example of loading an image from local asset and rotating the image around its own center.

Share:
4,117
fabriziog
Author by

fabriziog

Updated on December 14, 2022

Comments

  • fabriziog
    fabriziog over 1 year

    How can I draw an image to a customPaint() specifying its size and rotation in Flutter? Normally with only drawImage() the image gets drawn with its dimensions being its dimensions in pixels. For example, a 40x40 pixels image gets drawn with dimensions of 40 by 40. How can I draw the same image but for example with a size of 80x80 and a rotation of 120°? I've tried drawAtlas but I couldn't find anything online.

    This is the code I have to draw. (It draws a 20x20 rectangle at the top-right of the screen).

    class ParticlePainter extends CustomPainter {
      final List<GameObject> objects;
    
      @override
      void paint(Canvas canvas, Size size) {
    
    
        canvas.drawRect(
    
            Rect.fromCenter(
                height: 20,
                width: 20,
                center: Offset(0.0, 0.0)),
    
            Paint()
              ..color = Colors.red
              ..blendMode = BlendMode.darken);
        }
    
      }
    
      @override
      bool shouldRepaint(CustomPainter oldDelegate) {
        return true;
      }
    
      ParticlePainter(this.objects);
    }
    

    To get the image I'm using the code in this video: Custom Paint in Flutter

    Future<Image> _loadImage(File file) async{
       final data = await file.readAsBytesSync();
       return await decodeImageFromList(data);
    }