fluttercommunity / fluttercommunity/plus_plugins
[share_plus] iOS: sourceView set unconditionally on iPhone — on iOS 26 swipe-dismissing the sheet can hang the share() Future and break touch delivery
- 主要言語
- Dart
- スター
- 1.9k
- フォーク
- 1.3k
- PR マージ指標
- 30日以内にマージされた PR はありません
説明
## Platform
- `share_plus`: 13.2.0 (same code present in 12.x)
- iOS 26.3 – 26.5.2, iPhone (reproduced on iPhone 16e / 17 Pro / 17 Pro Max, simulator and physical devices)
- Flutter 3.44.4
## Description
In `FPPSharePlusPlugin.m`, `popoverPresentationController.sourceView` is assigned **unconditionally**, regardless of `UIUserInterfaceIdiom`:
```objc
activityViewController.popoverPresentationController.sourceView = topViewController.view;
if (!CGRectIsEmpty(origin)) {
activityViewController.popoverPresentationController.sourceRect = origin;
}
```
There is no way to opt out from Dart: even when the caller passes no `sharePositionOrigin`, `sourceView` is still set.
On iPhone this used to be harmless, but on **iOS 26** a non-nil `sourceView` routes `UIActivityViewController` into the popover/anchored presentation path (compact sheet anchored to the Flutter view). When the user dismisses that sheet **interactively** (swipe down — most reliably after visiting a sub-screen such as AirDrop and coming back), the dismissal can end up in a broken state:
1. `completionWithItemsHandler` is **never called** → the `Future` returned by `SharePlus.share()` never completes, and any `await`/`finally` after it is dead.
2. The `UIActivityViewController` can stay `presented` with `isBeingDismissed == true` indefinitely (interrupted interactive dismissal never finishes), or stay presented with its view already detached from the window.
3. Touch delivery inside the app degrades: taps in part of the Flutter UI (in our case the bottom sheet under the share origin) stop being delivered until the presentation stack is rebuilt (e.g., by pushing/popping any route). Instrumentation shows events dying before reaching gesture handlers while the widget tree is healthy.
## Steps to reproduce
1. iPhone with iOS 26.x, any app calling `SharePlus.instance.share(ShareParams(uri: ...))` (no `sharePositionOrigin`).
2. Open the share sheet, optionally enter the AirDrop sub-screen and come back.
3. Dismiss the sheet with a swipe-down gesture.
4. Observe: the `share()` Future often never completes; in a fraction of runs taps around the share origin stop working.
The race is timing-sensitive (roughly 1 in 3 attempts in our testing), the AirDrop detour makes it much more reliable.
## Expected
- `sourceView`/popover configuration only applies on iPad (where `sharePositionOrigin` is genuinely required), or is at least skippable from Dart.
- `completionWithItemsHandler` fires (with `completed == false`) for interrupted interactive dismissals so the returned Future always completes.
## Workaround we ship (vendored fork)
Wrapping the popover configuration in an idiom check fixes the iPhone path while keeping iPad behavior intact:
```objc
BOOL isIpad = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad);
if (isIpad) {
activityViewController.popoverPresentationController.sourceView =
topViewController.view;
if (!CGRectIsEmpty(origin)) {
activityViewController.popoverPresentationController.sourceRect = origin;
}
}
```
The hung-completion part still needs an app-level watchdog on iOS 26 even with this patch (the OS sometimes drops the completion for interrupted dismissals), but the touch-delivery breakage no longer reproduces with `sourceView` unset on iPhone.
Happy to send a PR with the idiom guard if that direction works for the maintainers.
コントリビューションガイド
調査の方向性
FPPSharePlusPlugin.m の UIActivityViewController の popover 設定から調査を開始し、報告されている iPhone と iPad の動作を比較します。可能であれば iOS 26 でスワイプによる閉じる操作を再現し、その後、sourceView/sourceRect が iPad に対してのみ設定されていること、および既存の iPad の動作が維持されていることを確認します。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- dart, ios, objective-c
- 領域
- mobile-dev
- issue の種類
- バグ
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 活発さ
- 静か
- 明瞭さ
- 明確に書かれている
- 初心者へのやさしさ
- 78/100