PhysicsControllerMulti class

A controller for multi-dimensional physics-based animations that supports both standard curves and physics simulations.

Similar to PhysicsController, but works with multiple dimensions to control animations in N-dimensional space using lists of values. Key features include:

  • Accepts both standard Curves and PhysicsSimulations (like Spring)
  • Dynamically responds to changes mid-animation when using physics simulations
  • Maintains velocity across animation updates
  • Drop-in replacement for AnimationController in multi-dimensional contexts

Usage

Create a controller with a TickerProvider (usually from SingleTickerProviderStateMixin):

class _MyWidgetState extends State<MyWidget> with SingleTickerProviderStateMixin {
  late final _controller = PhysicsControllerMulti(
    dimensions: 3,  // For 3D space
    vsync: this,
    defaultPhysics: [
      Spring.elegant, // X-axis physics
      Spring.swift,   // Y-axis physics
      Spring.gentle,  // Z-axis physics
    ],
  );

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

Using with Standard Curves

When using standard curves, you must provide a duration either in the constructor or in animation methods:

// In constructor
final controller = PhysicsControllerMulti(
  dimensions: 2,
  vsync: this,
  duration: const Duration(milliseconds: 300),
  defaultPhysics: [
    Curves.easeOut,
    Curves.easeIn,
  ],
);

// Or in methods
controller.animateTo(
  [100.0, 200.0],
  duration: const Duration(milliseconds: 300),
  physics: [
    Curves.easeOut,
    Curves.easeIn,
  ],
);

Using with Physics Simulations

{@tool snippet} Physics simulations like Spring automatically calculate their duration and respond naturally to interruptions:

class PhysicsObject extends StatefulWidget {
  const PhysicsObject({super.key});

  @override
  State<PhysicsObject> createState() => _PhysicsObjectState();
}

class _PhysicsObjectState extends State<PhysicsObject>
    with SingleTickerProviderStateMixin {
  late final _controller = PhysicsControllerMulti.unbounded(
    dimensions: 3,
    vsync: this,
    defaultPhysics: [
      Spring.elegant,
      Spring.elegant,
      Spring.elegant,
    ],
  );

  List<double> _position = [0, 0, 0];

  void _onUpdate(List<double> delta) {
    for (int i = 0; i < _position.length; i++) {
      _position[i] += delta[i];
    }
    _controller.animateTo(_position);
  }

  void _onEnd(List<double> velocity) {
    // Physics simulation maintains momentum
    _controller.animateTo(
      List.filled(3, 0),
      velocityDelta: velocity,
    );
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) {
        return Transform.translate(
          offset: Offset(_controller.value[0], _controller.value[1]),
          child: Transform.translate(
            offset: Offset(0, _controller.value[2]),
            child: child,
          ),
        );
      },
      child: const Card(
        child: FlutterLogo(size: 128),
      ),
    );
  }
}

{@end-tool}

Responding to Changes Mid-Animation

Physics simulations maintain momentum when target values change:

// Initial animation
controller.animateTo([100, 100, 100]);

// Later, interrupt with new target
await Future.delayed(const Duration(milliseconds: 100));
controller.animateTo([50, 50, 50]); // Maintains current velocity

Common Use Cases

  • Multi-dimensional animations (3D, 4D, etc.)
  • Complex physics simulations
  • Particle systems
  • Data visualization animations

This controller can be used anywhere AnimationController is accepted, with the values accessible as a list.

See also:

Inheritance
Mixed-in types

Constructors

PhysicsControllerMulti.new({required int dimensions, List<double>? value, String? debugLabel, required TickerProvider vsync, AnimationBehavior animationBehavior = AnimationBehavior.normal, List<Physics>? defaultPhysics, Physics? defaultPhysicsForAllDimensions, List<double>? lowerBound, List<double>? upperBound, Duration? duration, Duration? reverseDuration})
PhysicsControllerMulti.unbounded({required int dimensions, List<double>? value, String? debugLabel, required TickerProvider vsync, AnimationBehavior animationBehavior = AnimationBehavior.normal, List<Physics>? defaultPhysics, Physics? defaultPhysicsForAllDimensions, List<double>? lowerBound, List<double>? upperBound, Duration? duration, Duration? reverseDuration})
Creates an unbounded 2D physics-based animation controller.

