Bottom Sheet Expand Animation Implementation in Mobile Apps
A Bottom Sheet is one of those components where the difference between "works" and "feels right" is immediately noticeable. Spring-like expansion, tactile responsiveness to swipe, smooth background dimming—users feel this without conscious thought. Wooden animation or gesture interruption signals poor overall quality.
Where It Usually Breaks
The most common iOS issue: conflict between UIPanGestureRecognizer and scroll inside the sheet. User pulls content up, expects scroll, but sheet collapses instead. Standard UISheetPresentationController (iOS 15+) handles this via prefersGrabberVisible and fixed detents, but adding custom UIScrollView inside causes gestureRecognizerShouldBegin problems.
On Android, similar issue with BottomSheetBehavior from Material Components: with peekHeight equal to content height and hideable = true, sheet sometimes collapses on any downward swipe—because NestedScrollView doesn't pass events correctly when scrollY == 0.
In Flutter showModalBottomSheet gives minimal animation control. Real customization starts with DraggableScrollableSheet + AnimationController with SpringSimulation—only then does physical spring behavior emerge instead of linear ease-out.
How We Build It
On iOS, use UIViewPropertyAnimator with UISpringTimingParameters and custom UIPresentationController. This allows interrupting animation mid-gesture and smoothly changing speed without artifacts. Key: dampingRatio and initialVelocity must match current panGesture.velocity(in:) speed—otherwise sheet "bounces" on sudden release.
let velocity = panGesture.velocity(in: view)
let springParams = UISpringTimingParameters(
dampingRatio: 0.8,
initialVelocity: CGVector(dx: 0, dy: velocity.y / remainingDistance)
)
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: springParams)
On Android, work with BottomSheetBehavior + custom CoordinatorLayout.Behavior for non-standard behavior. For complex cases with multiple snap-points, use MotionLayout with ConstraintSet for each state. onSlide callback enables parallel scrim and content animation.
In Flutter, use DraggableScrollableSheet with DraggableScrollableController for programmatic control + HapticFeedback.lightImpact() at snap-points. For demanding cases, use modal_bottom_sheet package (woltapp), which implements native physics via Simulation.
Nuances Handled Separately
- Safe area handling: on Dynamic Island iPhones, sheet shouldn't cover Home indicator or extend under notch
- Keyboard: when
UIKeyboardappears, sheet shifts up with animation timed to system keyboard (UIKeyboardAnimationDurationUserInfoKey) - Haptic feedback:
UIImpactFeedbackGeneratoron iOS,VibrationEffect.createOneShoton Android—small detail users notice
Process
Start with existing component audit or requirements. If design exists in Figma with prototype, extract spring parameters (stiffness, damping) from Figma Spring Animation and port directly to code. Without design, calibrate parameters on interactive prototype before final implementation.
Testing includes slow animations mode (Simulator → Debug → Slow Animations) for smoothness check and XCTest for gesture regression.
Timeline: 1–2 days per platform, including integration into existing project and tests.







