feat: add Picture-in-Picture support for iOS/Android video player - #432
Draft
akaboshinit wants to merge 18 commits into
Draft
feat: add Picture-in-Picture support for iOS/Android video player#432akaboshinit wants to merge 18 commits into
akaboshinit wants to merge 18 commits into
Conversation
Add PiP API to platform interface (enable/disable/start/stop/isSupported/isActive) and implement it in the AVFoundation plugin with native iOS AVPictureInPictureController. Includes event handling for pipStarted, pipStopped, and pipRestoreUserInterface.
- Add PiP API to video_player_android (start, stop, isSupported, isActive) - Add VideoPlayerCallbacks interface and VideoPlayerEventCallbacks for PiP events - Implement ActivityAware in VideoPlayerPlugin for Activity lifecycle management - Add PipRequestHandler pattern to track which player requested PiP - Add isPipActive to VideoPlayerValue in video_player package - Handle pipStarted/pipStopped events in VideoPlayerController - Update PiP method docs to reflect iOS and Android support - Add PiP controls UI to both example apps - Configure AndroidManifest.xml with FlutterFragmentActivity and PiP support - Upgrade Gradle/AGP for video_player_android example (Java 17 compatibility)
When PiP starts on iOS, the app transitions to the background and _VideoAppLifeCycleObserver fires a pause. This fix skips the pause when isPipActive is true, keeping the video playing in the PiP window. Also guards resumed state to avoid auto-play when returning from PiP.
Add setAutoPictureInPicture API across platform interface, Android, and iOS. - Android 12+ (API 31+): uses native setAutoEnterEnabled - Android 8-11 (API 26-30): Flutter-side fallback via AppLifecycleState - iOS 14.2+: uses canStartPictureInPictureAutomaticallyFromInline
… to declarative Gradle plugins
- iOS: Add weak self pattern in dispatch_async blocks to prevent crashes during dispose - iOS: Release pipRestoreCompletionHandler in tearDownPictureInPicture to prevent leaks - iOS: Wrap frame changes in CATransaction to suppress implicit animations - iOS: Synchronize hidden flag instead of async dispatch - iOS: Set explicit initial frame (CGRectZero) on pipPlayerLayer - iOS: Remove unused savedFrame variable - Android: Reset lastPipPlayerId on player dispose to prevent event delivery to deleted players - Android: Remove existing PiP listener before re-registering to prevent double registration on config changes - Dart: Clear pipSourceRectProvider in controller dispose
…example UI - Defer PiP controller setup to startPictureInPicture/setAutoPictureInPicture to avoid multiple AVPictureInPictureControllers causing isPictureInPicturePossible=NO - Add exclusive auto PiP on Android: enabling on one player disables others - Remove PiP-specific fullscreen UI from example app - Clean up imports and fix code formatting
restoreUserInterface で黒オーバーレイを追加し、Flutter テクスチャの 遷移アーティファクトを隠す。didStopPictureInPicture で 0.3s フェード アウトして削除。PiP ウィンドウはシステムウィンドウのため オーバーレイの上に正常に描画される。
Atsumi3
reviewed
Jul 15, 2026
There was a problem hiding this comment.
PiPのときにSceneDelegateとの兼ね合いが気になります!
コアでそもそもUIScreen.mainを使っている箇所があるんですが、それがDeprecatedにもなっております。
- startPictureInPicture / isPictureInPictureSupported / isPictureInPictureActive を全層 (Dart / Java / ObjC / pigeon / test) から削除 - pipRestoreUserInterface event / completePipRestoreWithSourceRect / PipSourceRectMessage / pipSourceRectProvider / _getSourceRect を削除 - onAutoPipChanged / onPipRestoreUserInterface コールバックを削除 - Android 8-11 の auto PiP fallback ロジックも削除
- AVPlayerLayer を hidden にして PiP window のコンテンツを空にし、 iOS 内部の縮小アニメを視覚的に消す - 黒オーバーレイで Flutter view の遷移アーティファクトを補助的に隠蔽 - didStopPictureInPicture で overlay をフェードアウト削除、layer を復元 - AVPlayerLayer.frame = CGRectZero で縮小先を無効化
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
VideoPlayerControllerからstartPictureInPicture()/stopPictureInPicture()で PiP を制御可能VideoPlayerValue.isPipActiveで PiP 状態を監視可能Changes
video_player_platform_interface
startPictureInPicture,stopPictureInPicture,isPictureInPictureSupported,isPictureInPictureActive) を追加VideoEventType.pipStarted,pipStopped,pipRestoreUserInterfaceを追加video_player_avfoundation (iOS)
video_player_android (Android)
ActivityAwareを実装し Activity ライフサイクルを管理PipRequestHandlerパターンで PiP 要求元プレーヤーを追跡VideoPlayerCallbacks/VideoPlayerEventCallbacksで PiP イベントを Dart 側に通知PipStatusMessageと 4 つの PiP API を追加video_player
VideoPlayerValueにisPipActiveフィールドを追加pipStarted/pipStoppedイベントでisPipActiveを自動更新Example apps
AndroidManifest.xmlにFlutterFragmentActivity+supportsPictureInPictureを設定Usage
Android 利用側の設定
AndroidManifest.xmlの Activity に以下を追加:Test plan
isPictureInPictureSupportedが正しい値を返すisPipActiveがリアルタイムで更新される