1. 检测平台支持性
- Flutter:利用
flutter/services.dart
中的MethodChannel
来检测当前平台是否支持画中画模式。例如,在Android上可以通过调用MediaSession
相关API检测,iOS上通过AVPlayerViewController
的相关属性检测。
2. 选择视频播放库
- video_player:Flutter官方推荐库,能满足基本视频播放需求。但画中画功能需结合平台特定代码。
- chewie:基于
video_player
进行封装,提供更丰富UI组件。在画中画场景下,可利用其扩展功能进行定制。
3. Android实现
- 初始化:在
AndroidManifest.xml
中声明画中画权限和配置Activity
,如:
<uses-permission android:name="android.permission.PICTURE_IN_PICTURE" />
<activity
android:name=".MainActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboardHidden|layoutDirection|fontScale|density"
android:launchMode="singleTask"
android:resizeableActivity="true"
android:supportsPictureInPicture="true" />
- 代码实现:在Flutter的
Android
原生代码(MainActivity.kt
或MainActivity.java
)中,通过MediaSession
和PictureInPictureController
实现画中画。例如,在Kotlin
中:
import android.app.PictureInPictureParams
import android.content.Context
import android.content.Intent
import android.media.AudioAttributes
import android.media.MediaMetadata
import android.media.MediaSession
import android.os.Build
import android.os.Bundle
import android.util.Rational
import android.view.View
import android.widget.Toast
import androidx.annotation.RequiresApi
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
private lateinit var mediaSession: MediaSession
private lateinit var pictureInPictureController: PictureInPictureController
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mediaSession = MediaSession(this, "VideoPlayer")
mediaSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS or MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS)
mediaSession.setMediaButtonReceiver(null)
mediaSession.setMetadata(
MediaMetadata.Builder()
.putString(MediaMetadata.METADATA_KEY_TITLE, "Video Title")
.build()
)
mediaSession.setPlaybackToLocal(AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MOVIE)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build())
mediaSession.isActive = true
pictureInPictureController = mediaSession.pictureInPictureController
}
fun enterPictureInPictureMode(width: Int, height: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val aspectRatio = Rational(width, height)
val params = PictureInPictureParams.Builder()
.setAspectRatio(aspectRatio)
.build()
pictureInPictureController?.updatePictureInPictureParams(params)
enterPictureInPictureMode(params)
}
}
}
- Flutter调用:通过
MethodChannel
在Flutter中调用上述enterPictureInPictureMode
方法。
4. iOS实现
- 初始化:在
Info.plist
中添加UIRequiresFullScreen
为NO
,允许视频以非全屏模式播放。
- 代码实现:在
AppDelegate.m
或AppDelegate.swift
中,利用AVPlayerViewController
实现画中画。例如,在Swift
中:
import UIKit
import AVKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 配置AVPlayer相关设置
return true
}
func application(_ application: UIApplication, willEnterPictureInPictureModeWithOptions options: [String : Any]? = nil) -> Bool {
// 配置进入画中画前的操作
return true
}
func application(_ application: UIApplication, didEnterPictureInPictureMode: Bool) {
// 进入画中画后的操作
}
func application(_ application: UIApplication, willExitPictureInPictureModeWithOptions options: [String : Any]? = nil) -> Bool {
// 退出画中画前的操作
return true
}
func application(_ application: UIApplication, didExitPictureInPictureMode: Bool) {
// 退出画中画后的操作
}
}
- Flutter调用:同样通过
MethodChannel
在Flutter中与原生代码交互,控制画中画的进入与退出。
5. 兼容性与稳定性优化
- 版本适配:针对不同系统版本,分别处理画中画功能的初始化和操作。如在Android上,
Android 8.0 (API level 26)
及以上才支持画中画,在iOS上,iOS 9.0
及以上支持画中画。
- 设备适配:不同设备屏幕尺寸和分辨率不同,在进入画中画模式时,需根据设备屏幕信息调整视频尺寸和比例,保证画面显示正常。
- 错误处理:在视频播放和画中画切换过程中,增加错误捕获和处理机制。如网络异常、设备不支持等情况,给予用户友好提示。