0

I am trying to add multiple Rectangles in the Canvas and rotate them with user pan action. But the Constructor I found till now for Rect is all to draw them without Rotation. and I found a method canvas.rotate() which will rotate the whole canvas.

How to achieve this? Any code where rotation of the Rectangle is dealt with user pan action without using canvas.rotate() will be helpful.

Zihan
  • 668
  • 8
  • 19
  • 1
    you either need `canvas.rotate()` or `canvas.transform()` - without them you will not draw rotated rectangle - you can use `canvas.rotate()` like here: https://stackoverflow.com/questions/64758628/rotate-painted-text-about-its-center/64772650#64772650 – pskink Aug 12 '21 at 17:30
  • So, if I rotate the whole canvas, how can I rotate one Rectangle 20 degree and another 40 degree – Zihan Aug 12 '21 at 17:35
  • see the link i posted – pskink Aug 12 '21 at 17:35
  • Sorry for being naive, I get that this code is Transforming and rotating Then Transforming kind of like suggested here https://stackoverflow.com/questions/58042432/flutter-custompainter-canvas-rotate-pivot/58042557#58042557 but while I Rotate the second Rect won't the 1st one will also be rotated as we are rotating the whole canvas. – Zihan Aug 12 '21 at 17:42
  • 1
    post your code then: with canvas save>translate>rotate>translate>drawRect>restore – pskink Aug 12 '21 at 17:49
  • I understood the answer now. every rotation enclosed by `canvas.save()` and `canvas.restore()` will not effect other element. Thanks, @pskink . The code is working now. – Zihan Aug 13 '21 at 15:30
  • but if you want to make it shorter you can also just use `Canvas.transform()`, so you call: transform > drawRect transform > drawRect etc, the problem is that `Matrix4` api is not easy... here you have some helper method: `Matrix4 rotate(double angle, Offset focalPoint) { var c = cos(angle); var s = sin(angle); var dx = (1 - c) * focalPoint.dx + s * focalPoint.dy; var dy = (1 - c) * focalPoint.dy - s * focalPoint.dx; return Matrix4(c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, dx, dy, 0, 1); }` – pskink Aug 13 '21 at 15:38
  • Can you please add some example code. and Some link on how to understand `Canvas.transform()` also if you could add your answer in the Answer below, I could select it as Right answer. – Zihan Aug 13 '21 at 15:42
  • i was wrong: `save()` and `restore()` are still needed: `final r0 = Rect.fromLTWH(100, 100, 300, 100); final r1 = Rect.fromLTWH(100, 300, 300, 100); canvas.save(); canvas.transform(rotate(pi/6, r0.center).storage); canvas.drawRect(r0, paint); canvas.restore(); canvas.save(); canvas.transform(rotate(-pi/6, r1.center).storage); canvas.drawRect(r1, paint); canvas.restore();` – pskink Aug 13 '21 at 15:51

1 Answers1

2

The solution is simple as @pskink answered in the comment above.

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 for example:

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 it around its own center.

Zihan
  • 668
  • 8
  • 19