Search code examples
fluttertransformflutter-layoutgesture-recognitionflutter-test

Rotate text or image using button gesture in flutter


Now, the image is smoothly rotating only from left to right when I make a gesture of 360 degree.

Required result:

  • Should rotate from right to left when we make a gesture of 360 degree.
  • Done: Should rotate from right to left when we make a gesture of 360 degree.
  • Once we start rotating from left to right at some point and again back to right to left it should rotate from either direction from which the gesture is made.

enter image description here

  import 'dart:math';

  import 'package:flutter/material.dart';

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

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

  class _RotateImageState extends State<RotateImage> {
    double finalAngle = 0.0;

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

    _defaultApp(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text('Single finger Rotate text'), // 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,
                  origin: Offset(0, 0),
                  child: Container(
                    height: 100.0,
                    width: 100.0,
                    child: Image.network(
                      'https://picsum.photos/250?image=9',
                    ),
                  ),
                ),
              ),
              GestureDetector(
                onPanStart: (detials) {},
                onPanEnd: (detials) {},
                onPanUpdate: (details) {
                  setState(
                    () {
                      finalAngle += details.delta.distance * -pi / 180;
                    },
                  );
                },
                child: Container(
                  margin: EdgeInsets.only(top: 30),
                  color: Colors.black54,
                  width: 50,
                  height: 50,
                  child: Icon(
                    Icons.rotate_left,
                    color: Colors.white,
                  ),
                ),
              )
            ],
          ),
        ),
      );
    }
  }

Solution

  • Here if you do gesture (with one finger) in circle around the icon, it will rotate.

    Source Code 1: (Here the angle is based on finger position from the center of GestureDetector)

    Demo: DartPad,

    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 Rotate text'), // 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,
                          ),
                        ),
                      );
                    },
                  ),
                )
              ],
            ),
          ),
        );
      }
    }
    

    Source Code 2: (Here the angle is persisted and will continue update on every onPanStart)

    Demo: DartPad,

    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;
      double offsetAngle = 0.0;
    
      @override
      Widget build(BuildContext context) {
        return _defaultApp(context);
      }
    
      _defaultApp(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Single finger Rotate text'), // 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,
                        onPanStart: (details) {
                          Offset centerOfGestureDetector = Offset(
                              constraints.maxWidth / 2, constraints.maxHeight / 2);
                          final touchPositionFromCenter =
                              details.localPosition - centerOfGestureDetector;
                          offsetAngle =
                              touchPositionFromCenter.direction - finalAngle;
                        },
                        onPanUpdate: (details) {
                          Offset centerOfGestureDetector = Offset(
                              constraints.maxWidth / 2, constraints.maxHeight / 2);
                          final touchPositionFromCenter =
                              details.localPosition - centerOfGestureDetector;
                          setState(() {
                            finalAngle =
                                touchPositionFromCenter.direction - offsetAngle;
                          });
                        },
                        child: Transform.rotate(
                          angle: finalAngle,
                          child: Icon(
                            Icons.settings,
                            color: Colors.white,
                            size: 200.0,
                          ),
                        ),
                      );
                    },
                  ),
                )
              ],
            ),
          ),
        );
      }
    }