Two animations at the same time without setstate

Issue

So I am struggling with annoying problem. I want to run 2 animations at the same time and somewhat it does not work without:

animationController.addListener(() {
  setState(() {});
});

Which is making everything choppy, which I would like to eradicate.

When I delete setState in both of my animation controllers and run this function:

 void animateButton() {
    if (_forward) {
      animationController.reverse();
      animationController2.reverse();
      _forward = false;
    } else {
      animationController.forward();
      animationController2.forward();
      _forward = true;
    }
  }

It runs just the first animation controller, without second one. Animations are simple tweens which are opposite to each other, so I was thinking about doing something which would use one tween and for second property I would do some inverse function, but I can’t think of any solution for this, so it would actually work. Maybe I am missing something simple.

Here is my full code:

class _DetailScreenState extends State<DetailScreenWidget>
    with TickerProviderStateMixin {
  final GlobalKey<SendWidgetState> _key = GlobalKey();

  AnimationController animationController;
  Animation<double> tween;
  AnimationController animationController2;
  Animation<double> tween2;

  @override
  void initState() {
    super.initState();
    // FocusScope.of(context).unfocus();
    animationController = AnimationController(
        vsync: this, duration: Duration(milliseconds: 800));
    tween = Tween<double>(begin: 0.0, end: 1.0).animate(
        CurvedAnimation(parent: animationController, curve: Curves.easeOut));
    animationController.addListener(() {
      setState(() {});
    });

    animationController2 = AnimationController(
        vsync: this, duration: Duration(milliseconds: 800));
    tween2 = Tween<double>(begin: 1.0, end: 0.0).animate(
        CurvedAnimation(parent: animationController2, curve: Curves.easeOut));
    animationController2.addListener(() {
      setState(() {});
    });
  }
  void showHideSendView() {
    if (_forward) {
      animationController.reverse();
      animationController2.reverse();
      _forward = false;
    } else {
      animationController.forward();
      animationController2.forward();
      _key.currentState.initView();
      _forward = true;
    }
  }

  void showTransactions() {
    setState(() {
      showHideSendView();
    });
  }

  Widget build(BuildContext context) {
    return Stack(children: [
      
      SafeArea(
        child: Stack(children: [
          TransactionWidget(
            opacity: tween2,
            getTransactions: _getTransactions,
          ),
          SizeTransition(
            sizeFactor: tween,
            axis: Axis.vertical,
            axisAlignment: -1,
            child: SendWidget(
              key:_key,
              func: shrinkSendView,
              height: tween,
            ),
          ),
          BalanceCard(
              getBalanceFuture: _getBalanceFuture,
              onPressSend: showTransactions),
        ]),
      ),
    ]);
  }
}

I learned about staggered animation in flutter, however I need 2 animations to run at the same time without setState. And for that I didn’t find on the internet anything, so I resort to asking questions here.

Solution

Alright I solved it, I guess there is a need for statefulWidget which updates on every step of the animation or maybe I am imagine things, now the working code, without stuttering.

I used advice by @Simon Pot and on the top of that I wrapped widget into FadeTransition

Here is the code:

class _DetailScreenState extends State<DetailScreenWidget>
    with TickerProviderStateMixin {
  final GlobalKey<SendWidgetState> _key = GlobalKey();

  AnimationController animationController;
  Animation<double> tween;
  Animation<double> tween2;

  Future _getBalanceFuture;
  Future _getTransactions;

  bool _forward = false;

  @override
  void initState() {
    super.initState();
    animationController = AnimationController(
        vsync: this, duration: Duration(milliseconds: 800));
    tween2 = Tween<double>(begin: 1.0, end: 0.0).animate(
        CurvedAnimation(parent: animationController, curve: Curves.easeOut));
    tween = Tween<double>(begin: 0.0, end: 1.0).animate(
        CurvedAnimation(parent: animationController, curve: Curves.easeOut));

    _getBalanceFuture = getBalance();
    _getTransactions = _getTranData();
  }

 
  void animateButton() {
    if (_forward) {
      animationController.reverse();
      _forward = false;
    } else {
      animationController.forward();
      _key.currentState.initView();
      _forward = true;
    }
  }

  void showTransactions() {
      animateButton();
  }


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

  Widget build(BuildContext context) {
    return Stack(children: [
      SafeArea(
        child: Stack(children: [
          FadeTransition(
            opacity: tween2,
            child: TransactionWidget(
            getTransactions: _getTransactions,
          ),
            ),
          SizeTransition(
            sizeFactor: tween,
            axis: Axis.vertical,
            axisAlignment: -1,
            child: SendWidget(
              key:_key,
              func: shrinkSendView,
              height: tween,
            ),
          ),
          BalanceCard(
              getBalanceFuture: _getBalanceFuture,
              onPressSend: showTransactions),
        ]),
      ),
    ]);
  }
}

Answered By – Michal Žídek

Answer Checked By – Mildred Charles (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *