Flutter中屏幕共享的实现原理、所需条件及实施步骤
Flutter 屏幕共享实现指南:获取内容与Widget展示
前置条件
- 稳定版Flutter环境(推荐3.0+)
- Android端:
- 目标SDK版本≥21(MediaProjection API最低要求)
- 需申请
RECORD_AUDIO、WRITE_EXTERNAL_STORAGE权限(Android 13+需动态申请)
- iOS端:
- 需创建Broadcast Extension Target(Xcode操作)
- 配置
NSMicrophoneUsageDescription、NSScreenCaptureUsageDescription权限描述 - 仅支持真机测试,模拟器不支持录屏功能
核心实现步骤
1. 系统权限申请
- Android:调用MediaProjectionManager发起权限请求,用户授权后获取MediaProjection实例
- iOS:通过ReplayKit触发系统录屏授权弹窗,同时确保Broadcast Extension已正确配置
2. 获取屏幕画面流
- Android:利用MediaProjection创建虚拟显示器,将屏幕画面转为SurfaceTexture,再传递给Flutter端
- iOS:通过Broadcast Extension的
RPBroadcastSampleHandler捕获视频帧,将帧数据编码后传递给主App
3. 在Flutter Widget中展示
将原生传递的画面流转为Flutter的Texture Widget(最直接的方式),或通过视频播放器组件解码播放
4. 生命周期管理
- 页面销毁时停止屏幕捕获,释放MediaProjection/ReplayKit资源
- 监听权限状态变化,用户取消授权时立即终止共享
代码示例
Flutter端核心代码(基于flutter_media_projection插件)
先在pubspec.yaml添加依赖:
dependencies: flutter_media_projection: ^0.1.5 # 替换为最新版本
页面实现:
import 'package:flutter/material.dart'; import 'package:flutter_media_projection/flutter_media_projection.dart'; class ScreenShareDemo extends StatefulWidget { const ScreenShareDemo({super.key}); @override State<ScreenShareDemo> createState() => _ScreenShareDemoState(); } class _ScreenShareDemoState extends State<ScreenShareDemo> { final FlutterMediaProjection _mediaProjection = FlutterMediaProjection(); int? _textureId; Future<void> _startCapture() async { final permission = await _mediaProjection.requestPermission(); if (permission == PermissionResult.granted) { final textureId = await _mediaProjection.startScreenCapture( width: MediaQuery.of(context).size.width.toInt(), height: MediaQuery.of(context).size.height.toInt(), fps: 30, ); setState(() => _textureId = textureId); } } void _stopCapture() { if (_textureId != null) { _mediaProjection.stopScreenCapture(); setState(() => _textureId = null); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('屏幕共享测试')), body: Column( children: [ Expanded( child: _textureId != null ? Texture(textureId: _textureId!) : const Center(child: Text('点击下方按钮开始共享')), ), Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( onPressed: _textureId == null ? _startCapture : _stopCapture, child: Text(_textureId == null ? '开始共享' : '停止共享'), ), ), ], ), ); } @override void dispose() { _stopCapture(); super.dispose(); } }
Android配置(AndroidManifest.xml)
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" /> <!-- 若targetSdk≥33,需添加动态权限申请逻辑 -->
iOS配置
- 在Xcode中为项目添加Broadcast Extension Target(File → New → Target → Broadcast Extension)
- 修改主App的
Info.plist:
<key>NSMicrophoneUsageDescription</key> <string>屏幕共享需要麦克风权限捕捉音频</string> <key>NSScreenCaptureUsageDescription</key> <string>屏幕共享需要录屏权限捕捉画面</string>
- 修改Broadcast Extension的
Info.plist,确保RPBroadcastProcessMode设为RPSystemBroadcastProcessMode
注意事项
- 性能优化:根据需求调整分辨率、帧率,避免高配置导致的卡顿
- 权限异常处理:用户拒绝权限时需给出明确提示,引导用户去设置页开启
- iOS端:Broadcast Extension的代码需自行实现帧数据传递逻辑(部分插件已封装此步骤)
- Android 12+:需处理后台屏幕捕获的限制,避免应用退到后台后共享中断
内容的提问来源于stack exchange,提问作者Yunnane
相关产品推荐
相关产品推荐

