3

I want to make a container which can be dragged around, zoom and rotate. I am able to achieve a zoom. Below is my code:

//variable declaration
  double _scale = 1.0;
  double _previousScale;
  var yOffset = 400.0;
  var xOffset = 50.0;
  var rotation = 0.0;
  var lastRotation = 0.0;

//build method

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Center(
          child: GestureDetector(
            onScaleStart: (scaleDetails) {
              _previousScale = _scale;
              print(' scaleStarts = ${scaleDetails.focalPoint}');
            },
            onScaleUpdate: (scaleUpdates){
              //ScaleUpdateDetails
              rotation += lastRotation - scaleUpdates.rotation;
              lastRotation = scaleUpdates.rotation;
              print("lastRotation = $lastRotation");
              print(' scaleUpdates = ${scaleUpdates.scale} rotation = ${scaleUpdates.rotation}');
              setState(() => _scale = _previousScale * scaleUpdates.scale);
            },
            onScaleEnd: (scaleEndDetails) {
              _previousScale = null;
              print(' scaleEnds = ${scaleEndDetails.velocity}');
            },
            child:
            Transform(
              transform: Matrix4.diagonal3( Vector3(_scale, _scale, _scale))..rotateZ(rotation * math.pi/180.0),
              alignment: FractionalOffset.center,
              child: Container(
                height: 200.0,
                width: 200.0,
                color: Colors.red,
              ),
            )
            ,
          ),
        ),
      ),
    );
  }

Currently, there is no rotation and I can't move the container around.

