55

I have star png image and I need to rotate the star using Flutter AnimationController and Transformer. I couldn't find any documents or example for image rotation animation.

Any idea How to rotate an image using Flutter AnimationController and Transform?

UPDATE:

class _MyHomePageState extends State<MyHomePage>  with TickerProviderStateMixin {

  AnimationController animationController;

  @override
  void initState() {
    super.initState();
    animationController = new AnimationController(
      vsync: this,
      duration: new Duration(milliseconds: 5000),
    );
    animationController.forward();
    animationController.addListener(() {
      setState(() {
        if (animationController.status == AnimationStatus.completed) {
          animationController.repeat();
        }
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Container(
      alignment: Alignment.center,
      color: Colors.white,
      child: new AnimatedBuilder(
        animation: animationController,
        child: new Container(
          height: 80.0,
          width: 80.0,
          child: new Image.asset('images/StarLogo.png'),
        ),
        builder: (BuildContext context, Widget _widget) {
          return new Transform.rotate(
            angle: animationController.value,
            child: _widget,
          );
        },
      ),
    );
  }
}
3
  • Could you past code with initialization of animationRotate? Commented Dec 12, 2018 at 14:50
  • I update my code. Problem is that it never rotate 360 degree. It rotates about 200 or something and starts over and there is a gap they I can see it that it redraws. Having an issue to rotate 360 degree and and when stops I need to repeat without delay so I can get continues rotation whiteout stop... Commented Dec 13, 2018 at 9:30
  • Updated the answer Commented Dec 13, 2018 at 10:04

11 Answers 11

69

Full example (null safe):

Demo Screenshot

Press "go" makes the star icon spin until you press "stop".

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    _controller = AnimationController(
      duration: const Duration(milliseconds: 5000),
      vsync: this,
    );
    super.initState();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Demo"),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            RotationTransition(
              turns: Tween(begin: 0.0, end: 1.0).animate(_controller),
              child: Icon(Icons.stars),
            ),
            ElevatedButton(
              child: Text("go"),
              onPressed: () => _controller.forward(),
            ),
            ElevatedButton(
              child: Text("reset"),
              onPressed: () => _controller.reset(),
            ),
          ],
        ),
      ),
    );
  }
}

Step by step guide:

First, let your widget state class implement SingleTickerProviderStateMixin.

Secondly, define an AnimationController and don't forget to dispose it. If you are not yet using null-safe, remove the late keyword.

late AnimationController _controller;

@override
void initState() {
  _controller = AnimationController(
    duration: const Duration(milliseconds: 5000),
    vsync: this,
  );
  super.initState();
}

@override
void dispose() {
  _controller.dispose();
  super.dispose();
}

Then wrap your Widget with RotationTransition.

RotationTransition(
  turns: Tween(begin: 0.0, end: 1.0).animate(_controller),
  child: Icon(Icons.stars),
),

Finally, call methods on the AnimationController to start/stop animation.

  • Run the animation once, use .forward
  • Loop the animation, use .repeat
  • Stop immediately, use .stop
  • Stop and set it back to original rotation, use .reset
  • Stop and animate to a rotation value, use .animateTo
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks so much for this answer. I just wanted to add that if you want to add some nice animations on your rotation you could use this: CurveTween(curve: Curves.elasticInOut).animate(_controller) and you can experiment with the effects by changing Curves.elasticInOut
52

Screenshot (Null Safe)

enter image description here


Full code:

import 'dart:math' as math;

class _FooPageState extends State<FooPage> with SingleTickerProviderStateMixin{
  late final AnimationController _controller = AnimationController(vsync: this, duration: Duration(seconds: 2))..repeat();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: AnimatedBuilder(
          animation: _controller,
          builder: (_, child) {
            return Transform.rotate(
              angle: _controller.value * 2 * math.pi,
              child: child,
            );
          },
          child: FlutterLogo(size: 200),
        ),
      ),
    );
  }
}

3 Comments

How to rotate this along the Y axis, any ideas?
Hi, you've forgot to dispose the animation controller, which leads to 'ticker leak' exception each time the widget is disposed. Let me edit your solution...
@DmytroGierman To keep the answer small and relevant, I didn't dispose the controller.
43

Here my example of rotating image. I don't know - but maybe it suits for you

AnimationController rotationController;

@override
void initState() {
  rotationController = AnimationController(duration: const Duration(milliseconds: 500), vsync: this);
  super.initState();
}
//...
RotationTransition(
  turns: Tween(begin: 0.0, end: 1.0).animate(rotationController),
  child: ImgButton(...)
//...
rotationController.forward(from: 0.0); // it starts the animation

UPD - how to solve problem with Transform.rotate

In your case all works exactly as you've written - it rotates image from 0.0 to 1.0 (it's default parameters for AnimationController). For full circle you have to set upper parameter to 2 * pi (from math package)