Properties

animationBehavior AnimationBehavior
The behavior of the controller when animations are disabled via AccessibilityFeatures.disableAnimations.
final
debugLabel String?
A label that is used in the toString output. Useful for debugging.
final
defaultPhysics List<Physics>
Sets the default physics simulation for each dimension.
getter/setter pair
dimensions int
The number of dimensions this controller animates. For example, 3 for 3D space, 4 for 4D space, etc.
final
duration Duration?
The duration of the animation when using non-physics-based animations.
getter/setter pair
hashCode int
The hash code for this object.
no setterinherited
isAnimating bool
Whether this animation is currently running in either direction.
no setteroverride
isCompleted bool
Whether this animation is stopped at the end.
no setterinherited
isDismissed bool
Whether this animation is stopped at the beginning.
no setterinherited
isForwardOrCompleted bool
Whether the current aim of the animation is toward completion.
no setterinherited
lastElapsedDuration Duration?
The time elapsed since the animation started.
no setter
lowerBound UnmodifiableListView<double>
The lower bounds for each dimension.
final
reverseDuration Duration?
The duration of the animation when reversing with non-physics-based animations.
getter/setter pair
runtimeType Type
A representation of the runtime type of the object.
no setterinherited
status AnimationStatus
The current status of this animation.
no setteroverride
upperBound UnmodifiableListView<double>
The upper bounds for each dimension.
final
value UnmodifiableListView<double>
The current position of the animation in N-dimensional space.
getter/setter pairoverride-getter
velocity UnmodifiableListView<double>
The current velocity of the animation in points per second.
no setter

Methods

addListener(VoidCallback listener) → void
Calls the listener every time the value of the animation changes.
inherited
addStatusListener(AnimationStatusListener listener) → void
Calls listener every time the status of the animation changes.
inherited
animateTo(List<double> target, {Duration? duration, List<Physics>? physics, List<double>? velocityDelta, List<double>? velocityOverride}) TickerFuture
Drives the animation from its current position to target.
animateWith(List<Simulation> simulations) TickerFuture
Drives the animation according to the given 2D simulation.
clearListeners() → void
Removes all listeners added with addListener.
inherited
clearStatusListeners() → void
Removes all listeners added with addStatusListener.
inherited
didRegisterListener() → void
This implementation ignores listener registrations.
inherited
didUnregisterListener() → void
This implementation ignores listener registrations.
inherited
dimension(int dimension) Animation<double>
Returns an Animation<double> that tracks a single dimension of this controller.
dispose() → void
Release the resources used by this object.
override
drive<U>(Animatable<U> child) Animation<U>
Chains a Tween (or CurveTween) to this Animation.
inherited
forward({List<double>? from}) TickerFuture
Starts the animation from the current position (or from) to upperBound.
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
notifyListeners() → void
Calls all the listeners.
inherited
notifyStatusListeners(AnimationStatus status) → void
Calls all the status listeners.
inherited
removeListener(VoidCallback listener) → void
Stop calling the listener every time the value of the animation changes.
inherited
removeStatusListener(AnimationStatusListener listener) → void
Stops calling the listener every time the status of the animation changes.
inherited
repeat({List<double>? min, List<double>? max, bool reverse = false, int? count, List<Physics>? physics}) TickerFuture
Repeats the animation between min and max for count times or indefinitely.
reset() → void
Resets the controller's value to lowerBound, stopping the animation.
resync(TickerProvider vsync) → void
Recreates the Ticker with the new TickerProvider.
reverse({List<double>? from}) TickerFuture
Starts the animation from the current position (or from) to lowerBound.
stop({bool canceled = true}) UnmodifiableListView<double>
Stops the current animation and returns the velocity at the moment of stopping.
toString() String
A string representation of this object.
inherited
toStringDetails() String
Provides a string describing the status of this object, but not including information about the object itself.
override

Operators

operator ==(Object other) bool
The equality operator.
inherited