Flutter图片选择插件fl_image_picker的使用

发布于 1周前 作者 yibo5220 来自 Flutter

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

1 回复

更多关于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!),
      ),
    );
  }
}

在这个示例中:

  1. ImagePickerDemo是一个有状态的Widget,它维护了一个_imageFile变量来存储选中的图片文件。
  2. _pickImage方法使用ImagePicker实例从指定的ImageSource(相册或相机)中选择图片。
  3. 当图片被选中时,_imageFile被更新,UI也随之刷新。
  4. 如果_imageFilenull,则显示两个按钮让用户从相册或相机中选择图片;否则,显示选中的图片。

请确保在Android和iOS项目中配置必要的权限,特别是相机和存储访问权限。对于Android,你需要在AndroidManifest.xml中添加权限,并在运行时请求它们。对于iOS,你需要在Info.plist中添加相应的权限描述。

这个示例代码提供了一个基本的框架,你可以根据需要进行扩展和修改。

回到顶部