An entering element is new information: users need a beat to notice it, so the motion can take its time. An exiting element is already dismissed in the user's head: every extra frame is waiting.
So the two directions must not share one animation. Enter slow, exit fast:
Material's spec encodes this directly: enter over 400ms on a decelerate curve, exit over 200ms on an accelerate curve. Flutter ships both:
In a route, the same split is transitionDuration and reverseTransitionDuration.
You don't have to use Material's tokens. The Curves class encodes the shape in the name: every easeOut* curve decelerates (starts fast, lands gently), and every easeIn* curve accelerates (starts gently, leaves fast). Yes, the names cross: In/Out is about where the motion is slow, not about entering the screen.
So any twin pair works: easeOutCubic in, easeInCubic out. The suffix is the strength: Quad, Cubic, Quart, Quint, each sharper than the last.
With springs, the difference isn't only time, it's bounce. An expressive spring overshoots: it goes past the target and swings back, and that bounce is what makes users notice the entrance. On an exit the same bounce pulls attention to something that's leaving. So exits use a standard spring: it stops at the target, no bounce.
There is no fixed pair to memorize here either. The rule is relative: whatever spring the entrance uses, exit on a faster one without bounce. With the motor package's Material springs, entering on expressiveSpatialDefault means exiting on standardSpatialFast:
// Enter slower, with bouncestatic const Motion enter = MaterialSpringMotion.expressiveSpatialDefault();// Exit faster, no bouncestatic const Motion exit = MaterialSpringMotion.standardSpatialFast();
Rolling your own SpringDescription? Same rule in raw numbers: raise the stiffness and keep the damping ratio at 1 or above. Stiffer is faster, and at full damping there is no bounce.