Flutter图片选择插件sparrow_image_picker的使用

Flutter图片选择插件sparrow_image_picker的使用

安装

首先,在pubspec.yaml文件中添加sparro_image_picker作为依赖项。

iOS

该插件需要iOS 9.0或更高版本。从版本0.8.1开始,iOS实现使用PHPicker来选择(多个)图片。在iOS 14或更高版本的iOS模拟器上,无法选择HEIC格式的图片。这是一个已知问题。请在真实设备上测试,或者使用非HEIC格式的图片,直到Apple解决此问题。

Info.plist文件中添加以下键:

  • NSPhotoLibraryUsageDescription - 描述你的应用为什么需要访问照片库。
  • NSCameraUsageDescription - 描述你的应用为什么需要访问相机。
  • NSMicrophoneUsageDescription - 如果你的应用打算录制视频,则需要描述你的应用为什么需要访问麦克风。

Android

从版本0.8.1开始,Android实现支持在Android 4.3或更高版本上选择(多个)图片。无需额外配置,插件应能直接工作。

注意:使用相机拍摄的图片和视频会保存到应用的本地缓存中,并且应被视为临时文件。如果你需要永久存储所选的图片,请将其移动到更持久的位置。

示例

以下是使用sparrow_image_picker选择图片和视频的基本示例:

