Search code examples
flutterflutter-animation

Flutter rebuild a TweenAnimationBuilder


I want to make a flipping animation with TweenAnimationBuilder, a container will flip over and change the color. I want to add a button when user click on it, the container will flip over again and change into another colour.

Here is my code:

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

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

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

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool isBack = true;

  Color backColor = Colors.green;
  Color topColor = Colors.red;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              box(),
              SizedBox(height: 10),
              ElevatedButton(
                  style: ElevatedButton.styleFrom(
                    minimumSize: Size(25, 10),
                    elevation: 10,
                  ),
                  onPressed: () {
                    setState(() {
                      backColor = Colors.red;
                      topColor = Colors.blue;
                    });
                  },
                  child:
                      Text('change to blue', style: TextStyle(fontSize: 16))),
            ],
          ),
        ),
      ),
    );
  }

  Widget box() {
    print('building');
    return TweenAnimationBuilder(
        tween: Tween<double>(begin: 0, end: pi),
        duration: Duration(seconds: 1),
        builder: (BuildContext context, double value, _) {
          print(value);
          if (value >= (pi / 2)) {
            isBack = false;
          } else {
            isBack = true;
          }
          return (Transform(
            alignment: Alignment.center,
            transform: Matrix4.identity()
              ..setEntry(3, 2, 0.001)
              ..rotateY(value),
            child: Container(
                width: 100,
                height: 100,
                child: isBack
                    ? Container(
                        color: backColor,
                      )
                    : Transform(
                        alignment: Alignment.center,
                        transform: Matrix4.identity()
                          ..rotateY(
                              pi),
                        child: Container(
                          color: topColor,
                        ),
                      ) 
                ),
          ));
        });
  }
}

At the first build, the tween value will start from the beginning:

building
0
0
0
0.13080335172486462
0.19619246121668257
0.2180893620122034
...
3.141592653589793

but when I click on the button to change the color, it will not start again from the begin value, it just stays at 3.14:

building
3.141592653589793

Right now the button will only change the color of the container, but it will not flip again.

I suppose after the setstate function, the tween value will restart again at 0, why won't it do so? Can anybody explain it please?


Solution

  • All you need is an AnimationController and AnimatedBuilder to control the animation.

    class _HomePageState extends State<HomePage>
        with SingleTickerProviderStateMixin {
      bool isBack = true;
    
      Color backColor = Colors.blue;
      Color topColor = Colors.red;
      late AnimationController _animationController;
      late Animation _rotationAnimation;
      @override
      void initState() {
        super.initState();
        _animationController =
            AnimationController(vsync: this, duration: const Duration(seconds: 1));
    
        _rotationAnimation =
            Tween<double>(begin: 0, end: pi).animate(_animationController);
    
         _animationController.forward();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: SafeArea(
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  box(),
                  SizedBox(height: 10),
                  ElevatedButton(
                      style: ElevatedButton.styleFrom(
                        minimumSize: Size(25, 10),
                        elevation: 10,
                      ),
                      onPressed: () {
                        setState(() {
                          backColor = Colors.red;
                          topColor = Colors.blue;
                        });
                        // if (_animationController.isDismissed) {
                        //   _animationController.forward();
                        // } else if (_animationController.isCompleted) {
                        //   _animationController.reverse();
                        // }
                        _animationController.forward(from: 0.0);
                      },
                      child:
                          Text('change to blue', style: TextStyle(fontSize: 16))),
                ],
              ),
            ),
          ),
        );
      }
    
      Widget box() {
        return AnimatedBuilder(
            animation: _animationController,
            // tween: Tween<double>(begin: 0, end: pi),
            // duration: Duration(seconds: 1),
            builder: (_, __) {
              // print(value);
              if (_rotationAnimation.value >= (pi / 2)) {
                isBack = false;
              } else {
                isBack = true;
              }
              return Transform(
                alignment: Alignment.center,
                transform: Matrix4.identity()
                  ..setEntry(3, 2, 0.001)
                  ..rotateY(_rotationAnimation.value),
                child: Container(
                  width: 100,
                  height: 100,
                  child: isBack
                      ? Container(
                          color: backColor,
                        )
                      : Transform(
                          alignment: Alignment.center,
                          transform: Matrix4.identity()..rotateY(pi),
                          child: Container(
                            color: topColor,
                          ),
                        ),
                ),
              );
            });
      }
    }