import 'dart:math';
//...
animationController = AnimationController(vsync: this, duration: Duration(seconds: 5), upperBound: pi * 2);

2 Comments

how can we add curves here
@codeboi here is answer stackoverflow.com/a/55365126/7130820
5

Flutter also has the widget AnimatedRotation (docs) which makes rotating something much easier.

You only need a double to set the state of the rotation. It works in a percentage of rotation, if you turn it into degrees, it will be 0.0 = 0deg, 1.0 = 360deg

double turns = 0.0;
AnimatedRotation(
  duration: const Duration(milliseconds: 500),
  turns: turns,
  child: const Icon(Icons.refresh),
)

To make the rotation happen you only need to update the state, and Flutter will execute the animation automatically

void _changeRotation() {
  setState(() => turns += 1.0 / 8.0);
}

Full example taken from the Flutter docs to rotate the flutter Logo

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

  @override
  State<LogoRotate> createState() => LogoRotateState();
}

class LogoRotateState extends State<LogoRotate> {
  double turns = 0.0;

  void _changeRotation() {
    setState(() => turns += 1.0 / 8.0);
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        ElevatedButton(
          onPressed: _changeRotation,
          child: const Text('Rotate Logo'),
        ),
        Padding(
          padding: const EdgeInsets.all(50),
          child: AnimatedRotation(
            turns: turns,
            duration: const Duration(seconds: 1),
            child: const FlutterLogo(),
          ),
        ),
      ],
    );
  }
}

Comments

4

Here i rotate 3 images at once... images saved in assets folder... if you want you can use network images also... i am here rotate 3 images on 3 speeds...

import 'package:flutter/material.dart';
import 'package:fynd/services/auth.dart';
import 'dart:async';
import 'package:fynd/services/cons.dart';

class SplashScreen extends StatefulWidget {
  _SplashScreen createState() => new _SplashScreen();
}

class _SplashScreen extends State<StatefulWidget>
    with SingleTickerProviderStateMixin {
  AnimationController animationController;

  @override
  void initState() {
    super.initState();
    animationController = new AnimationController(
      vsync: this,
      duration: new Duration(seconds: 5),
    );

    animationController.repeat();
  }

  @override
  Widget build(BuildContext context) {

    return new Container(
      alignment: Alignment.center,
      color: Colors.white,
      child: new AnimatedBuilder(
        animation: animationController,
        child: new Container(
          decoration: BoxDecoration(
              image: DecorationImage(
                  image: AssetImage('assets/images/splash_circle3.png'))),
          child: new AnimatedBuilder(
            animation: animationController,
            child: new Container(
              decoration: BoxDecoration(
                  image: DecorationImage(
                      image: AssetImage('assets/images/splash_circle2.png'))),
              child: new AnimatedBuilder(
                  animation: animationController,
                  child: Container(
                      child: Container(
                    decoration: BoxDecoration(
                        image: DecorationImage(
                            image: AssetImage(
                                'assets/images/splash_circle1.png'))),
                  )),
                  builder: (BuildContext context, Widget _widget) {
                    return new Transform.rotate(
                      angle: animationController.value * 4,
                      child: _widget,
                    );
                  }),
            ),
            builder: (BuildContext context, Widget _widget) {
              return new Transform.rotate(
                angle: animationController.value * 5,
                child: _widget,
              );
            },
          ),
        ),
        builder: (BuildContext context, Widget _widget) {
          return new Transform.rotate(
            angle: animationController.value * 6,
            child: _widget,
          );
        },
      ),
    );
  }
}

Comments

4

here i put the animated builder in Stack. then you can animate image rotate right(clockwise) and rotate left(anti clockwise).

import 'package:flutter/material.dart';
import 'package:fynd/services/auth.dart';
import 'dart:async';
import 'package:fynd/services/cons.dart';

class SplashScreen extends StatefulWidget {
  _SplashScreen createState() => new _SplashScreen();
}

