Flutter + iOS 实战指南:教程视频 PiP + 退桌面(可复用模板)

这篇文章解决什么问题

目标不是"讲原理",而是给你一套以后能直接复用的方案,用来快速实现:

  1. Flutter 页面点击触发原生能力。
  2. iOS 启动隐藏视频播放器并进入 PiP。
  3. PiP 启动后主动退到桌面。
  4. App 回前台时,立即关闭 PiP,而不是只暂停。

这套方案适合沉淀成团队模板,也适合直接喂给 AI,让它一次产出高质量代码。


适用场景

  1. 引导用户添加小组件的教程悬浮窗。
  2. 退到桌面后继续播放短视频教学。
  3. 仅 iOS 需要

最小实现蓝图

调用链路固定为:

Flutter 点击事件

-> NativeWidgetUtil

-> Pigeon HostApi

-> iOS PiP Manager(AVPlayer + AVPictureInPictureController)

-> suspend 退桌面

建议文件分层:

  1. pigeon/native_widget_service.dart 定义接口。
  2. lib/pigeon/native_widget_service.dart / native_widget_util.dart Dart 调用封装。
  3. ios/Runner/pigeon/NativeWidgetAPI.swift 放完整 PiP 管理器。
  4. Android/OHOS 侧实现 no-op。

关键实现步骤

1) Pigeon 新增统一入口

dart 复制代码
@HostApi()
abstract class NativeWidgetService {
  void startWidgetTutorialPipAndExitDebug(String assetPath, bool loop, bool muted);
}

执行生成(按你项目脚本):

  1. ./pigeon.sh

2) Flutter 页面入口

页面标题点击触发:

dart 复制代码
void onTapTitle() {
  if (!GetPlatform.isIOS) {
    MyToastUtil.showToast('该测试入口仅 iOS 可用');
    return;
  }
  unawaited(_startWidgetTutorialPipAndExitDebug());
}

Future<void> _startWidgetTutorialPipAndExitDebug() async {
  try {
    await NativeWidgetUtil.startWidgetTutorialPipAndExitDebug(
      assetPath: 'assets/audios/widget_add_tutorial.mp4',
      loop: true,
      muted: false,
    );
  } catch (e) {
    printLog('启动小组件教程画中画失败: $e');
    MyToastUtil.showToast('画中画启动失败,请稍后重试');
  }
}

3) iOS PiP Manager 必做项

一个类统一管理以下对象,避免散点逻辑:

  1. AVPlayer
  2. AVPlayerLayer
  3. AVPictureInPictureController
  4. 前台通知监听
  5. cleanup 生命周期

启动流程关键点:

  1. 校验 AVPictureInPictureController.isPictureInPictureSupported()。
  2. 通过 FlutterDartProject.lookupKey(forAsset:) 找到 MP4。
  3. 用隐藏容器挂 AVPlayerLayer。
  4. startPictureInPicture(),并处理首帧 isPictureInPicturePossible 为 false 的延迟兜底。

4) "边退边出"时序

在 delegate 的 willStartPictureInPicture 就触发:

swift 复制代码
UIApplication.shared.perform(#selector(NSXPCConnection.suspend))

在 didStartPictureInPicture 再兜底一次,外加防重复标记 hasRequestedSuspend。

这样用户体感最好,不会出现"PiP 出来后等一会才退桌面"。


5) 前台关闭 PiP 的稳定方案(核心)

这部分决定成败。推荐固定策略:

  1. 同时监听 willEnterForeground 和 didBecomeActive。
  2. 前台阶段持续调用 stopPictureInPicture(),不要只 pause()。
  3. 做短间隔重试(例如 0.12s * 12 次)。
  4. 多次 stop 仍失败时,断开 playerLayer.player = nil 后再 stop。
  5. 只在 didStop 回调或确认 isPictureInPictureActive == false 后 cleanup。

restoreUserInterfaceForPictureInPictureStopWithCompletionHandler 返回 true,可减少灰底等待态。


6) 必加日志(排障效率翻倍)

建议统一前缀,例如 小组件教程画中画,打印:

  1. 启动成功/失败。
  2. active / possible / retry。
  3. willStart / didStart / willStop / didStop。
  4. 前台兜底断链是否触发。

你会快速判断是:

  1. 系统没收到 stop。
  2. 回调没回来。
  3. cleanup 过早导致状态机断裂。

7) AI 一次生成高质量代码的提示词模板

把下面这段直接给 AI,成功率会高很多:

text 复制代码
请在 Flutter 项目中实现"教程视频 PiP + 退桌面"能力,要求:
1) 页面入口:历史页标题点击触发;
2) Flutter 仅调用 NativeWidgetUtil,不在页面内渲染视频;
3) 用 Pigeon 新增 HostApi:startWidgetTutorialPipAndExitDebug(String assetPath, bool loop, bool muted);
4) iOS 原生在 NativeWidgetAPI.swift 内新增独立管理器,使用 AVPlayer + AVPictureInPictureController;
5) 启动时序:willStartPictureInPicture 即触发 suspend,didStart 再兜底;
6) 回前台必须关闭 PiP,不是仅暂停:
   - 监听 willEnterForeground + didBecomeActive;
   - stopPictureInPicture 重试;
   - 必要时断开 playerLayer.player 再 stop;
   - 仅 didStop 或 inactive 后 cleanup;
7) Android/OHOS 实现 no-op;
8) 代码要包含中文注释,抽常量,提升可维护性;
9) 输出修改文件清单、关键 diff、验收步骤。

8) 验收清单(复制即测)

  1. iOS 真机点击入口后,出现 PiP 并退桌面。
  2. 回到前台时,PiP 在短时间内关闭,不只暂停。
  3. 不支持 PiP 的机型不崩溃,有错误提示。
  4. Android/OHOS 不受影响。
  5. flutter analyze 与 iOS 构建通过。

按这个模板实现这个功能,下次你或 AI 都能少走很多弯路。

感谢您的阅读和参与,HH思无邪愿与您一起在技术的道路上不断探索。如果您喜欢这篇文章,不妨留下您宝贵的赞!如果您对文章有任何疑问或建议,欢迎在评论区留言,我会第一时间处理,您的支持是我前行的动力,愿我们都能成为更好的自己!

相关推荐
茶底世界之下3 天前
为什么预览、录制、离线导出不能共享同一个背压策略?
ios·swift
2501_916007473 天前
苹果商店iOS应用上架费用全面解析:开发者计划与审核费用计算
android·ios·小程序·https·uni-app·iphone·webview
君赏3 天前
棉宇宙 Flutter 热更新是如何落地的
flutter
2501_916008893 天前
如何实现iOS App代码混淆:SwiftShield使用指南
android·ios·小程序·https·uni-app·iphone·webview
2501_915909063 天前
移动端开发工具链怎么组合比较好,iOS、Android、跨端三套配置方案
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
恋猫de小郭3 天前
AndroidX 新增 Security State ,支持可编程的系统安全状态查询
android·前端·flutter
zihan5183 天前
自己做软件日记9/21 得到了一个良好的文件结构框架
后端·flutter·前端框架·android studio
2501_916008893 天前
怎么用 Godot 导出 iOS 应用并上架 App Store?签名字段该填什么?
android·ios·小程序·https·uni-app·iphone·webview
2501_915918414 天前
iOS编程用什么软件好?主流工具Xcode、AppCode、CodeRunner与新兴快蝎对比
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
终端安全笔记4 天前
iOS 27 之后「策略空转」:设备升级不报错,但旧策略不再管它
android·网络·安全·ios·智能手机