import 'package:flutter/material.dart';
import 'package:sparrow_image_picker/sparrow_image_picker.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Image Picker Demo',
      home: MyHomePage(title: 'Image Picker Example'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, this.title}) : super(key: key);

  final String? title;

  [@override](/user/override)
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<XFile>? _imageFileList;

  set _imageFile(XFile? value) {
    _imageFileList = value == null ? null : [value];
  }

  dynamic _pickImageError;
  bool isVideo = false;

  VideoPlayerController? _controller;
  VideoPlayerController? _toBeDisposed;
  String? _retrieveDataError;

  final ImagePicker _picker = ImagePicker();

  void _onImageButtonPressed(ImageSource source, {BuildContext? context, bool isMultiImage = false}) async {
    if (_controller != null) {
      await _controller!.setVolume(0.0);
    }
    if (isVideo) {
      final XFile? file = await _picker.pickVideo(source: source, maxDuration: const Duration(seconds: 10));
      print("file=${file?.path}");
      await _playVideo(file);
    } else if (isMultiImage) {
      final List<XFile>? pickedFileList = await _picker.pickMultiImage(count: 3);
      setState(() {
        _imageFileList = pickedFileList;
        pickedFileList?.forEach((element) {
          print("${element.path}");
          print("${element.name}");
        });
      });
    } else {
      final XFile? pickedFile = await _picker.pickImage(source: source);
      setState(() {
        _imageFile = pickedFile;
        print("_imageFile=${pickedFile?.path}");
      });
    }
  }

  Future<void> _playVideo(XFile? file) async {
    if (file != null && mounted) {
      await _disposeVideoController();
      late VideoPlayerController controller;
      if (kIsWeb) {
        controller = VideoPlayerController.network(file.path);
      } else {
        controller = VideoPlayerController.file(File(file.path));
      }
      _controller = controller;
      final double volume = kIsWeb ? 0.0 : 1.0;
      await controller.setVolume(volume);
      await controller.initialize().then((value) {
        print("initialize...");
      });
      await controller.setLooping(true);
      await controller.play();
      setState(() {});
    }
  }

  Future<void> _disposeVideoController() async {
    if (_toBeDisposed != null) {
      await _toBeDisposed!.dispose();
    }
    _toBeDisposed = _controller;
    _controller = null;
  }

  Widget _previewVideo() {
    final Text? retrieveError = _getRetrieveErrorWidget();
    if (retrieveError != null) {
      return retrieveError;
    }
    if (_controller == null) {
      return const Text(
        'You have not yet picked a video',
        textAlign: TextAlign.center,
      );
    }
    return Padding(
      padding: const EdgeInsets.all(10.0),
      child: AspectRatioVideo(_controller),
    );
  }

  Widget _previewImages() {
    final Text? retrieveError = _getRetrieveErrorWidget();
    if (retrieveError != null) {
      return retrieveError;
    }
    if (_imageFileList != null) {
      return Semantics(
          child: ListView.builder(
            key: UniqueKey(),
            itemBuilder: (context, index) {
              return Semantics(
                label: 'image_picker_example_picked_image',
                child: kIsWeb
                    ? Image.network(_imageFileList![index].path)
                    : Image.file(File(_imageFileList![index].path)),
              );
            },
            itemCount: _imageFileList!.length,
          ),
          label: 'image_picker_example_picked_images');
    } else if (_pickImageError != null) {
      return Text(
        'Pick image error: $_pickImageError',
        textAlign: TextAlign.center,
      );
    } else {
      return const Text(
        'You have not yet picked an image.',
        textAlign: TextAlign.center,
      );
    }
  }

  Widget _handlePreview() {
    if (isVideo) {
      return _previewVideo();
    } else {
      return _previewImages();
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title!),
      ),
      body: Center(
        child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android
            ? FutureBuilder<void>(
                future: retrieveLostData(),
                builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
                  switch (snapshot.connectionState) {
                    case ConnectionState.none:
                    case ConnectionState.waiting:
                      return const Text(
                        'You have not yet picked an image.',
                        textAlign: TextAlign.center,
                      );
                    case ConnectionState.done:
                      return _handlePreview();
                    default:
                      if (snapshot.hasError) {
                        return Text(
                          'Pick image/video error: ${snapshot.error}}',
                          textAlign: TextAlign.center,
                        );
                      } else {
                        return const Text(
                          'You have not yet picked an image.',
                          textAlign: TextAlign.center,
                        );
                      }
                  }
                },
              )
            : _handlePreview(),
      ),
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          Semantics(
            label: 'image_picker_example_from_gallery',
            child: FloatingActionButton(
              onPressed: () {
                isVideo = false;
                _onImageButtonPressed(ImageSource.gallery, context: context);
              },
              heroTag: 'image0',
              tooltip: 'Pick Image from gallery',
              child: const Icon(Icons.photo),
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(top: 16.0),
            child: FloatingActionButton(
              onPressed: () {
                isVideo = false;
                _onImageButtonPressed(
                  ImageSource.gallery,
                  context: context,
                  isMultiImage: true,
                );
              },
              heroTag: 'image1',
              tooltip: 'Pick Multiple Image from gallery',
              child: const Icon(Icons.photo_library),
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(top: 16.0),
            child: FloatingActionButton(
              onPressed: () {
                isVideo = false;
                _onImageButtonPressed(ImageSource.camera, context: context);
              },
              heroTag: 'image2',
              tooltip: 'Take a Photo',
              child: const Icon(Icons.camera_alt),
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(top: 16.0),
            child: FloatingActionButton(
              backgroundColor: Colors.red,
              onPressed: () {
                isVideo = true;
                _onImageButtonPressed(ImageSource.gallery);
              },
              heroTag: 'video0',
              tooltip: 'Pick Video from gallery',
              child: const Icon(Icons.video_library),
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(top: 16.0),
            child: FloatingActionButton(
              backgroundColor: Colors.red,
              onPressed: () {
                isVideo = true;
                _onImageButtonPressed(ImageSource.camera);
              },
              heroTag: 'video1',
              tooltip: 'Take a Video',
              child: const Icon(Icons.videocam),
            ),
          ),
        ],
      ),
    );
  }

  Text? _getRetrieveErrorWidget() {
    if (_retrieveDataError != null) {
      final Text result = Text(_retrieveDataError!);
      _retrieveDataError = null;
      return result;
    }
    return null;
  }

  Future<void> retrieveLostData() async {
    final LostDataResponse response = await _picker.retrieveLostData();
    if (response.isEmpty) {
      return;
    }
    if (response.file != null) {
      if (response.type == RetrieveType.video) {
        isVideo = true;
        await _playVideo(response.file);
      } else {
        isVideo = false;
        setState(() {
          _imageFile = response.file;
          _imageFileList = response.files;
        });
      }
    } else {
      _retrieveDataError = response.exception!.code;
    }
  }
}

typedef void OnPickImageCallback(double? maxWidth, double? maxHeight, int? quality);

class AspectRatioVideo extends StatefulWidget {
  AspectRatioVideo(this.controller);

  final VideoPlayerController? controller;

  [@override](/user/override)
  AspectRatioVideoState createState() => AspectRatioVideoState();
}

class AspectRatioVideoState extends State<AspectRatioVideo> {
  VideoPlayerController? get controller => widget.controller;
  bool initialized = false;