class _SplashScreen extends State<StatefulWidget>
    with SingleTickerProviderStateMixin {
  AnimationController animationController;

  @override
  void initState() {
    super.initState();
    animationController = new AnimationController(
      vsync: this,
      duration: new Duration(seconds: 5),
    );

    animationController.repeat();

  }

  @override
  Widget build(BuildContext context)

    return new Container(
      alignment: Alignment.center,
      color: Colors.white,

      child: new Stack(children: <Widget>[

        new AnimatedBuilder(
          animation: animationController,
          child :Container(
            decoration: BoxDecoration(
                image: DecorationImage(image: AssetImage('assets/images/splash_circle3.png'),fit: BoxFit.cover)),

          ),
          builder: (BuildContext context, Widget _widget) {
            return new Transform.rotate(
              angle: animationController.value * 10,
              child: _widget,
            );
          },
        ),

       new AnimatedBuilder(
        animation: animationController,
        child: Container(
          decoration: BoxDecoration(
              image: DecorationImage(image: AssetImage('assets/images/splash_circle2.png'),fit: BoxFit.cover)),

        ),
        builder: (BuildContext context, Widget _widget) {
          return new Transform.rotate(
            angle: -animationController.value * 10,
            child: _widget,
          );
        },
       ),

        new AnimatedBuilder(
            animation: animationController,
           child: Container(
              decoration: BoxDecoration(
                  image: DecorationImage(image: AssetImage('assets/images/splash_circle1.png'), fit: BoxFit.cover)),
            ),
            builder: (BuildContext context, Widget _widget) {
              return new Transform.rotate(
                angle: animationController.value * 10,
                child: _widget,
              );
            }),

      ],),
    );
  }
}

Comments

2
class _MyHomePageState extends State<MyHomePage>  with TickerProviderStateMixin {

  AnimationController animationController;

  @override
  void initState() {
    super.initState();
    animationController = new AnimationController(
      vsync: this,
      duration: new Duration(milliseconds: 5000),
    );
    animationController.repeat();

  }

  @override
  Widget build(BuildContext context) {
    return new Container(
      alignment: Alignment.center,
      color: Colors.white,
      child: RotationTransition(
              child: Icon(Icons.refresh),
              turns: controller,
            )
    );
  }
}

Comments

1
    _controller = AnimationController(duration: const Duration(seconds: 3), vsync: this);
    _animation = Tween(begin: 0.0, end: 250.0).animate(_controller)
      ..addListener(() {
        setState(() {});
      })
      ..addStatusListener((state) {
        if (state == AnimationStatus.completed) {
          print("complete");
        }
      });
    _controller.forward();

    new Future.delayed(
        const Duration(seconds: 5),
            () => Navigator.push(
          context,
          MaterialPageRoute(builder: (context) => SignScreen()),
        ));    

Comments

0

full example just call ImageAnimateRotate( your widget )

class ImageAnimateRotate extends StatefulWidget {
  final Widget child;
  const ImageAnimateRotate({Key? key, required this.child}) : super(key: key);

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

class _ImageAnimateRotateState extends State<ImageAnimateRotate> with SingleTickerProviderStateMixin {
  late final AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: Duration(seconds: 20))..repeat();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: AnimatedBuilder(
        animation: _controller,
        builder: (_, child) {
          return Transform.rotate(
            angle: _controller.value * 2 * math.pi,
            child: child,
          );
        },
        child: widget.child,
      ),
    );
  }
}

Comments

0

2023.9 Update:Now you can use AnimatedRotation Widget. It's much easier, no AnimationController is needed.

From official docs:

class LogoRotate extends StatefulWidget {
  const LogoRotate({super.key});

  @override
  State<LogoRotate> createState() => LogoRotateState();
}

class LogoRotateState extends State<LogoRotate> {
  double turns = 0.0;

  void _changeRotation() {
    setState(() => turns += 1.0 / 8.0);
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        ElevatedButton(
          onPressed: _changeRotation,
          child: const Text('Rotate Logo'),
        ),
        Padding(
          padding: const EdgeInsets.all(50),
          child: AnimatedRotation(
            turns: turns,
            duration: const Duration(seconds: 1),
            child: const FlutterLogo(),
          ),
        ),
      ],
    );
  }
}

Comments

0
IconButton(
        splashColor: Colors.transparent,
        highlightColor: Colors.transparent,
        icon: AnimatedSwitcher(
          duration: const Duration(milliseconds: 350),
          transitionBuilder: (child, anim) => RotationTransition(
            turns: child.key == const ValueKey('icon1') ? Tween<double>(begin: 1, end: 0).animate(anim) : Tween<double>(begin: 0, end: 1).animate(anim),
            child: ScaleTransition(scale: anim, child: child),
          ),
          child: ThemeService().isSavedDarkMode() == true ? const Icon(Icons.dark_mode, key: ValueKey("icon2")) : const Icon(Icons.light_mode, key: ValueKey("icon1")),
        ),
        onPressed: () => setState(() {ThemeService().changeTheme();}),
      ),

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.