Flutter媒体资源管理插件media_asset_utils的使用
Flutter媒体资源管理插件media_asset_utils的使用
媒体资源管理插件media_asset_utils
本库可以在Android和iOS上运行。
写在前面
工作繁忙,只能不定期更新,还望网友们见谅!
各平台最低要求:
- Android 24+
- iOS 12.0+
项目描述
- 图片压缩使用 Luban(鲁班)—— 图片压缩工具
- 仿微信朋友圈压缩策略,不支持控制 quality。
- 视频压缩 使用硬件编码,并未使用
ffmpeg
- 根据 quality 对 width、height 进行自动缩放以及 bitrate 计算。
- bitrate 计算公式 width * height * fps * 0.07。
- Native 获取视频、图片信息。
- 保存图片、视频到系统相册。
配置
Android
由于库依赖于 Kotlin 版本 1.7.10
,请更改项目级别的 build.gradle
文件来确保项目中的最低 kotlin 版本。
在 AndroidManifest.xml
中添加如下权限:
API < 29
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"
tools:ignore="ScopedStorage" />
API >= 29
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32"/>
API >= 33
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
iOS
将以下内容添加到您的 Info.plist
文件中:
<key>NSPhotoLibraryUsageDescription</key>
<string>${PRODUCT_NAME} library Usage</string>
完整示例Demo
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:media_asset_utils/media_asset_utils.dart';
import 'package:media_asset_utils_example/permission_utils.dart';
import 'package:path_provider/path_provider.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
[@override](/user/override)
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
File? outputFile;
File? file;
int? outputFileSize;
int? fileSize;
[@override](/user/override)
void initState() {
super.initState();
}
Future<void> initThumbnail(BuildContext context) async {
final bool isGranted = await GGPermissionUtil.album();
if (isGranted) {
final List<AssetEntity>? assets = await AssetPicker.pickAssets(
context,
pickerConfig: AssetPickerConfig(
requestType: RequestType.video,
maxAssets: 1,
),
);
if ((assets ?? []).isNotEmpty) {
file = await assets!.first.file;
setState(() {
fileSize = file!.lengthSync();
});
Directory? directory;
directory = await getApplicationDocumentsDirectory();
outputFile =
File('${directory.path}/thumbnail_${Random().nextInt(100000)}.jpg');
return;
} else {
throw Exception("No files selected");
}
}
throw Exception("Permission denied");
}
Future<void> initCompress(BuildContext context, RequestType type) async {
final bool isGranted = await GGPermissionUtil.album();
if (isGranted) {
final List<AssetEntity>? assets = await AssetPicker.pickAssets(
context,
pickerConfig: AssetPickerConfig(
requestType: type,
maxAssets: 1,
),
);
if ((assets ?? []).isNotEmpty) {
Directory? directory;
directory = await getApplicationDocumentsDirectory();
if (Platform.isIOS) {
file = await assets!.first.originFile;
} else {
file = await assets!.first.file;
}
setState(() {
fileSize = file!.lengthSync();
});
if (type == RequestType.video) {
outputFile =
File('${directory.path}/video_${Random().nextInt(100000)}.mp4');
} else {
Directory('${directory.path}/abdd/12233').createSync(recursive: true);
outputFile = File('${directory.path}/abdd/12233/image_1.jpg');
}
return;
} else {
throw Exception("No files selected");
}
}
throw Exception("Permission denied");
}
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Builder(builder: (_) {
return Column(
children: [
Container(
width: double.infinity,
constraints: BoxConstraints(minHeight: 200),
child: Column(
children: [
Text(
'当前选择: $file, 文件大小: ${fileSize != null ? (fileSize! / 1024 / 1024).toStringAsFixed(2) : 0}'),
Text(
'处理后: $outputFile, 文件大小: ${outputFileSize != null ? (outputFileSize! / 1024 / 1024).toStringAsFixed(2) : 0}'),
],
),
),
TextButton(
onPressed: () async {
await initCompress(_, RequestType.video);
outputFile = await MediaAssetUtils.compressVideo(
file!,
saveToLibrary: true,
quality: VideoQuality.high,
thumbnailConfig: ThumbnailConfig(),
onVideoCompressProgress: (double progress) {
print(progress);
},
);
setState(() {
outputFileSize = outputFile!.lengthSync();
});
},
child: Text('Compress Video'),
),
TextButton(
onPressed: () async {
await initThumbnail(_);
outputFile = await MediaAssetUtils.getVideoThumbnail(
file!,
quality: 50,
saveToLibrary: true,
thumbnailFile: outputFile!,
);
setState(() {
outputFileSize = outputFile!.lengthSync();
});
},
child: Text('Get Video Thumbnail'),
),
TextButton(
onPressed: () async {
await initCompress(_, RequestType.video);
final result = await MediaAssetUtils.getVideoInfo(file!);
print(result.toJson());
},
child: Text('Get Video Info'),
),
TextButton(
onPressed: () async {
await initCompress(_, RequestType.image);
final result = await MediaAssetUtils.getImageInfo(file!);
print(result.toJson());
},
child: Text('Get Image Info'),
),
TextButton(
onPressed: () async {
await initCompress(_, RequestType.image);
print(outputFile);
final result = await MediaAssetUtils.compressImage(
file!,
saveToLibrary: true,
outputFile: outputFile,
);
print(result);
setState(() {
outputFileSize = outputFile!.lengthSync();
});
},
child: Text('Compress Image'),
),
TextButton(
onPressed: () async {
await initCompress(_, RequestType.image);
await MediaAssetUtils.saveToGallery(file!);
},
child: Text('Save To Media Store'),
),
],
);
}),
),
);
}
}
更多关于Flutter媒体资源管理插件media_asset_utils的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter媒体资源管理插件media_asset_utils的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用media_asset_utils
插件来管理媒体资源的一个基本示例。这个插件可以帮助你更方便地访问和管理设备的媒体文件,比如图片和视频。
首先,确保你已经在你的pubspec.yaml
文件中添加了media_asset_utils
依赖项:
dependencies:
flutter:
sdk: flutter
media_asset_utils: ^x.y.z # 请替换为最新版本号
然后,运行flutter pub get
来安装依赖项。
接下来,让我们编写一些代码来演示如何使用这个插件。以下是一个简单的示例,展示了如何获取设备的媒体文件列表并显示它们。
1. 导入必要的包
在你的Dart文件中,导入media_asset_utils
和其他必要的包:
import 'package:flutter/material.dart';
import 'package:media_asset_utils/media_asset_utils.dart';
2. 初始化插件并获取媒体文件
在你的Flutter组件中,初始化MediaAssetUtils
插件并获取媒体文件列表。
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Media Asset Utils Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MediaAssetScreen(),
);
}
}
class MediaAssetScreen extends StatefulWidget {
@override
_MediaAssetScreenState createState() => _MediaAssetScreenState();
}
class _MediaAssetScreenState extends State<MediaAssetScreen> {
List<MediaAsset> mediaAssets = [];
@override
void initState() {
super.initState();
_getMediaAssets();
}
Future<void> _getMediaAssets() async {
try {
// 初始化插件
final MediaAssetUtils mediaAssetUtils = MediaAssetUtils();
// 获取图片和视频文件
final List<MediaAsset> images = await mediaAssetUtils.getImages();
final List<MediaAsset> videos = await mediaAssetUtils.getVideos();
// 合并结果
setState(() {
mediaAssets = [...images, ...videos];
});
} catch (e) {
print("Error fetching media assets: $e");
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Media Assets'),
),
body: mediaAssets.isEmpty
? Center(child: Text('Loading media assets...'))
: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 4.0,
mainAxisSpacing: 4.0),
itemCount: mediaAssets.length,
itemBuilder: (context, index) {
final MediaAsset asset = mediaAssets[index];
return GridTile(
child: Image.file(File(asset.path)),
footer: GridTileBar(
title: Text(asset.name),
),
);
}),
);
}
}
3. 权限处理
由于访问设备的媒体文件需要特定的权限,你需要在AndroidManifest.xml
和Info.plist
中声明这些权限。此外,你可能还需要在运行时请求这些权限(尤其是在Android上)。
Android
在AndroidManifest.xml
中添加以下权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
iOS
在Info.plist
中添加以下键:
<key>NSPhotoLibraryUsageDescription</key>
<string>We need access to your photo library to display your media assets.</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
注意:iOS的权限请求通常是通过UIAlertController
在运行时进行的,这超出了media_asset_utils
插件的范围,因此你可能需要额外的代码来处理。
总结
以上代码展示了如何使用media_asset_utils
插件来获取设备的媒体文件,并在Flutter应用中显示它们。请注意,实际使用时你可能需要处理更多的错误情况和权限请求。希望这个示例对你有所帮助!