Custom Page Transition Animation Implementation in Mobile Apps
Standard slide-from-right on iOS and fade on Android are no longer perceived as special—this is the minimum. Problems arise when navigation must convey meaning: card expands into full-screen detail, photo from grid "flies" into gallery, settings screen slides up with parent content dimmed. Each requires a different approach.
Shared Element Transitions—Most Complex and Most Valuable
Transition where an element "flies" from one screen to another—not redrawn, but actually flies—is Shared Element Transition (iOS calls it Hero Animation).
On iOS, implement via custom UIViewControllerAnimatedTransitioning. Key: extract source element into containerView via snapshotView(afterScreenUpdates: false), hide original, animate snapshot along desired path, hide snapshot, show destination. Skip afterScreenUpdates: false and you get white rectangle instead of snapshot on first frames.
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
guard let snapshot = sourceView.snapshotView(afterScreenUpdates: false) else { return }
snapshot.frame = sourceFrame
containerView.addSubview(destinationVC.view)
containerView.addSubview(snapshot)
sourceView.isHidden = true
UIView.animate(withDuration: duration, delay: 0,
usingSpringWithDamping: 0.85, initialSpringVelocity: 0.3) {
snapshot.frame = destinationFrame
} completion: { _ in
snapshot.removeFromSuperview()
self.sourceView.isHidden = false
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
On Android, use SharedElementCallback + ActivityOptions.makeSceneTransitionAnimation or Jetpack Compose SharedTransitionLayout with Modifier.sharedElement. Compose approach is significantly simpler and needs no XML transition scenes.
In Flutter, Hero widget works for simple cases; flutter_animate or custom PageRouteBuilder for complex ones. Hero is limited: doesn't work correctly with ListView inside NestedScrollView, sometimes clips on transition—flightShuttleBuilder rescues.
Custom Transitions Without Shared Elements
For screens without common elements, implement custom PageRoute:
-
Expand from point—screen "grows" from tapped button via
ClipOval→ClipRRect→ full screen. Used for action buttons (FAB). -
Depth transition—current screen shrinks and recedes, new screen approaches. Achieved via
transform: Matrix4.translationValues+scale. -
Stagger reveal—new screen elements appear sequentially with delay via
AnimationStaggered. Works well for lists and dashboards.
Interactive (Interruptible) Transitions
Transition interruptible by back-swipe mid-way—this is UIPercentDrivenInteractiveTransition on iOS and BackHandler + AnimationController in Flutter. Important: animation must "bounce" back on incomplete gesture, not just stop. For this, completionCurve should be .easeOut, not .linear.
Process
Start with Figma prototype or transition description. Determine type: shared element, expand, custom route. Implement for target platform. Test interruptibility by gesture (if needed) and behavior in accessibility Reduce Motion mode—animations there should replace with simple fade.
Timeline: 1–3 days per transition, depends on complexity and screen count.







