Blog Details

img
Development

Flutter – Physics Simulation in Animation

Spoke Right / 2 Nov, 2023

Physics simulation in Flutter is a beautiful way to Animate components of the flutter app to make it look more realistic and interactive. These can be used to create a range of animations like falling objects due to gravity to making a container seem attached to a spring. In this article, we will explore the same by building a simple application.

Follow the below steps to create a simple physics simulation in a Widget:

  • Develop an Animation controller.
  • Use gestures for movement.
  • Display the animation.
  • Use velocity to simulate the springing motion.

Let’s discuss them in detail:

Developing an Animation Controller:

To create the Animation controller make a StatefulWidget called DraggableCard as shown below:


import 'package:flutter/material.dart';


main() {

  runApp(MaterialApp(home: PhysicsCardDragDemo()));

}


class PhysicsCardDragDemo extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(),

      body: DraggableCard(

        child: FlutterLogo(

          size: 128,

        ),

      ),

    );

  }

}


class DraggableCard extends StatefulWidget {

  final Widget child;

  DraggableCard({this.child});


  @override

  _DraggableCardState createState() => _DraggableCardState();

}


class _DraggableCardState extends State {

  @override

  void initState() {

    super.initState();

  }


  @override

  void dispose() {

    super.dispose();

  }


  @override

  Widget build(BuildContext context) {

    return Align(

      child: Card(

        child: widget.child,

      ),

    );

  }

}

Using gestures for movement:

Here we will make the widget move when dragged in any direction. The movement can be mapped using a GestureDetector that handles onPanEndonPanUpdate, and onPanDown as shown below:

  • Dart




@override

Widget build(BuildContext context) {

  var size = MediaQuery.of(context).size;

  return GestureDetector(

    onPanDown: (details) {},

    onPanUpdate: (details) {

      setState(() {

        _dragAlignment += Alignment(

          details.delta.dx / (size.width / 2),

          details.delta.dy / (size.height / 2),

        );

      });

    },

    onPanEnd: (details) {},

    child: Align(

      alignment: _dragAlignment,

      child: Card(

        child: widget.child,

      ),

    ),

  );

}

Display Animation:

Use the Animation field and the _runAnimation method to produce a spring like spring-like effect as shown below to display the Animation:

  • Dart




Animation _animation;


void _runAnimation() {

  _animation = _controller.drive(

    AlignmentTween(

      begin: _dragAlignment,

      end: Alignment.center,

    ),

  );

 _controller.reset();

 _controller.forward();

}

Now, whenever the Animation Controller produces a value update the _dragAlignment field as shown below:

  • Dart




@override

void initState() {

  super.initState();

  _controller = AnimationController(vsync: this, duration: Duration(seconds: 1));

  _controller.addListener(() {

    setState(() {

      _dragAlignment = _animation.value;

    });

  });

}

Now, use the _dragAlignment field to Align the widget as shown below:

  • Dart




child: Align(

  alignment: _dragAlignment,

  child: Card(

    child: widget.child,

  ),

),

Finally manage the Animation using the GestureDetector as follows:

  • Dart




onPanDown: (details) {

 _controller.stop();

},

onPanUpdate: (details) {

 setState(() {

   _dragAlignment += Alignment(

     details.delta.dx / (size.width / 2),

     details.delta.dy / (size.height / 2),

   );

 });

},

onPanEnd: (details) {

 _runAnimation();

},

Using velocity to simulate the springing motion:

First, import the Physics package as below:

import 'package:flutter/physics.dart';

Now use the animateWith() method of the AnimationController to create a spring-like effect as shown below:

  • Dart




void _runAnimation(Offset pixelsPerSecond, Size size) {

  _animation = _controller.drive(

    AlignmentTween(

      begin: _dragAlignment,

      end: Alignment.center,

    ),

  );


  final unitsPerSecondX = pixelsPerSecond.dx / size.width;

  final unitsPerSecondY = pixelsPerSecond.dy / size.height;

  final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY);

  final unitVelocity = unitsPerSecond.distance;


  const spring = SpringDescription(

    mass: 30,

    stiffness: 1,

    damping: 1,

  );


  final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);


  _controller.animateWith(simulation);

}

Finally, make a call to _runAnimation() method with velocity and size as a parameter as shown below:

  • Dart




onPanEnd: (details) {

  _runAnimation(details.velocity.pixelsPerSecond, size);

},

Complete Source Code:

  • Dart




import 'package:flutter/material.dart';

import 'package:flutter/physics.dart';


main() {

  runApp(MaterialApp(home: PhysicsCardDragDemo()));

}


class PhysicsCardDragDemo extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: Text('GeeksForGeeks'),

        backgroundColor: Colors.green,

      ),

      body: DraggableCard(

        child: Container(

          width: 140,

          height: 140,

          decoration: BoxDecoration(

            color: Colors.green

          ),

        )

      ),

    );

  }

}


class DraggableCard extends StatefulWidget {

  final Widget child;

  DraggableCard({this.child});


  @override

  _DraggableCardState createState() => _DraggableCardState();

}


class _DraggableCardState extends State

    with SingleTickerProviderStateMixin {

  AnimationController _controller;



  Alignment _dragAlignment = Alignment.center;


  Animation _animation;


  void _runAnimation(Offset pixelsPerSecond, Size size) {

    _animation = _controller.drive(

      AlignmentTween(

        begin: _dragAlignment,

        end: Alignment.center,

      ),

    );

    // evaluating velocity

    final unitsPerSecondX = pixelsPerSecond.dx / size.width;

    final unitsPerSecondY = pixelsPerSecond.dy / size.height;

    final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY);

    final unitVelocity = unitsPerSecond.distance;


    const spring = SpringDescription(

      mass: 30,

      stiffness: 1,

      damping: 1,

    );


    final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);


    _controller.animateWith(simulation);

  }


  @override

  void initState() {

    super.initState();

    _controller = AnimationController(vsync: this);


    _controller.addListener(() {

      setState(() {

        _dragAlignment = _animation.value;

      });

    });

  }


  @override

  void dispose() {

    _controller.dispose();

    super.dispose();

  }


  @override

  Widget build(BuildContext context) {

    final size = MediaQuery.of(context).size;

    return GestureDetector(

      onPanDown: (details) {

        _controller.stop();

      },

      onPanUpdate: (details) {

        setState(() {

          _dragAlignment += Alignment(

            details.delta.dx / (size.width / 2),

            details.delta.dy / (size.height / 2),

          );

        });

      },

      onPanEnd: (details) {

        _runAnimation(details.velocity.pixelsPerSecond, size);

      },

      child: Align(

        alignment: _dragAlignment,

        child: Card(

          child: widget.child,

        ),

      ),

    );

  }

}

 

Output:

0 comments

Warning: PHP Startup: Unable to load dynamic library 'imagick.so' (tried: /usr/local/lib/php/extensions/no-debug-non-zts-20210902/imagick.so (/usr/local/lib/php/extensions/no-debug-non-zts-20210902/imagick.so: cannot open shared object file: No such file or directory), /usr/local/lib/php/extensions/no-debug-non-zts-20210902/imagick.so.so (/usr/local/lib/php/extensions/no-debug-non-zts-20210902/imagick.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0