Ankur Prakash
  • 1,417
  • 4
  • 18
  • 31
  • 1
    use [MatrixGestureDetector](https://pub.dartlang.org/packages/matrix_gesture_detector) – pskink Mar 18 '19 at 14:15
  • there is some sample code in `example` folder, what do you have problems with? – pskink Mar 18 '19 at 16:13
  • Sorry for the late response, It does resolve the issue but I want a solution to combine Transform.Scale, Transform.rotate and Transform.translate in flutter. – Ankur Prakash Mar 23 '19 at 06:22
  • I have no idea what you mean by that – pskink Mar 23 '19 at 06:37
  • Actually, I was looking for a simple example. MatrixGestureDetector solves the problem and is the correct answer to my question but I wanted the solution where we have 3 values from onScaleUpdate and we can combine to make the Matrix like the example I posted in my question. I think I am now able to explain my point clearly. – Ankur Prakash Mar 23 '19 at 13:06
  • 3 values? you have the matrix, what do you want 3 values for? – pskink Mar 24 '19 at 12:53
  • MatrixGestureDetector doesn't solve my problem. I want to make a collage app where I need to drag, scale images on the board. – Ankur Prakash Mar 24 '19 at 13:18
  • you want to use `Transform` widget, right? so `MatrixGestureDetector` solves your problem, why dont you simply run any of 6 examples provided? – pskink Mar 24 '19 at 13:19
  • I tried all of them. The problem that I am facing is, I need to drag view in the complete space of the screen, currently, it is doing only to the specified constraints. Transform( transform: notifier.value, child: Container( width: 200.0, height: 200.0, color: Colors.yellow, ), ) – Ankur Prakash Mar 24 '19 at 13:26
  • Can you add one example where we can drag,scale different images in the whole screen. Both scale should be independent of each other? I will be very thankful to you. – Ankur Prakash Mar 24 '19 at 13:28
  • 1
    use two `MatrixGestureDetector`s then (or more) - each `Transform`ed `Widget` should have its own `MatrixGestureDetector` – pskink Mar 24 '19 at 13:29
  • You are correct, I am able to do that and need to take 2 notifier variables too, to manage different values, Should i take array of ValueNotifiers for more images? – Ankur Prakash Mar 24 '19 at 13:32
  • you can take array, list, map - whatever data structure you want – pskink Mar 24 '19 at 13:33
  • Can you post "MatrixGestureDetector " as an answer? I would like to mark it as the correct answer. Currently, we are discussing in comments. – Ankur Prakash Mar 24 '19 at 13:35

5 Answers5

7

use Matrix Gesture Detector package1 package, here you have the basic sample:

MatrixGestureDetector(
  onMatrixUpdate: (m, tm, sm, rm) {
    setState(() {
      matrix = n;
    });
  },
  child: Transform(
    transform: matrix,
    child: ....
  ),
),

for more sample code refer to example folder that contains 6 demos

Swapnil Kadam
  • 4,075
  • 5
  • 29
  • 35
pskink
  • 23,874
  • 6
  • 66
  • 77
  • How to do text scale, rotate, translate using matrix_gesture_detector – Ankur Prakash Apr 08 '19 at 09:38
  • @AnkurPrakash you have the code in my answer - just set your `Text` widget as a child of `Transform` – pskink Apr 08 '19 at 09:42
  • 1
    what trouble? see the simplest form [here](https://github.com/pskink/matrix_gesture_detector/blob/master/example/lib/transform_demo.dart) or [here](https://github.com/pskink/matrix_gesture_detector/blob/master/example/lib/custom_painter_demo.dart) – pskink Apr 19 '19 at 12:34
  • My trouble is that even Text container is taking the complete screen. I want to limit the text area where I have text. Currently, It takes a complete screen for resizing. Another problem is saving and loading back containers. I am saving the container matrix and retrieving back, but when I touch it goes to full screen (identity). – Ankur Prakash Apr 19 '19 at 12:49
  • Here is my code https://www.dropbox.com/s/2suvmiz5r1muqya/matrix_gesture_screen.dart?dl=0 – Ankur Prakash Apr 19 '19 at 12:50
  • 1
    I request you to add a sample in an example where user can resize image and text separately as well as save the current layout. That will be a big help. I am new to flutter development. – Ankur Prakash Apr 19 '19 at 12:52
  • can you help me please? – Ankur Prakash Apr 20 '19 at 10:50
  • The link not works, should use the following [matrix_gesture_detector](https://pub.dev/packages/matrix_gesture_detector) – Shan Sep 28 '20 at 11:02
2

Had the same problem, here is my solution:

https://stackoverflow.com/a/74165192/12098106

import 'package:flutter/material.dart';

// -------------------------------------------------------------------
// THE ITEM TO BE DRAWN
// -------------------------------------------------------------------

class DrawContainer {
  Color color;
  Offset offset;
  double width;
  double height;
  double scale;
  double angle;
  late double _baseScaleFactor;
  late double _baseAngleFactor;

  DrawContainer(this.color, this.offset, this.width, this.height, this.scale,
      this.angle) {
    onScaleStart();
  }

  onScaleStart() {
    _baseScaleFactor = scale;
    _baseAngleFactor = angle;
  }

  onScaleUpdate(double scaleNew) =>
      scale = (_baseScaleFactor * scaleNew).clamp(0.5, 5);

  onRotateUpdate(double angleNew) => angle = _baseAngleFactor + angleNew;
}

// -------------------------------------------------------------------
// APP
// -------------------------------------------------------------------

void main() {
  runApp(const MaterialApp(home: GestureTest()));
}

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

  @override
  // ignore: library_private_types_in_public_api
  _GestureTestState createState() => _GestureTestState();
}

// -------------------------------------------------------------------
// APP STATE
// -------------------------------------------------------------------

class _GestureTestState extends State<GestureTest> {
  final List<DrawContainer> containers = [
    DrawContainer(Colors.red, const Offset(50, 50), 100, 100, 1.0, 0.0),
    DrawContainer(Colors.yellow, const Offset(100, 100), 200, 100, 1.0, 0.0),
    DrawContainer(Colors.green, const Offset(150, 150), 50, 100, 1.0, 0.0),
  ];

  void onGestureStart(DrawContainer e) => e.onScaleStart();

  onGestureUpdate(DrawContainer e, ScaleUpdateDetails d) {
    e.offset = e.offset + d.focalPointDelta;
    if (d.rotation != 0.0) e.onRotateUpdate(d.rotation);
    if (d.scale != 1.0) e.onScaleUpdate(d.scale);
    setState(() {}); // redraw
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
        child: Scaffold(
      body: SizedBox(
        height: double.infinity,
        width: double.infinity,
        child: Stack(
          children: [
            ...containers.map((e) {
              return GestureDetector(
                  onScaleStart: (details) {
                    // detect two fingers to reset internal factors
                    if (details.pointerCount == 2) {
                      onGestureStart(e);
                    }
                  },
                  onScaleUpdate: (details) => onGestureUpdate(e, details),
                  child: DrawWidget(e));
            }).toList(),
          ],
        ),
      ),
    ));
  }
}

// -------------------------------------------------------------------
// POSITION, ROTATE AND SCALE THE WIDGET
// -------------------------------------------------------------------

class DrawWidget extends StatelessWidget {
  final DrawContainer e;
  const DrawWidget(this.e, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Positioned(
          left: e.offset.dx,
          top: e.offset.dy,
          child: Transform.rotate(
            angle: e.angle,
            child: Transform.scale(
              scale: e.scale,
              child: Container(
                height: e.width,
                width: e.height,
                color: e.color,
              ),
            ),
          ),
        ),
      ],
    );
  }
}
Dabbel
  • 2,468
  • 1
  • 8
  • 25
