In Flutter, how do SpringDescription's default constructor, withDampingRatio and withDurationAndBounce differ, and what does each parameter do to the motion?
answer
- three constants of a damped spring
- coefficient is not ratio
- ratio 1.0 means critically damped
- duration 500 ms, bounce 0 defaults
- bounce 0.3 gives ratio 0.7
basics
~20 sThe const constructor takes mass, stiffness and damping coefficient; withDampingRatio derives damping from a ratio defaulting to 1.0; withDurationAndBounce (Flutter 3.32+) derives everything from a perceptual duration and a bounce, with mass fixed at 1.
solid answer
~30 s`SpringDescription(mass:, stiffness:, damping:)` takes the raw physics: heavier mass swings wider and returns slower, higher stiffness pulls harder, and the damping coefficient bleeds energy. `withDampingRatio(mass:, stiffness:, ratio:)` computes `damping = ratio * 2 * sqrt(mass * stiffness)`; the ratio defaults to 1.0, critically damped, below 1 bounces and above 1 creeps. `withDurationAndBounce(duration:, bounce:)`, added in Flutter 3.32, fixes mass at 1, derives stiffness from the duration (default 500 ms) and maps bounce (default 0) to a ratio of `1 - bounce` for positive values. The duration is perceptual, not a guaranteed stop time; `SpringSimulation.isDone` decides when motion ends.
code
dart · 27 linesimport 'package:flutter/physics.dart';
// Physical constants, as the engine uses them.
const SpringDescription raw = SpringDescription(
mass: 1,
stiffness: 300,
damping: 20,
);
// Same stiffness, damping derived from a ratio: 0.6 bounces a little.
final SpringDescription byRatio = SpringDescription.withDampingRatio(
mass: 1,
stiffness: 300,
ratio: 0.6,
);
// Designer-facing: perceptual pace and bounciness (Flutter 3.32+).
final SpringDescription byFeel = SpringDescription.withDurationAndBounce(
duration: const Duration(milliseconds: 350),
bounce: 0.2,
);
void main() {
final SpringSimulation sim = SpringSimulation(byFeel, 0, 1, 0);
print(sim.type); // SpringType.underDamped
print(byFeel.bounce.toStringAsFixed(2)); // 0.20
}go deeper
Know that a spring is described by mass, stiffness and damping, and that a damping ratio of 1 means no overshoot.
Explain the three constructors, their defaults, the ratio formula and how bounce maps to a ratio, and why duration here is perceptual rather than exact.
Pick constructors deliberately, keep designer specs readable with duration and bounce, and flag the Flutter 3.32 underdamped-formula change when upgrading apps with custom springs.
Decide how spring specs flow from design to code across an app, for example a shared set of named descriptions, so motion stays consistent without every screen tuning raw physics.
## What a `SpringDescription` is A **`SpringDescription`** holds the three constants of a damped spring. A `SpringSimulation` combines it with a start position, an end (rest) position and an initial velocity, and solves the spring equation for any time. Flutter offers three ways to build one, from most physical to most designer-friendly. | Constructor | You pass | Derived | Defaults | |---|---|---|---| | `SpringDescription(...)` (const) | `mass`, `stiffness`, `damping` | nothing | all three required | | `SpringDescription.withDampingRatio(...)` | `mass`, `stiffness`, `ratio` | `damping = ratio * 2 * sqrt(mass * stiffness)` | `ratio` = 1.0 | | `SpringDescription.withDurationAndBounce(...)` (factory) | `duration`, `bounce` | mass fixed at 1, stiffness and damping computed | `duration` = 500 ms, `bounce` = 0.0 | ## The three physical parameters - **`mass`** — how heavy the attached object is. More mass means larger swings and a slower return to rest. - **`stiffness`** — the spring constant *k*. A stiffer spring pulls harder for the same displacement, so motion is faster. - **`damping`** — the damping *coefficient* *c*, the friction that bleeds energy away. Do not confuse it with the damping *ratio* ζ; the source doc comment warns about exactly that mix-up. The **damping ratio** is the unitless number that decides the character of the motion: 1. **ζ = 1, critically damped** — returns to rest as fast as possible without crossing it. This is the default for `withDampingRatio`. 2. **ζ < 1, underdamped** — overshoots and oscillates, each swing smaller. 3. **ζ > 1, overdamped** — creeps to rest without crossing, more slowly than critical. `SpringSimulation.type` reports which of these a given description produced as a `SpringType` value: `criticallyDamped`, `underDamped` or `overDamped`. It exists for debugging and assertions. ## `withDurationAndBounce` Added in **Flutter 3.32**, `withDurationAndBounce` lets you describe a spring by how it looks rather than by physics. Its doc comment states it produces the same result as SwiftUI's `spring(duration:bounce:blendDuration:)`. - **`duration`** is a *perceptual* duration: roughly the settle time for a calm spring, closer to the oscillation period for a bouncy one. It is **not** a hard stop — with `bounce: 1` the spring never settles. Internally, `stiffness = 4π² · mass / duration²` with mass 1. - **`bounce`** maps to a damping ratio: for `bounce > 0` the ratio is `1 - bounce` (so 0.3 gives ζ = 0.7); `bounce` 0 gives ζ = 1; a negative bounce gives `1 / (bounce + 1)`, an overdamped spring. - The instance also exposes `duration` and `bounce` getters that work back from any description, which helps when translating a raw spring into designer language. ## Choosing one in practice - Reach for **`withDurationAndBounce`** when a designer hands you "about 350 ms, a little bounce" — it keeps the spec readable in code review. - Use **`withDampingRatio`** when you need a guaranteed type: ratio 1.0 for motion that must never cross its target (a card sliding under a toolbar), ratio below 1 for a playful settle. - Use the **raw constructor** when porting constants from another physics setup or matching an existing value exactly. - Whatever you pick, `AnimationController.fling` accepts only critically damped or overdamped descriptions; an underdamped one belongs in `animateWith` with an explicit `SpringSimulation`. ## Version note Flutter 3.32 also corrected the formula for **underdamped** springs whose mass is not 1. Before the fix, damping ratios of 0.9999 and 1.0001 produced visibly different motion. Springs built with ratio below 1 and mass other than 1 bounce differently after upgrading, and the breaking-change page gives a conversion (set mass to 1 and rescale stiffness and damping) to restore the old feel. Springs with mass 1, such as every `withDurationAndBounce` spring, are unaffected.
- How do you tell whether a SpringDescription will bounce without running it?Compute the damping ratio `damping / (2 * sqrt(mass * stiffness))`: below 1 bounces, 1 is critical, above 1 creeps. At runtime, `SpringSimulation(...).type` returns `SpringType.underDamped`, `criticallyDamped` or `overDamped`, and the description's `bounce` getter gives the same answer in designer terms.
- Does withDurationAndBounce(duration: 300 ms) guarantee the animation ends after 300 ms?No. The duration is perceptual: close to the settle time for calm springs and to the oscillation period for bouncy ones. With `bounce: 1` the spring never settles at all. The controller stops when `SpringSimulation.isDone` finds position and velocity within tolerance of rest.
saying these in an interview costs you the question
- damping and damping ratio are the same number with different names.
- withDampingRatio defaults to a bouncy ratio of 0.5.
- withDurationAndBounce stops the spring exactly at the given duration.
- A higher bounce value makes the spring more heavily damped.
- Increasing mass makes a spring settle faster.