Flutter图片选择插件fl_image_picker的使用
Flutter图片选择插件fl_image_picker的使用
fl_image_picker
是一个对 image_picker
插件的简单封装,可以用于在 Flutter 应用中方便地选择图片和视频。以下是如何使用 fl_image_picker
的详细步骤和示例代码。
初始化
首先,在应用启动时进行必要的初始化:
import 'package:flutter/material.dart';
import 'package:fl_image_picker/fl_image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:device_info_plus/device_info_plus.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 设置资源渲染组件(默认仅支持图片预览)
FlImagePicker.imageBuilder = (entity, bool isThumbnail) => ImageBuilder(entity, isThumbnail: isThumbnail);
// 设置权限申请回调
FlImagePicker.checkPermission = (PickerOptionalActions action) async {
if (!isMobile) return true;
if (action == PickerOptionalActions.image || action == PickerOptionalActions.video) {
if (isIOS) {
return (await Permission.photos.request()).isGranted;
} else if (isAndroid) {
final androidInfo = await DeviceInfoPlugin().androidInfo;
if (androidInfo.version.sdkInt < 33) {
return (await Permission.storage.request()).isGranted;
}
return (await Permission.photos.request()).isGranted && (await Permission.videos.request()).isGranted;
}
return false;
} else if (action == PickerOptionalActions.takePictures || action == PickerOptionalActions.recording) {
final permissionState = await Permission.camera.request();
return permissionState.isGranted;
}
return false;
};
// 设置多选框点击方法预览的弹出方式
FlImagePicker.previewModalPopup = (_, Widget widget) => widget.popupDialog();
// 设置多选框点击预览的UI组件
FlImagePicker.previewBuilder = (context, entity, allEntity) {
return FlPreviewGesturePageView(
pageView: PageView.builder(
itemCount: allEntity.length,
itemBuilder: (_, int index) => FlImagePicker.imageBuilder(allEntity[index], false)));
};
// 设置错误回调的提示
FlImagePicker.errorCallback = (ErrorDes des) {
switch (des) {
case ErrorDes.maxBytes:
showToast('资源过大');
break;
case ErrorDes.maxCount:
showToast('超过最大数量');
break;
case ErrorDes.maxVideoCount:
showToast('超过最大视频数量');
break;
}
};
runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Fl Image Picker')),
body: const _HomePage())));
}
使用示例
单选
/// 单选
SingleImagePicker();
多选
/// 多选
MultipleImagePicker();
直接调用方法选择
void fun() {
/// 选择Actions
FlImagePicker.showPickActions();
/// 最原始的选择器
FlImagePicker.showPick();
/// 最原始的多选择器 只能图片多选
FlImagePicker.showPickMultipleImage();
/// 以上两个方法依次调用
FlImagePicker.showPickWithOptionalActions();
}
完整示例代码
import 'package:device_info_plus/device_info_plus.dart';
import 'package:example/src/previewed.dart';
import 'package:fl_extended/fl_extended.dart';
import 'package:fl_image_picker/fl_image_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:permission_handler/permission_handler.dart';
bool get isAndroid => defaultTargetPlatform == TargetPlatform.android;
bool get isIOS => defaultTargetPlatform == TargetPlatform.iOS;
bool get isMobile => isAndroid || isIOS;
bool get isWeb => kIsWeb;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
flImagePickerInit();
runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Fl Image Picker')),
body: const _HomePage())));
}
void flImagePickerInit() {
FlImagePicker.imageBuilder = (entity, bool isThumbnail) => ImageBuilder(entity, isThumbnail: isThumbnail);
FlImagePicker.checkPermission = (PickerOptionalActions action) async {
if (isWeb || !isMobile) return true;
if (action == PickerOptionalActions.image || action == PickerOptionalActions.video) {
if (isIOS) {
return (await Permission.photos.request()).isGranted;
} else if (isAndroid) {
final androidInfo = await DeviceInfoPlugin().androidInfo;
if (androidInfo.version.sdkInt < 33) {
return (await Permission.storage.request()).isGranted;
}
return (await Permission.photos.request()).isGranted && (await Permission.videos.request()).isGranted;
}
return false;
} else if (action == PickerOptionalActions.takePictures || action == PickerOptionalActions.recording) {
final permissionState = await Permission.camera.request();
return permissionState.isGranted;
}
return false;
};
FlImagePicker.previewModalPopup = (_, Widget widget) => widget.popupDialog();
FlImagePicker.previewBuilder = (context, entity, allEntity) {
return FlPreviewGesturePageView(
pageView: PageView.builder(
itemCount: allEntity.length,
itemBuilder: (_, int index) => FlImagePicker.imageBuilder(allEntity[index], false)));
};
FlImagePicker.errorCallback = (ErrorDes des) {
switch (des) {
case ErrorDes.maxBytes:
showToast('资源过大');
break;
case ErrorDes.maxCount:
showToast('超过最大数量');
break;
case ErrorDes.maxVideoCount:
showToast('超过最大视频数量');
break;
}
};
}
const url = 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fitem%2F201612%2F31%2F20161231205134_uVTex.thumb.400_0.jpeg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1666931842&t=44493a6c92d1ddda89367519c6206491';
class _HomePage extends StatelessWidget {
const _HomePage();
[@override](/user/override)
Widget build(BuildContext context) {
return Universal(
padding: const EdgeInsets.all(12),
isScroll: true,
children: [
const Text('单资源选择 仅图片').marginAll(12),
buildSingleImagePicker(ImageType.image),
const Text('单资源选择 仅视频').marginAll(12),
buildSingleImagePicker(ImageType.video),
const Text('多资源选择 仅图片').marginAll(12),
buildMultiImagePicker(ImageType.image),
const Text('多资源选择 仅视频').marginAll(12),
buildMultiImagePicker(ImageType.video),
]);
}
Widget buildSingleImagePicker(ImageType imageType) {
List<PickerActions> actions = [
const PickerActions(action: PickerOptionalActions.cancel, text: Text('取消')),
];
switch (imageType) {
case ImageType.other:
break;
case ImageType.image:
actions.insertAll(0, const [
PickerActions(action: PickerOptionalActions.image, text: Text('图库选择')),
PickerActions(action: PickerOptionalActions.takePictures, text: Text('相机拍照')),
]);
break;
case ImageType.video:
actions.insertAll(0, const [
PickerActions(action: PickerOptionalActions.video, text: Text('图库选择')),
PickerActions(action: PickerOptionalActions.recording, text: Text('相机拍摄')),
]);
break;
}
return Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
SingleImagePicker(
actions: actions,
renovate: (ImageType imageType, XFile entity) async {
if (imageType == ImageType.image) {
return await compressImage(entity);
}
return null;
},
initialData: SingleImagePicker.convertUrl(url),
itemConfig: ImagePickerItemConfig(
borderRadius: BorderRadius.circular(10),
color: Colors.amberAccent),
onChanged: (ExtendedXFile value) {
'onChanged ${value.realValueStr} renovated Type: ${value.renovated.runtimeType}'.log();
}),
SingleImagePicker(
renovate: (ImageType imageType, XFile entity) async {
if (imageType == ImageType.image) {
return await compressImage(entity);
}
return null;
},
actions: actions,
initialData: SingleImagePicker.convertUrl(url),
itemConfig: ImagePickerItemConfig(
borderRadius: BorderRadius.circular(40),
color: Colors.amberAccent),
onChanged: (ExtendedXFile value) {
'onChanged ${value.realValueStr}'.log();
}),
]);
}
Widget buildMultiImagePicker(ImageType imageType) {
List<PickerActions> actions = [
const PickerActions(action: PickerOptionalActions.cancel, text: Text('取消')),
];
switch (imageType) {
case ImageType.other:
break;
case ImageType.image:
actions.insertAll(0, const [
PickerActions(action: PickerOptionalActions.image, text: Text('图库选择')),
PickerActions(action: PickerOptionalActions.takePictures, text: Text('相机拍照')),
]);
break;
case ImageType.video:
actions.insertAll(0, const [
PickerActions(action: PickerOptionalActions.video, text: Text('图库选择')),
PickerActions(action: PickerOptionalActions.recording, text: Text('相机拍摄')),
]);
break;
}
return MultipleImagePicker(
initialData: MultipleImagePicker.convertUrls(url),
actions: actions,
renovate: (ImageType imageType, XFile entity) async {
if (imageType == ImageType.image) {
return await compressImage(entity);
}
return null;
},
itemConfig: ImagePickerItemConfig(
delete: const DefaultDeleteIcon(backgroundColor: Colors.blue),
deletionConfirmation: (_) async {
final value = await CupertinoAlertDialog(
content: Container(
padding: const EdgeInsets.symmetric(vertical: 10),
constraints: const BoxConstraints(maxHeight: 100),
child: const Text('确定要删除么')),
actions: [
Universal(
height: 45,
alignment: Alignment.center,
onTap: () {
pop(false);
},
child: const BText('取消', fontSize: 14, color: Colors.grey)),
Universal(
height: 45,
alignment: Alignment.center,
onTap: () {
pop(true);
},
child: const BText('确定', fontSize: 14, color: Colors.grey)),
]).popupCupertinoModal<Bool?>();
return value ?? false;
}),
onChanged: (List<ExtendedXFile> value) {
'onChanged ${value.builder((item) => item.realValueStr)}'.log();
});
}
}
/// 图片压缩
Future<Uint8List?> compressImage(XFile file) async {
if (isWeb || (!isWeb && isMobile)) {
final fileName = file.path.removePrefix(file.path);
final suffix = fileName.split('.').last.toLowerCase();
CompressFormat? format;
if (suffix == 'jpg' || suffix == 'jpeg') {
format = CompressFormat.jpeg;
} else if (suffix == 'png') {
format = CompressFormat.png;
} else if (suffix == 'webp') {
format = CompressFormat.webp;
} else if (suffix == 'heic') {
format = CompressFormat.heic;
}
if (format != null) {
return await FlutterImageCompress.compressWithFile(file.path,
format: format, minWidth: 480, minHeight: 480, quality: 80);
}
}
return null;
}
更多关于Flutter图片选择插件fl_image_picker的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter图片选择插件fl_image_picker的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用fl_image_picker
插件来选择图片的示例代码。fl_image_picker
是一个流行的Flutter插件,用于从设备相册或相机中选择图片。需要注意的是,这个插件的实际名称可能是image_picker
(官方插件),而fl_image_picker
可能是一个社区维护的变体或者特定情况下的命名。下面的例子基于官方的image_picker
插件,因为这是最常见和推荐的用法。
首先,确保在pubspec.yaml
文件中添加依赖:
dependencies:
flutter:
sdk: flutter
image_picker: ^0.8.4+4 # 请检查最新版本号并替换
然后,运行flutter pub get
来安装依赖。
接下来,在你的Dart文件中使用image_picker
插件。以下是一个完整的示例,展示如何选择图片并在Image.widget中显示:
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ImagePickerDemo(),
);
}
}
class ImagePickerDemo extends StatefulWidget {
@override
_ImagePickerDemoState createState() => _ImagePickerDemoState();
}
class _ImagePickerDemoState extends State<ImagePickerDemo> {
File? _imageFile;
final ImagePicker _picker = ImagePicker();
Future<void> _pickImage(ImageSource source) async {
final XFile? image = await _picker.pickImage(source: source);
if (image != null) {
final File imageFile = File(image.path);
// 如果需要,可以在这里处理图片,比如压缩或裁剪
// 例如,使用image库进行图像处理
// import 'package:image/image.dart' as img;
// Uint8List imageBytes = imageFile.readAsBytesSync();
// img.Image image = img.decodeImage(imageBytes)!;
// ... 对image进行处理 ...
// File processedImageFile = File('path/to/save/processed_image.jpg');
// processedImageFile.writeAsBytesSync(img.encodeJpg(image, quality: 85));
setState(() {
_imageFile = imageFile;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Image Picker Demo'),
),
body: Center(
child: _imageFile == null
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'No image selected.',
style: TextStyle(fontSize: 24),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () => _pickImage(ImageSource.gallery),
child: Text('Pick Image from Gallery'),
),
SizedBox(height: 10),
ElevatedButton(
onPressed: () => _pickImage(ImageSource.camera),
child: Text('Pick Image from Camera'),
),
],
)
: Image.file(_imageFile!),
),
);
}
}
在这个示例中:
ImagePickerDemo
是一个有状态的Widget,它维护了一个_imageFile
变量来存储选中的图片文件。_pickImage
方法使用ImagePicker
实例从指定的ImageSource
(相册或相机)中选择图片。- 当图片被选中时,
_imageFile
被更新,UI也随之刷新。 - 如果
_imageFile
为null
,则显示两个按钮让用户从相册或相机中选择图片;否则,显示选中的图片。
请确保在Android和iOS项目中配置必要的权限,特别是相机和存储访问权限。对于Android,你需要在AndroidManifest.xml
中添加权限,并在运行时请求它们。对于iOS,你需要在Info.plist
中添加相应的权限描述。
这个示例代码提供了一个基本的框架,你可以根据需要进行扩展和修改。