Search code examples
flutterdartflutter-animationmobile-development

how to use flutter curve value from high to low


hi guys I just started learning flutter animation and using flutter curves from flutter curve website using tween value which goes like

    _animation = Tween<double>(begin: -100, end: 0).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Curves.bounceInOut,
      ),
    );)

i want to animate this in stack with the circular container with positioned widget using Animated builder code is likke this

Stack(
        children: [
// background image view
          Container(
            decoration: const BoxDecoration(
              image: DecorationImage(
                  image: AssetImage('images/sky.webp'), fit: BoxFit.cover),
            ),
          ),
//! I want to animate this like a bouncing ball 
          AnimatedBuilder(
            animation: _controller,
            builder: (context, child) {
              return Positioned(
// This is the animation value as position of the circular container
                bottom: _animation.value,
                left: _animation.value + 50,
                child: Container(
                  height: 250,
                  width: 250,
                  decoration: const BoxDecoration(
                    shape: BoxShape.circle,
                    // borderRadius: BorderRadius.only(
                    //   topLeft: Radius.circular(15),
                    //   topRight: Radius.circular(15),
                    // ),
                    image: DecorationImage(
                      image: AssetImage(
                        'images/colz.jpg',
                      ),
                      fit: BoxFit.cover,
                    ),
                  ),
                ),
              );
            },
          )
        ],
      ),

I want bounce direction as

from here start curve for bounceinout


--^
^---- > here the curve ends this what happenens after the code

enter image description here

how i wanted the curve movement with animationsenter image description here


Solution

  • I think you may be better served by having two different animations that share the same controller to drive them. The controller to adjust the horizontal direction could be a different curve than the one that adjusts the horizontal direction.

    I've modified your original code to do this, as well as added an example for how you might go about making your own curve that could give you that springiness that you want. Note that the demo curves I have at the bottom are very rough and can be janky if not paired up with linear primary curves. You could make them smoother by possibly averaging the starting and ending points of each curve - I will leave that up to you to modify.

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return const MaterialApp(
          home: MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      const MyHomePage({super.key});
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage>
        with SingleTickerProviderStateMixin {
      late AnimationController _controller;
      late Animation<double> _horizontalAnimation;
      late Animation<double> _verticalAnimation;
    
      @override
      void initState() {
        _controller = AnimationController(
          vsync: this,
          duration: const Duration(
            seconds: 5,
          ),
        );
        _horizontalAnimation = Tween<double>(begin: -1.0, end: 1.0).animate(
          CurvedAnimation(
            parent: _controller,
            // curve: Curves.bounceInOut,
            // TODO: Try to experiment with this a bit too!
            curve: const InterpolateCurve(
              primary: Curves.linear,
              secondary: Curves.bounceInOut,
            ),
          ),
        );
        _verticalAnimation = Tween<double>(begin: -1.0, end: 1.0).animate(
          CurvedAnimation(
            parent: _controller,
            curve: Curves.linear,
            // TODO: You may even be able to experiment with this a bit
            // curve: const InterpolateCurve(
            //   primary: Curves.linear,
            //   secondary: WhipCurve(Curves.easeInOut),
            //   startTransition: 0.2,
            //   endTransition: 0.8,
            // ),
          ),
        );
        super.initState();
        _controller.forward();
      }
    
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Drag and Drop Example'),
          ),
          body: Stack(
            children: [
    // background image view
              Container(
                decoration: const BoxDecoration(
                  image: DecorationImage(
                      image: NetworkImage(
                          'https://hdwallpaperim.com/wp-content/uploads/2017/08/25/462707-universe-galaxy-space-stars.jpg'),
                      fit: BoxFit.cover),
                ),
              ),
    //! I want to animate this like a bouncing ball
              AnimatedBuilder(
                animation: _controller,
                builder: (context, child) {
                  return Align(
                    alignment: Alignment(
                        _horizontalAnimation.value, _verticalAnimation.value),
    // This is the animation value as position of the circular container
    
                    child: Container(
                      height: 250,
                      width: 250,
                      decoration: BoxDecoration(
                        shape: BoxShape.circle,
                        // borderRadius: BorderRadius.only(
                        //   topLeft: Radius.circular(15),
                        //   topRight: Radius.circular(15),
                        // ),
                        // image: DecorationImage(
                        //   image: AssetImage(
                        //     'images/colz.jpg',
                        //   ),
                        //   fit: BoxFit.cover,
                        // ),
                        color: Colors.indigo.shade900,
                      ),
                    ),
                  );
                },
              )
            ],
          ),
        );
      }
    }
    
    // Try this too:
    class InterpolateCurve extends Curve {
      final Curve primary;
      final Curve secondary;
      final double startTransition;
      final double endTransition;
    
      const InterpolateCurve({
        required this.primary,
        required this.secondary,
        this.startTransition = 0.4,
        this.endTransition = 0.6,
      });
    
      @override
      double transform(double t) {
        t = t.clamp(0.0, 1.0);
    
        // We want to squiggle in the middle a bit so...
        if (t <= startTransition || t >= endTransition) {
          return primary.transform(t);
        }
    
        final double centerPeriod = endTransition - startTransition;
        final double centerSegementScalar = 1.0 / centerPeriod;
        t = (t - startTransition) * centerSegementScalar;
        t = t.clamp(0.0, 1.0);
        t = secondary.transform(t);
        t = (t / centerSegementScalar) + startTransition;
        return t;
      }
    }
    
    class WhipCurve extends Curve {
      final Curve curve;
    
      const WhipCurve(
        this.curve,
      );
    
      @override
      double transform(double t) {
        t = t.clamp(0.0, 1.0);
        if (t <= 1 / 3) {
          return curve.transform(t * 3);
        }
        if (t >= 2 / 3) {
          t = t - (2 / 3);
          return curve.transform(t * 3);
        } else {
          t = (t - (1 / 3));
          return 1.0 - curve.transform(t * 3);
        }
      }
    }
    

    Also note that I swapped your Positioned widget for an Align widget; I think Align may be what you want- Positioned in order to get it to move across the screen would require some MediaQuery.of(context).size calls to properly scale it to the screen, whereas Align will just keep it on the screen.

    And I obviously didn't have your images, so you'd need to add those back.