Axel
Axel

Reputation: 5111

How to rotate image in flutter based on a GestureDetector handle?

I have the following code:

import 'package:flutter/material.dart';

double ballRadius = 7.5;

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  double finalAngle = 0.0;
  double oldAngle = 0.0;
  double upsetAngle = 0.0;

  double _height = 200;
  double _width = 300;

  Offset centerOfGestureDetector = Offset(ballRadius, ballRadius);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: SafeArea(
          child: // rotate
              Transform.rotate(
            angle: finalAngle,
            child: Stack(
              children: [
                Positioned(
                  top: 50,
                  left: 50 + _width / 2,
                  child: Column(
                    children: [
                      GestureDetector(
                        behavior: HitTestBehavior.translucent,
                        onPanStart: (details) {
                          final touchPositionFromCenter =
                              details.localPosition - centerOfGestureDetector;
                          upsetAngle =
                              oldAngle - touchPositionFromCenter.direction;
                        },
                        onPanEnd: (details) {
                          setState(
                            () {
                              oldAngle = finalAngle;
                            },
                          );
                        },
                        onPanUpdate: (details) {
                          final touchPositionFromCenter =
                              details.localPosition - centerOfGestureDetector;

                          setState(
                            () {
                              finalAngle = touchPositionFromCenter.direction +
                                  upsetAngle;
                            },
                          );
                        },
                        child: Container(
                          height: 2 * ballRadius,
                          width: 2 * ballRadius,
                          decoration: BoxDecoration(
                            color: Colors.white,
                            borderRadius: BorderRadius.circular(ballRadius),
                            border: Border.all(
                              width: 2,
                              color: Colors.blue,
                            ),
                          ),
                        ),
                      ),
                      Container(
                        height: 50,
                        width: 2,
                        color: Colors.blue,
                      ),
                    ],
                  ),
                ),
                Positioned(
                  top: 100,
                  left: 50,
                  child: Image.network(
                    "https://via.placeholder.com/300x200",
                    width: _width,
                    height: _height,
                    fit: BoxFit.fill,
                  ),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}

When the handle is dragged, the image should rotate accordingly. But as you can see it's not working correctly. If I don't use Stack and Positioned, it works fine. But I do need Stack and Positioned. Please help me with this.

I am expecting the rotation to be like:

enter image description here

Upvotes: 3

Views: 2378

Answers (2)

Henok
Henok

Reputation: 3383

Adding a fixed height and width to the stack fixes the issue and it is rotating correctly as you can see below.

enter image description here


import 'package:flutter/material.dart';

double ballRadius = 7.5;

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  double finalAngle = 0.0;
  double oldAngle = 0.0;
  double upsetAngle = 0.0;

  double _height = 200;
  double _width = 300;

  Offset centerOfGestureDetector = Offset(ballRadius, ballRadius);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: SafeArea(
          child: // rotate
          Stack(
            children: [
              getImageWithHandle(),
            ],
          ),
        ),
      ),
    );
  }
  
  
  Widget getImageWithHandle(){
    return Transform.rotate(
                  angle: finalAngle,
                  child: SizedBox(
                    height: 400,
                    width: 400,
                    child: Stack(
                      children: [
                        Positioned(
                          top: 50,
                          left: 50 + _width / 2,
                          child: Column(
                            mainAxisSize: MainAxisSize.min,
                            children: [
                              GestureDetector(
                                behavior: HitTestBehavior.translucent,
                                onPanStart: (details) {
                                  final touchPositionFromCenter =
                                      details.localPosition - centerOfGestureDetector;
                                  upsetAngle =
                                      oldAngle - touchPositionFromCenter.direction;
                                },
                                onPanEnd: (details) {
                                  setState(
                                        () {
                                      oldAngle = finalAngle;
                                    },
                                  );
                                },
                                onPanUpdate: (details) {
                                  final touchPositionFromCenter =
                                      details.localPosition - centerOfGestureDetector;

                                  setState(
                                        () {
                                      finalAngle = touchPositionFromCenter.direction +
                                          upsetAngle;
                                    },
                                  );
                                },
                                child: Container(
                                  height: 2 * ballRadius,
                                  width: 2 * ballRadius,
                                  decoration: BoxDecoration(
                                    color: Colors.white,
                                    borderRadius: BorderRadius.circular(ballRadius),
                                    border: Border.all(
                                      width: 2,
                                      color: Colors.blue,
                                    ),
                                  ),
                                ),
                              ),
                              Container(
                                height: 50,
                                width: 2,
                                color: Colors.blue,
                              ),
                            ],
                          ),
                        ),
                        Positioned(
                          top: 100,
                          left: 50,
                          child: Image.network(
                            "https://via.placeholder.com/300x200",
                            width: _width,
                            height: _height,
                            fit: BoxFit.fill,
                          ),
                        )
                      ],
                    ),
                  ),
                );
  }
}


Upvotes: 7

Ronak Patel
Ronak Patel

Reputation: 629

I think this could be your solution and here is the demo https://dartpad.dartlang.org/dart.

import 'dart:math';

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: RotateText(),
    );
  }
}

class RotateText extends StatefulWidget {
  RotateText({Key key}) : super(key: key); // changed

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

class _RotateTextState extends State<RotateText> {
  double finalAngle = 0.0;

  @override
  Widget build(BuildContext context) {
    return _defaultApp(context);
  }

  _defaultApp(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Single finger Rotation'), // changed
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            Container(
              color: Colors.red,
              padding: EdgeInsets.all(10),
              margin: EdgeInsets.only(top: 50),
              child: Transform.rotate(
                angle: finalAngle,
                child: Container(
                  height: 100.0,
                  width: 100.0,
                  child: Image.network(
                    'https://picsum.photos/250?image=9',
                  ),
                ),
              ),
            ),
            Container(
              width: 250,
              height: 250,
              color: Colors.grey,
              margin: EdgeInsets.all(30.0),
              child: LayoutBuilder(
                builder: (context, constraints) {
                  return GestureDetector(
                    behavior: HitTestBehavior.translucent,
                    onPanUpdate: (details) {
                      Offset centerOfGestureDetector = Offset(
                          constraints.maxWidth / 2, constraints.maxHeight / 2);
                      final touchPositionFromCenter =
                          details.localPosition - centerOfGestureDetector;
                      print(touchPositionFromCenter.direction * 180/pi);
                      setState(
                            () {
                          finalAngle = touchPositionFromCenter.direction;
                        },
                      );
                    },
                    child: Transform.rotate(
                      angle: finalAngle,
                      child: Icon(
                        Icons.arrow_forward,
                        color: Colors.white,
                        size: 200,
                      ),
                    ),
                  );
                },
              ),
            )
          ],
        ),
      ),
    );
  }
}

Upvotes: 0

Related Questions