Flutter媒体扩展功能插件media_extension的使用
Flutter媒体扩展功能插件media_extension的使用
简介
media_extension
是一个帮助 Flutter 应用程序像原生图库一样工作的插件。该插件由 ente 提供。
功能
- 处理来自其他应用的调用:
- 可以识别和处理来自不同应用的调用,并获取触发调用的意图动作(如
PICK
、EDIT
、VIEW
)。 - 使用
setResult
方法将选定媒体文件的 URI 传递给请求的应用,通过在内容提供者路径中创建临时副本。
- 可以识别和处理来自不同应用的调用,并获取触发调用的意图动作(如
- 打开第三方应用:
- 能够使用第三方应用(如图库、编辑器、壁纸管理器等)打开图片。
实现
- Native 代码调用 Flutter 代码:通过
onAttachedEngineMethod
调用 Flutter 方法,返回模式异步。在 Dart 代码中使用Completer
获取状态并设置应用模式。 - 作为图库行为:从应用程序文档存储目录中选择图像的 URI,并调用带有 URI 参数的方法。
- 创建临时文件:在缓存目录中创建临时文件,并授予其他应用读取该目录的权限。一旦创建了
content://uri
(内容提供者),结果将通过 intent 发送到请求的活动。
数据类型描述
类型 | 字段 |
---|---|
IntentAction |
枚举值:main 、pick 、edit 、view |
MediaExtentionAction |
IntentAction action [调用应用的意图动作],String? uri [其他应用通过意图发送的图片 URI] |
方法描述
方法 | 参数 | 返回 |
---|---|---|
getIntentAction |
无 | MediaExtentionAction |
setResult |
String uri [选定文件的路径] |
void |
setAs |
String uri [选定文件的路径],String mimeType [选定文件的 MIME 类型] |
void |
openWith |
String uri [选定文件的路径],String mimeType [选定文件的 MIME 类型] |
void |
edit |
String uri [选定文件的路径],String mimeType [选定文件的 MIME 类型] |
void |
入门指南
安装
- 通过终端命令安装:
flutter pub get media_extension
- 在
pubspec.yaml
文件中添加依赖:dependencies: flutter: sdk: flutter media_extension:
配置 Android
- 在
android/src/main/res/xml/provider_paths.xml
中添加以下内容:<?xml version="1.0" encoding="utf-8"?> <paths> <external-path name="external_files" path="." /> <cache-path name="embedded" path="." /> </paths>
- 在
android/src/main/AndroidManifest.xml
中添加以下内容:<application> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="image/*" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.PICK" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="image/*" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.GET_CONTENT" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.OPENABLE" /> <data android:mimeType="image/*" /> </intent-filter> </activity> </application>
示例代码
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:media_extension_example/download.dart';
import 'package:path_provider/path_provider.dart';
import 'package:media_extension/media_extension_action_types.dart';
import 'package:media_extension/media_extension.dart';
import 'package:video_player/video_player.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final imgUrl = "https://cdn-chat.sstatic.net/chat/img/404_funny_hats.jpg";
MediaExtentionAction _intentAction = MediaExtentionAction(action: IntentAction.main);
final _mediaExtensionPlugin = MediaExtension();
final _downloadHelper = DownloadHelper(Dio());
late VideoPlayerController _controller;
@override
void initState() {
super.initState();
initPlatformState();
}
Future<void> initPlatformState() async {
MediaExtentionAction intentAction;
try {
intentAction = await _mediaExtensionPlugin.getIntentAction();
if (intentAction.action == IntentAction.view && intentAction.type == MediaType.video) {
_controller = VideoPlayerController.contentUri(Uri.parse(intentAction.data!));
await _controller.initialize();
}
} on PlatformException {
intentAction = MediaExtentionAction(action: IntentAction.main);
throw ("Platform exception");
} on Exception catch (_) {
intentAction = MediaExtentionAction(action: IntentAction.main);
}
if (!mounted) return;
setState(() {
_intentAction = intentAction;
if (_intentAction.type == MediaType.video) _controller.play();
});
}
Future<String> _getLocalFile(String filename) async {
var tempDir = await getTemporaryDirectory();
String fullPath = "${tempDir.path}/image.jpg";
await _downloadHelper.downloadToFile(imgUrl, fullPath);
return fullPath;
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Intent Action is: ${_intentAction.action!.toShortString()}'),
),
body: Center(
child: _intentAction.action == IntentAction.view
? (_intentAction.type == MediaType.video
? Center(
child: _controller.value.isInitialized
? AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
)
: Container())
: Image.memory(base64Decode(_intentAction.data!)))
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FutureBuilder(
future: _getLocalFile("image.jpg"),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
return snapshot.data != null
? GestureDetector(
child: Image.file(File(snapshot.data!)),
onTap: () async {
if (_intentAction.action == IntentAction.pick) {
_mediaExtensionPlugin.setResult("file://${snapshot.data!}");
}
},
)
: Container();
}),
GestureDetector(
child: Text(
'Set as',
style: Theme.of(context).textTheme.headlineMedium,
),
onTap: () async {
var tempDir = await getTemporaryDirectory();
String fullPath = "${tempDir.path}/image.jpg'";
await _downloadHelper.downloadToFile(imgUrl, fullPath);
try {
final isDone = await _mediaExtensionPlugin.setAs("file://$fullPath", "image/*");
debugPrint("Is done $isDone");
} on PlatformException {
debugPrint("Some error");
}
},
),
GestureDetector(
child: Text(
'Edit',
style: Theme.of(context).textTheme.headlineMedium,
),
onTap: () async {
var tempDir = await getTemporaryDirectory();
String fullPath = "${tempDir.path}/image.jpg'";
await _downloadHelper.downloadToFile(imgUrl, fullPath);
try {
final isDone = await _mediaExtensionPlugin.edit("file://$fullPath", "image/*");
debugPrint("Is done $isDone");
} on PlatformException {
debugPrint("Some error");
}
},
),
GestureDetector(
child: Text(
'Open With',
style: Theme.of(context).textTheme.headlineMedium,
),
onTap: () async {
var tempDir = await getTemporaryDirectory();
String fullPath = "${tempDir.path}/image.jpg'";
await _downloadHelper.downloadToFile(imgUrl, fullPath);
try {
final result = await _mediaExtensionPlugin.openWith("file://$fullPath", "image/*");
debugPrint("Is done $result");
} on PlatformException {
debugPrint("Some error");
}
},
),
],
),
),
),
);
}
}
问题和即将的变化
- 实现
setResults
方法,用于ACTION_PICK
意图的多选图片。
致谢
- aves:插件中的大多数方法都受到此仓库的启发。
许可证
更多关于Flutter媒体扩展功能插件media_extension的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter媒体扩展功能插件media_extension的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用media_extension
插件的示例代码。media_extension
插件通常用于扩展Flutter应用中媒体文件的处理功能,比如自定义视频播放器或音频解码等。需要注意的是,实际使用中可能会有不同的API和配置需求,以下示例基于一个假设的场景,即创建一个自定义的视频播放器。
首先,确保你已经在pubspec.yaml
文件中添加了media_extension
依赖:
dependencies:
flutter:
sdk: flutter
media_extension: ^x.y.z # 请替换为实际的版本号
然后运行flutter pub get
来安装依赖。
1. 配置Info.plist
(iOS)和AndroidManifest.xml
(Android)
对于iOS,你可能需要在Info.plist
中添加一些必要的配置来允许媒体播放。对于Android,确保在AndroidManifest.xml
中有相应的权限声明。
iOS (Info.plist
)
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>video-playback</string>
</array>
Android (AndroidManifest.xml
)
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
2. 创建一个自定义的视频播放器
接下来,我们将创建一个自定义的视频播放器,利用media_extension
插件提供的API。假设media_extension
插件提供了一个简单的接口来加载和播放视频。
main.dart
import 'package:flutter/material.dart';
import 'package:media_extension/media_extension.dart'; // 假设插件提供了这个包名
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: CustomVideoPlayerScreen(),
);
}
}
class CustomVideoPlayerScreen extends StatefulWidget {
@override
_CustomVideoPlayerScreenState createState() => _CustomVideoPlayerScreenState();
}
class _CustomVideoPlayerScreenState extends State<CustomVideoPlayerScreen> {
late MediaExtensionPlayer _player;
@override
void initState() {
super.initState();
// 初始化播放器
_player = MediaExtensionPlayer(
url: 'https://www.example.com/path/to/your/video.mp4',
onReady: () {
print('Player is ready');
},
onError: (error) {
print('Error: $error');
},
onEnd: () {
print('Video ended');
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Custom Video Player'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_player.widget, // 假设插件提供了一个widget属性
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
_player.play();
},
child: Text('Play'),
),
ElevatedButton(
onPressed: () {
_player.pause();
},
child: Text('Pause'),
),
],
),
),
);
}
@override
void dispose() {
_player.dispose();
super.dispose();
}
}
// 假设MediaExtensionPlayer类
class MediaExtensionPlayer {
final String url;
final VoidCallback onReady;
final Function(dynamic) onError;
final VoidCallback onEnd;
late Widget widget;
MediaExtensionPlayer({
required this.url,
required this.onReady,
required this.onError,
required this.onEnd,
}) {
// 初始化widget,这里只是模拟,实际插件可能有不同的初始化方式
widget = Platform.isIOS
? CupertinoSlider.adaptive(
value: 0.0, onChanged: (_) {}, min: 0.0, max: 1.0, activeColor: Colors.blue)
: Slider(
value: 0.0, onChanged: (_) {}, min: 0.0, max: 1.0, activeColor: Colors.blue);
// 插件初始化代码(假设)
_initializePlayer();
}
void _initializePlayer() async {
// 模拟异步初始化过程
await Future.delayed(Duration(seconds: 2));
onReady();
// 注意:这里应该包含实际的播放器初始化代码
}
void play() {
// 播放视频的逻辑
print('Playing video...');
}
void pause() {
// 暂停视频的逻辑
print('Pausing video...');
}
void dispose() {
// 清理资源
print('Player disposed');
}
}
注意
- 插件API: 上述代码中的
MediaExtensionPlayer
类是一个假设的类,实际插件可能有不同的API。请查阅media_extension
插件的官方文档来了解其提供的具体API和用法。 - 错误处理: 在实际应用中,应添加更多的错误处理和边界情况检查。
- 平台特定代码: 根据不同的平台(iOS和Android),可能需要编写特定的代码来处理媒体播放。
确保在实际项目中根据插件的文档和API进行调整。如果media_extension
插件有具体的示例代码或文档,请参考那些资源以获得更准确的信息。