[PR #25425] fix(mobile): bottom sheet drag gesture jump #18204

Open
opened 2026-02-05 16:34:27 +03:00 by OVERLORD · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/immich-app/immich/pull/25425
Author: @ByteSizedMarius
Created: 1/21/2026
Status: 🔄 Open

Base: mainHead: fix/mobile-bottom-sheet-drag-gestures


📝 Commits (3)

  • 71553ea fix(mobile): bottom sheet drag and close behavior
  • ae02792 Merge branch 'immich-app:main' into fix/mobile-bottom-sheet-drag-gestures
  • 48791c8 fix(mobile): remove animate-to-center

📊 Changes

1 file changed (+21 additions, -9 deletions)

View changed files

📝 mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart (+21 -9)

📄 Description

Description

Issue 1: Sheet Jumps Up Before Closing

When opening and closing the sheet in a single gesture (drag up, then back down without releasing), the sheet would jump UP to 67% before animating closed - even if below that point. This only happened in one continuous gesture because when the user releases first, the sheet animates up to 67% (the snap point), so it's already at 67% before any close attempt and the jump isn't visible.

I attached a before/after video in the spoiler. Before is slowed to 1/4 so you can see the jump/flicker better.

Fix: Only jump if currently above 67%:

if (bottomSheetController.isAttached && bottomSheetController.size > _kBottomSheetSnapExtent) {
  bottomSheetController.jumpTo(_kBottomSheetSnapExtent);
}

Issue 2: Accidental Closes

Any 1px downward movement triggered a close when below the 67% threshold - whether from a still position or while dragging. This was especially noticeable when dragging up slowly, because your finger naturally shifts around while touching the screen, and if that shift registered as 1px lower than the current position, it counted as "dragging down" and closed the sheet immediately.

Fix: Add a 2% threshold so only intentional movement counts:

const double closeThreshold = 0.02;
final isDraggingDown = previousExtent - currentExtent > closeThreshold;

This caused another small issue, as with the threshold in place, slow dragging downwards never exceeded 2% per notification, so isDraggingDown was never true. The sheet would shrink but never actually close.

Fix for that: Also close when reaching minimum extent (40%), regardless of drag speed:

final shouldClose = (isDraggingDown && delta.extent < _kBottomSheetSnapExtent) ||
    delta.extent <= _kBottomSheetMinimumExtent;

This makes the interaction feel much better IMO; before, the sheet closing on a 1px jitter didn't feel intentional at all. Now it only closes when you've either made a deliberate swipe or dragged it all the way down, so it always feels intentional

Follow-up Fix: Image Jumps Back When Sheet Closes During Drag

This edgecase only appeared after the first two changes. Before, any 1px movement triggered instant close - the finger could still be on screen, but the close happened so immediately that it didn't matter. After the previous changes, slow dragging became possible: now the bottomsheet only closes after gradually dragging the sheet down to 40% with a finger still on screen. This created a new scenario where the close animation runs while the user still has the finger on the screen.

When this happened, the image (behind the sheet) animated down (back to the center of the screen) then sometimes jumped back up if the user hadn't let go of the drag yet. I believe the animateMultiple in _handleSheetClose conflicted with the active drag gesture.

Fix: Defer animation until drag ends:

// _handleSheetClose:
if (!dragInProgress) {
  viewController?.animateMultiple(position: Offset.zero);
}

// _onDragEnd:
if (blockGestures) {
  blockGestures = false;
  viewController?.animateMultiple(position: Offset.zero);
  return;
}

Follow-up Fix: Sheet Grows When Dragging Past Origin

Also only possible after prev. changes; before, the sheet would close immediately when dragging down, so you couldn't drag past the origin.

When dragging up to open the sheet, then back down past the starting point (without releasing), the sheet grew again instead of staying small. Sheet extent was calculated using position.distance which is absolute, so distance increased whether you dragged up OR down past origin.

Fix: Only use upward movement (negative Y) for extent:

final distanceToOrigin = position.dy < 0 ? position.distance : 0.0;

How Has This Been Tested?

I installed the app on my phone and did all kinds of dragging. Slow, fast, without releasing, with releasing, etc.

Screenshots (if appropriate)

Before:
https://github.com/user-attachments/assets/b5471f3f-9a1f-4a6c-acf8-bc4f04020b96

After:
https://github.com/user-attachments/assets/f3081e58-26eb-4e1e-b340-c468ce27b0e0

Checklist:

  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation if applicable
  • I have no unrelated changes in the PR.
  • I have confirmed that any new dependencies are strictly necessary.
  • I have written tests for new code (if applicable)
  • I have followed naming conventions/patterns in the surrounding code
  • [NA] All code in src/services/ uses repositories implementations for database calls, filesystem operations, etc.
  • [NA] All code in src/repositories/ is pretty basic/simple and does not have any immich specific logic (that belongs in src/services/)

Please describe to which degree, if any, an LLM was used in creating this pull request.

Claude was used during debugging the mentioned behaviors


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/immich-app/immich/pull/25425 **Author:** [@ByteSizedMarius](https://github.com/ByteSizedMarius) **Created:** 1/21/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `fix/mobile-bottom-sheet-drag-gestures` --- ### 📝 Commits (3) - [`71553ea`](https://github.com/immich-app/immich/commit/71553ea5eae4beedcc084286eefd940b1416ac1a) fix(mobile): bottom sheet drag and close behavior - [`ae02792`](https://github.com/immich-app/immich/commit/ae02792e1f44991712fc5ceacbdf60eb3949a29d) Merge branch 'immich-app:main' into fix/mobile-bottom-sheet-drag-gestures - [`48791c8`](https://github.com/immich-app/immich/commit/48791c8d11fcbdac98cb89e55a0831c6c2890388) fix(mobile): remove animate-to-center ### 📊 Changes **1 file changed** (+21 additions, -9 deletions) <details> <summary>View changed files</summary> 📝 `mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart` (+21 -9) </details> ### 📄 Description ## Description **Issue 1: Sheet Jumps Up Before Closing** When opening and closing the sheet in a single gesture (drag up, then back down without releasing), the sheet would jump UP to 67% before animating closed - even if below that point. This only happened in one continuous gesture because when the user releases first, the sheet animates up to 67% (the snap point), so it's already at 67% before any close attempt and the jump isn't visible. I attached a before/after video in the spoiler. Before is slowed to 1/4 so you can see the jump/flicker better. Fix: Only jump if currently above 67%: ```dart if (bottomSheetController.isAttached && bottomSheetController.size > _kBottomSheetSnapExtent) { bottomSheetController.jumpTo(_kBottomSheetSnapExtent); } ``` **Issue 2: Accidental Closes** Any 1px downward movement triggered a close when below the 67% threshold - whether from a still position or while dragging. This was especially noticeable when dragging up slowly, because your finger naturally shifts around while touching the screen, and if that shift registered as 1px lower than the current position, it counted as "dragging down" and closed the sheet immediately. Fix: Add a 2% threshold so only intentional movement counts: ```dart const double closeThreshold = 0.02; final isDraggingDown = previousExtent - currentExtent > closeThreshold; ``` This caused another small issue, as with the threshold in place, slow dragging downwards never exceeded 2% per notification, so `isDraggingDown` was never true. The sheet would shrink but never actually close. Fix for that: Also close when reaching minimum extent (40%), regardless of drag speed: ```dart final shouldClose = (isDraggingDown && delta.extent < _kBottomSheetSnapExtent) || delta.extent <= _kBottomSheetMinimumExtent; ``` This makes the interaction feel much better IMO; before, the sheet closing on a 1px jitter didn't feel intentional at all. Now it only closes when you've either made a deliberate swipe or dragged it all the way down, so it always feels intentional **Follow-up Fix: Image Jumps Back When Sheet Closes During Drag** This edgecase only appeared after the first two changes. Before, any 1px movement triggered instant close - the finger could still be on screen, but the close happened so immediately that it didn't matter. After the previous changes, slow dragging became possible: now the bottomsheet only closes after gradually dragging the sheet down to 40% with a finger still on screen. This created a new scenario where the close animation runs while the user still has the finger on the screen. When this happened, the image (behind the sheet) animated down (back to the center of the screen) then sometimes jumped back up if the user hadn't let go of the drag yet. I believe the `animateMultiple` in `_handleSheetClose` conflicted with the active drag gesture. Fix: Defer animation until drag ends: ```dart // _handleSheetClose: if (!dragInProgress) { viewController?.animateMultiple(position: Offset.zero); } // _onDragEnd: if (blockGestures) { blockGestures = false; viewController?.animateMultiple(position: Offset.zero); return; } ``` **Follow-up Fix: Sheet Grows When Dragging Past Origin** Also only possible after prev. changes; before, the sheet would close immediately when dragging down, so you couldn't drag past the origin. When dragging up to open the sheet, then back down past the starting point (without releasing), the sheet grew again instead of staying small. Sheet extent was calculated using `position.distance` which is absolute, so distance increased whether you dragged up OR down past origin. Fix: Only use upward movement (negative Y) for extent: ```dart final distanceToOrigin = position.dy < 0 ? position.distance : 0.0; ``` ## How Has This Been Tested? I installed the app on my phone and did all kinds of dragging. Slow, fast, without releasing, with releasing, etc. <details><summary><h2>Screenshots (if appropriate)</h2></summary> Before: https://github.com/user-attachments/assets/b5471f3f-9a1f-4a6c-acf8-bc4f04020b96 After: https://github.com/user-attachments/assets/f3081e58-26eb-4e1e-b340-c468ce27b0e0 </details> ## Checklist: - [x] I have performed a self-review of my own code - [x] I have made corresponding changes to the documentation if applicable - [x] I have no unrelated changes in the PR. - [x] I have confirmed that any new dependencies are strictly necessary. - [x] I have written tests for new code (if applicable) - [x] I have followed naming conventions/patterns in the surrounding code - [NA] All code in `src/services/` uses repositories implementations for database calls, filesystem operations, etc. - [NA] All code in `src/repositories/` is pretty basic/simple and does not have any immich specific logic (that belongs in `src/services/`) ## Please describe to which degree, if any, an LLM was used in creating this pull request. Claude was used during debugging the mentioned behaviors --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
OVERLORD added the pull-request label 2026-02-05 16:34:27 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: immich-app/immich#18204