1

You can use RotatedBox widget (for rotation) along with InteractiveViewer widget(for zoom in and zoom out).

panEnabled property in InteractiveViewer widget used for moving the container

Scaffold(
    backgroundColor: Colors.black,
    body: Center(
      child: RotatedBox(
        quarterTurns: 1,
        child: InteractiveViewer(
          boundaryMargin: EdgeInsets.zero,
          minScale: 1,
          maxScale: 4,
          child: Container(
            height: 200,
            width: 200,
            color: Colors.blue,
          ),
        ),
      ),
    ),
  ),
Sanju Bhatt
  • 944
  • 10
  • 7
0

You can also use photo_viewer instead of matrix_gesture_detector. More info in this answer: https://stackoverflow.com/a/62426232/2942294

syonip
  • 2,762
  • 25
  • 27
0

i had same issue. the problem is solved by Matrix gesture detetctor package. but i have solved this by another case:- the whole code is here.

// @dart=2.9
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

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

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

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

class ContainerList {
  double height;
  double width;
  double scale;
  double rotation;
  double xPosition;
  double yPosition;

  ContainerList({
    this.height,
    this.rotation,
    this.scale,
    this.width,
    this.xPosition,
    this.yPosition,
  });
}

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

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

class _HomePageState extends State<HomePage> {
  List<ContainerList> list = [];
  Offset _initPos;
  Offset _currentPos = Offset(0, 0);
  double _currentScale;
  double _currentRotation;
  Size screen;

  @override
  void initState() {
    screen = Size(400, 500);
    list.add(ContainerList(
      height: 200.0,
      width: 200.0,
      rotation: 0.0,
      scale: 1.0,
      xPosition: 0.1,
      yPosition: 0.1,
    ));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Container(
        padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
        height: 500.0,
        color: Colors.blue.withOpacity(0.8),
        width: double.infinity,
        child: Stack(
          children: list.map((value) {
            return GestureDetector(
              onScaleStart: (details) {
                if (value == null) return;
                _initPos = details.focalPoint;
                _currentPos = Offset(value.xPosition, value.yPosition);
                _currentScale = value.scale;
                _currentRotation = value.rotation;
              },
              onScaleUpdate: (details) {
                if (value == null) return;
                final delta = details.focalPoint - _initPos;
                final left = (delta.dx / screen.width) + _currentPos.dx;
                final top = (delta.dy / screen.height) + _currentPos.dy;

                setState(() {
                  value.xPosition = Offset(left, top).dx;
                  value.yPosition = Offset(left, top).dy;
                  value.rotation = details.rotation + _currentRotation;
                  value.scale = details.scale * _currentScale;
                });
              },
              child: Stack(
                children: [
                  Positioned(
                    left: value.xPosition * screen.width,
                    top: value.yPosition * screen.height,
                    child: Transform.scale(
                      scale: value.scale,
                      child: Transform.rotate(
                        angle: value.rotation,
                        child: Container(
                          height: value.height,
                          width: value.width,
                          child: FittedBox(
                            fit: BoxFit.fill,
                            child: Listener(
                              onPointerDown: (details) {
                                // if (_inAction) return;
                                // _inAction = true;
                                // _activeItem = val;
                                _initPos = details.position;
                                _currentPos =
                                    Offset(value.xPosition, value.yPosition);
                                _currentScale = value.scale;
                                _currentRotation = value.rotation;
                              },
                              onPointerUp: (details) {
                                // _inAction = false;
                              },
                              child: Container(
                                height: value.height,
                                width: value.width,
                                color: Colors.red,
                              ),
                              // child: Image.network(value.name),
                            ),
                          ),
                        ),
                      ),
                    ),
                  )
                ],
              ),
            );
          }).toList(),
        ),
      ),
    );
  }
}

so now i can khow the rotation angle value and scale value also. in matrix gesture detector package you can not khow about this values.

  • Your answer helped finding a solution, thank you. I came up with this: https://stackoverflow.com/a/74165192/12098106 – Dabbel Oct 22 '22 at 16:23