skip to Main Content

I am trying to create two sequential animations using Flutter. Both are using Curves.easeIn, and the second one starts on top of the first one, instead of from 0. Image a car starts to move from 0 to 50 miles/hours, after it runs a few while, it speeds to 70 miles/hour from 50 miles/hour instead of 0 miles/hour. How can I do it?
I am aware of and using TweenSequence, but I can’t figure out a way to make it work.
Thanks.

2

Answers


  1. It may be helpful to see what exactly it is that you’ve tried to ensure that we’re not just flying blind here and missing your point, but I can point you to the documentation here and provide:

    final Animation<double> animation = TweenSequence<double>(
      <TweenSequenceItem<double>>[
        TweenSequenceItem<double>(
          tween: Tween<double>(begin: 0.0, end: 50.0)
            .chain(CurveTween(curve: Curves.ease)),
          weight: 40.0,
        ),
        // This may be optional for you: this should keep the animation at a given point.
        TweenSequenceItem<double>(
          tween: ConstantTween<double>(50.0),
          weight: 20.0,
        ),
        TweenSequenceItem<double>(
          tween: Tween<double>(begin: 50.0, end: 70.0)
            .chain(CurveTween(curve: Curves.easeIn)),
          weight: 40.0,
        ),
      ],
    ).animate(myAnimationController);
    

    Happy to take a deeper look if you can add the code that’s giving you trouble, and I can modify my answer to be more bespoke if needed. Goodluck.

    Login or Signup to reply.
  2. You could try something like this:

    import 'package:flutter/material.dart';
    
    class PageAnimationExample extends StatefulWidget {
      const PageAnimationExample({super.key});
    
      @override
      State<PageAnimationExample> createState() => _PageAnimationExampleState();
    }
    
    class _PageAnimationExampleState extends State<PageAnimationExample>
        // If you need two or more animation controllers be sure to use
        //  [TickerProviderStateMixin] instead of [SingleTickerProviderStateMixin]
        with
            SingleTickerProviderStateMixin {
      late AnimationController animationController;
      late Animation<double> animation1;
      late Animation<double> animation2;
    
      // This can be whatever duration you want
      static const Duration _duration = Duration(milliseconds: 3000);
    
      @override
      void initState() {
        super.initState();
        // Create an [AnimationController] to drive the animations: both animations
        //  can share a controller as long as they are both supposed to share the
        //  same animation duration
        animationController = AnimationController(vsync: this, duration: _duration)
          // Note: you'll need to setState to force a rebuild. You can avoid this
          //  step if you use an [AnimatedBuilder] widget instead and provide the
          //  animationController as the controller.
          ..addListener(() => setState(() {}));
    
        // We'll need two animations, the first of which will animate from 0.0-1.0
        //  but only on the first half of the curve (0.0 - 0.5). The second will run
        //  only on the second half of the curve (0.5 - 1.0).
    
        // The tricky thing is that Flutter doesn't directly support cutting curves.
        //  You can string together two curves with similar momentum/movement though
        //  such that the result feels like a single animation. I'll give more
        //  examples below.
    
        // For easeIn, see if a combo of [easeIn] at first and then [linear] would
        //  work for you. Keep in mind that if you wanted to keep the linear
        //  component with the same slope as where your first curve ends, you could
        //  adjust the interval to have the first curve take longer/shorter and the
        //  second curve to be shorter/longer. Imagine them like stacking.
    
        animation1 = Tween<double>(begin: 0.0, end: 1.0).animate(
          CurvedAnimation(
            parent: animationController,
            curve: const Interval(
              // The first half of the animation only, using the given curve
              0.0,
              // TODO: you can overlap these intervals too, to have one curve start
              //  before the first one is done. Try this one as 0.67 and the below
              //  as 0.33 as an example
    
              0.5,
              curve: Curves.easeIn,
            ),
          ),
        );
    
        // NOTE: You can contrary to what I mentioned above, you can leave a gap
        //  between when your curves start and end if you need to have a pause
    
        animation2 = Tween<double>(begin: 0.0, end: 1.0).animate(
          CurvedAnimation(
            parent: animationController,
            curve: const Interval(
              // The second half of the animation only, using the given curve
              0.5,
              1.0,
              curve: Curves.easeOut,
            ),
          ),
        );
    
        _animate();
      }
    
      /// This function runs the animation from 0.0 - 1.0
      void _animate() {
        animationController.reset();
        animationController.forward();
      }
    
      @override
      void dispose() {
        animationController.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) => Scaffold(
            appBar: AppBar(),
            body: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                // Example
                // The ball should move to the right, then up, sharing the same curve
                Center(
                  child: SizedBox(
                    height: 200.0,
                    width: 200.0,
                    child: Align(
                      alignment: Alignment(
                        animation1.value,
                        -animation2.value,
                      ),
                      child: Container(
                        height: 10.0,
                        width: 10.0,
                        decoration: const BoxDecoration(
                          color: Colors.red,
                          shape: BoxShape.circle,
                        ),
                      ),
                    ),
                  ),
                ),
    
                // Reset button - you can re-run with these
                Center(
                  child: MaterialButton(
                    onPressed: _animate,
                    child: Text('reset'),
                  ),
                ),
              ],
            ),
          );
    }
    
    // If you're really hell-bent you can create your own curve by extending the
    //  Curve class and giving fine-grained control over how fast the curve reaches
    //  its terminus so that the curves would work as you want them to.
    
    /// A silly curve that races to the end point in the first third of the
    /// animation, returns to the start in the middle third of the animation, and
    /// then returns to the end point in the last third of the animation
    class LinearCurve extends Curve {
      // You can provide whatever arguments you want, like any other class.
    
      const LinearCurve();
    
      // Override the transform function to convert an input number from 0 - 1 to
      //  another number from 0 - 1
      @override
      double transform(double t) => t.clamp(0.0, 1.0);
    }
    

    Note it’s sort of a pain because Flutter doesn’t natively support what I think you’re trying to achieve of sharing a curve’s momentum across animation intervals.

    Without knowing your exact use case I can’t provide a great example that demonstrates the curve nicely; using the curve as I am here to move the same widget just in a different direction is a little silly, but I’m hoping maybe this is close enough to be useful to you?

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search