Skip to content

feat: add Picture-in-Picture support for iOS/Android video player - #432

Draft
akaboshinit wants to merge 18 commits into
feat-set-max-bitratefrom
feat/picture-in-picture
Draft

feat: add Picture-in-Picture support for iOS/Android video player#432
akaboshinit wants to merge 18 commits into
feat-set-max-bitratefrom
feat/picture-in-picture

Conversation

@akaboshinit

@akaboshinit akaboshinit commented Feb 22, 2026

Copy link
Copy Markdown

Summary

  • iOS/Android の video_player に Picture-in-Picture (PiP) サポートを追加
  • VideoPlayerController から startPictureInPicture() / stopPictureInPicture() で PiP を制御可能
  • VideoPlayerValue.isPipActive で PiP 状態を監視可能

Changes

video_player_platform_interface

  • PiP 関連の API (startPictureInPicture, stopPictureInPicture, isPictureInPictureSupported, isPictureInPictureActive) を追加
  • VideoEventType.pipStarted, pipStopped, pipRestoreUserInterface を追加

video_player_avfoundation (iOS)

  • AVPictureInPictureController を使った PiP 実装
  • iOS 14.2+ 対応

video_player_android (Android)

  • ActivityAware を実装し Activity ライフサイクルを管理
  • PipRequestHandler パターンで PiP 要求元プレーヤーを追跡
  • VideoPlayerCallbacks / VideoPlayerEventCallbacks で PiP イベントを Dart 側に通知
  • Android 8.0+ (API 26+) 対応
  • Pigeon メッセージに PipStatusMessage と 4 つの PiP API を追加

video_player

  • VideoPlayerValueisPipActive フィールドを追加
  • pipStarted / pipStopped イベントで isPipActive を自動更新
  • PiP メソッドのドキュメントを iOS/Android 両対応に更新

Example apps

  • PiP 操作 UI (Start PiP / Stop PiP ボタン) を追加
  • PiP モード時にビデオのみ全画面表示に切り替え
  • AndroidManifest.xmlFlutterFragmentActivity + supportsPictureInPicture を設定

Usage

// PiP サポート確認
final supported = await controller.isPictureInPictureSupported();

// PiP 開始/停止
await controller.startPictureInPicture();
await controller.stopPictureInPicture();

// PiP 状態監視
controller.addListener(() {
  if (controller.value.isPipActive) {
    // PiP モード中の UI 処理
  }
});

Android 利用側の設定

AndroidManifest.xml の Activity に以下を追加:

android:name="io.flutter.embedding.android.FlutterFragmentActivity"
android:supportsPictureInPicture="true"
android:configChanges="...|smallestScreenSize|screenLayout"

Test plan

  • iOS で PiP 開始/停止が正常に動作する
  • Android で PiP 開始/停止が正常に動作する
  • PiP モード中にビデオのみ表示される
  • PiP 終了後に通常 UI に戻る
  • isPictureInPictureSupported が正しい値を返す
  • isPipActive がリアルタイムで更新される

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)
@akaboshinit akaboshinit changed the title Feat/picture-in-picture feat: add Picture-in-Picture support for iOS/Android video player Feb 26, 2026
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
@akaboshinit
akaboshinit changed the base branch from main to feat-set-max-bitrate March 23, 2026 07:52
- 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 ウィンドウはシステムウィンドウのため
オーバーレイの上に正常に描画される。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 で縮小先を無効化
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants