Lakshay Dutta
Lakshay Dutta

Reputation: 476

How to apply translate and rotate animation in flutter to create a "cards being flung around" effect

I am creating a n UNO game app and this is the intro screen part. I need a very custom kind of animation and I dont have much knowledge in flutter custom animations.

Here is a little preview

enter image description here

Now I want to create a "cards flying around" animation. It is basically cards (which are containers with svg assets) being translated and rotated AT THE SAME TIME across the screen to create a flying card effect. This animation will be repeated over and over.

I have managed to make a very basic version which just translates , doesnt rotate and doesnt look all that pretty. Here is the code.

INTROSCREEN

class IntroScreen extends StatefulWidget {
  @override
  _IntroScreenState createState() => _IntroScreenState();
}

class _IntroScreenState extends State<IntroScreen>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation _animation;

  @override
  void initState() {
    _controller =
        AnimationController(vsync: this, duration: Duration(seconds: 4));
    _animation = RainbowColorTween([
      CardColors.COLOR1,
      CardColors.COLOR2,
      CardColors.COLOR3,
      CardColors.COLOR4,
      CardColors.COLOR1,
    ]).chain(CurveTween(curve: Curves.easeInOut)).animate(_controller);
    _controller.addListener(() {
      setState(() {});
    });
    _controller.repeat();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: Container(
          color: _animation.value,
          child: ChangeNotifierProvider<_DataModel>(
            create: (context) => _DataModel(),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                SizedBox(
                  height: 100,
                ),
                _Logo(),
                SizedBox(
                  height: 50,
                ),
                _TextField(),
                _SelectCards(),
                _Play(),
                Expanded(child: FlyingCards(MediaQuery.of(context).size.width)),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class _Logo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 200,
      child: FlareActor(
        "assets/intro_anim.flr",
        alignment: Alignment.center,
        fit: BoxFit.contain,
        animation: 'intro',
      ),
    );
  }
}

Here is what I managed to build

class FlyingCards extends StatefulWidget {
  final double width;
  FlyingCards(this.width);
  @override
  _FlyingCardsState createState() => _FlyingCardsState();
}

class _FlyingCardsState extends State<FlyingCards>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation _animation;

  @override
  void initState() {
    _controller =
        AnimationController(vsync: this, duration: Duration(seconds: 2));

    _animation = Tween<double>(begin: 0, end: widget.width)
        .chain(CurveTween(curve: Curves.ease))
        .animate(_controller)
          ..addListener(() {
            setState(() {});
          })
          ..addStatusListener((status) {
            if (status == AnimationStatus.dismissed)
              _controller.forward();
            else if (status == AnimationStatus.completed) _controller.reverse();
          });

    _controller.forward();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      child: Transform.translate(
        offset: Offset(_animation.value, 0),
        child: Container(
          height: 50,
          width: 50,
          child: SvgPicture.asset('assets/plus4.svg'),
          decoration: BoxDecoration(borderRadius: BorderRadius.circular(20)),
        ),
      ),
    );
  }
}

In the above picture , the card is actually translating across the device's width back and forth.

I feel what I have created is the cumbersome way of doing animations. If anyone still didnt get what I mean by flying cards animation, the same kind of effect appears on angry birds game screen, it's just bird and pigs flying around. I need the same but with my cards.

Please check out this video and jump to 0:22 for a small reference of what UI I am hoping for.

I tried to reduce the complexity as much as I could. Thanks for your time!

Upvotes: 5

Views: 9784

Answers (2)

Lakshay Dutta
Lakshay Dutta

Reputation: 476

I am also posting another way for doing this same task, which will be easier for a lot of people. However I will not mark this as the accepted answer because previous answer is the real way to do it.

firstly we need to add flare dependency. Flare is like lottie for flutter. Basically it helps us play pre-made vector animations in 60fps or more

dependencies:
  flutter:
  flare_flutter: ^2.0.3

now head over to rive and create any animation that you like. You can add vectors and your own resources and create animations. I just made some cards using illustrator. you can see my cards flying animation here

Once that is done, just export the animation in a binary format. the file will have .flr format.

now apply the animation from the assets folder.

import 'package:flare_flutter/flare_actor.dart';
return Container(
      height: 200,
      width: MediaQuery.of(context).size.width,
      child: FlareActor(
        "assets/cards_flying.flr",
        alignment: Alignment.center,
        fit: BoxFit.fill,
        animation: 'cards',
      ),
    );

I hope it was useful in some way. I found it worth mentioning.

Upvotes: 1

camillo777
camillo777

Reputation: 2377

Here is a simple example that you can extend. The "Hello world!" text is translating and rotating.

import 'package:flutter/material.dart';
import 'dart:math';

final Color darkBlue = Color.fromARGB(255, 18, 32, 47);

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: MyAnim(),
        ),
      ),
    );
  }
}

class MyAnim extends StatefulWidget {
  @override
  State<MyAnim> createState() => MyAnimState();
}

class MyAnimState extends State<MyAnim> with SingleTickerProviderStateMixin {
  AnimationController control;

  Animation<double> rot;
  Animation<double> trasl;

  @override
  void initState() {
    super.initState();

    control = AnimationController(
      duration: Duration(seconds: 5),
      vsync: this,
    );

    rot = Tween<double>(
      begin: 0,
      end: 2 * pi,
    ).animate(control);

    trasl = Tween<double>(
      begin: 0,
      end: 300,
    ).animate(control);

    control.repeat();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
        animation: control,
        builder: (_, child) => Stack(children: <Widget>[
              Positioned(
                top: 100,
                left: trasl.value,
                child: Transform(
                  transform: Matrix4.rotationZ(rot.value),
                  alignment: Alignment.center,
                  child: Text('Hello, World!',
                      style: Theme.of(context).textTheme.headline4),
                ),
              ),
            ]));
  }
}

Upvotes: 4

Related Questions