  void _onVideoControllerUpdate() {
    if (!mounted) {
      return;
    }
    if (initialized != controller!.value.isInitialized) {
      initialized = controller!.value.isInitialized;
      setState(() {});
    }
  }

  [@override](/user/override)
  void initState() {
    super.initState();
    controller!.addListener(_onVideoControllerUpdate);
  }

  [@override](/user/override)
  void dispose() {
    controller!.removeListener(_onVideoControllerUpdate);
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    if (initialized) {
      return Center(
        child: AspectRatio(
          aspectRatio: controller!.value.aspectRatio,
          child: VideoPlayer(controller!),
        ),
      );
    } else {
      return Container();
    }
  }
}

更多关于Flutter图片选择插件sparrow_image_picker的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter图片选择插件sparrow_image_picker的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


sparrow_image_picker 是一个用于 Flutter 的图片选择插件,它可以帮助开发者在应用中方便地从相册或相机中选择图片。以下是如何使用 sparrow_image_picker 插件的基本步骤:

1. 添加依赖

首先,在 pubspec.yaml 文件中添加 sparrow_image_picker 依赖:

dependencies:
  flutter:
    sdk: flutter
  sparrow_image_picker: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

2. 导入插件

在你的 Dart 文件中导入 sparrow_image_picker

import 'package:sparrow_image_picker/sparrow_image_picker.dart';

3. 使用插件选择图片

从相册选择图片

你可以使用 pickImage 方法从相册中选择图片:

Future<void> pickImageFromGallery() async {
  final imagePicker = SparrowImagePicker();
  final pickedFile = await imagePicker.pickImage(source: ImageSource.gallery);

  if (pickedFile != null) {
    // 处理选中的图片
    print('Image path: ${pickedFile.path}');
  } else {
    print('No image selected.');
  }
}

从相机拍摄图片

你也可以使用 pickImage 方法从相机拍摄图片:

Future<void> takePhoto() async {
  final imagePicker = SparrowImagePicker();
  final pickedFile = await imagePicker.pickImage(source: ImageSource.camera);

  if (pickedFile != null) {
    // 处理拍摄的图片
    print('Image path: ${pickedFile.path}');
  } else {
    print('No image taken.');
  }
}

4. 显示选中的图片

你可以使用 Image.file 来显示选中的图片:

Image.file(File(pickedFile.path))

5. 处理权限

在使用相机或访问相册时,可能需要处理权限问题。确保在 AndroidManifest.xmlInfo.plist 文件中添加相应的权限声明。

Android

AndroidManifest.xml 中添加以下权限:

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

iOS

Info.plist 中添加以下权限:

<key>NSCameraUsageDescription</key>
<string>需要相机权限来拍摄照片</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问相册以选择照片</string>

6. 完整示例

以下是一个完整的示例,展示如何使用 sparrow_image_picker 从相册或相机中选择图片并显示:

import 'package:flutter/material.dart';
import 'package:sparrow_image_picker/sparrow_image_picker.dart';
import 'dart:io';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ImagePickerExample(),
    );
  }
}

class ImagePickerExample extends StatefulWidget {
  [@override](/user/override)
  _ImagePickerExampleState createState() => _ImagePickerExampleState();
}

class _ImagePickerExampleState extends State<ImagePickerExample> {
  File? _image;

  Future<void> pickImageFromGallery() async {
    final imagePicker = SparrowImagePicker();
    final pickedFile = await imagePicker.pickImage(source: ImageSource.gallery);

    setState(() {
      if (pickedFile != null) {
        _image = File(pickedFile.path);
      } else {
        print('No image selected.');
      }
    });
  }

  Future<void> takePhoto() async {
    final imagePicker = SparrowImagePicker();
    final pickedFile = await imagePicker.pickImage(source: ImageSource.camera);

    setState(() {
      if (pickedFile != null) {
        _image = File(pickedFile.path);
      } else {
        print('No image taken.');
      }
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Image Picker Example'),
      ),
      body: Center(
        child: _image == null
            ? Text('No image selected.')
            : Image.file(_image!),
      ),
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: pickImageFromGallery,
            tooltip: 'Pick Image',
            child: Icon(Icons.photo_library),
          ),
          SizedBox(height: 10),
          FloatingActionButton(
            onPressed: takePhoto,
            tooltip: 'Take Photo',
            child: Icon(Icons.camera_alt),
          ),
        ],
      ),
    );
  }
}
回到顶部