Flutter Guide By Flutter Agency - ImagePicker widget

ImagePicker Widget – Flutter Guide By Flutter Agency

In this article, we are going to learn ImagePicker Widget to pick images from the gallery in a flutter.

What is ImagePicker Widget?

A Flutter plugin library for both iOS and Android that is been used to pick an image from a mobile phone gallery or even you can take a new photo with the camera.

Let’s straight away start to implement the flutter ImagePicker Widget in our flutter app.

Get the latest version of the ImagePicker Widget from the official website. When I am writing this article we are using the plugin with version ” image_picker 0.8.5+3“. Let’s follow instructions as per the official document. If there are any changes that occur in the future we need to implement the plugin with new and latest instructions.

Now, put this ” image_picker 0.8.5+3 ” plugin into pubspec.yaml under dependencies.

Step 1: Adding dependencies to project

dependencies:
   image_picker 0.6.7

Step 2: Import image_picker.dart

Now, as we have added the required library in our project now users need to import the picker library wherever required.

import 'package:image_picker/image_picker.dart';

Ways to implement ImagePicker Widget will defer depending on the type of platform the user is implementing.

For Android:

For Android with API 29+ no more configuration required – the plugin should work like a charm.

If a user has API < 29 then follow instructions like below :

Add android:requestLegacyExternalStorage=”true” as an attribute to the tag in AndroidManifest.xml.The attribute is false by default on apps targeting Android Q.

Create a FloatingActionButton Widget which is used to choose an image from the gallery or choose a camera.

The code snippet will look like below :

floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          FloatingActionButton(
            onPressed: () {
              isVideo = false;
              _onImageButtonPressed(
                ImageSource.gallery,
                context: context,
              );
            },
            heroTag: 'image0',
            tooltip: 'Pick 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: 'image1',
              tooltip: 'Take a Photo',
              child: const Icon(Icons.camera_alt),
            ),
          ),
        ],
      ),

We will get a FAB(FloatingActionButton) button like below :

Image Picker FAB-Button

Image Picker

In this example when users click on FAB Button user will get a dialog where users can enter the information like maxWidth, maxHeight, Quality parameters. into TextField Widget  The code snippet will look like below :

    Future<void> _displayPickImageDialog(
        BuildContext context, OnPickImageCallback onPick) async {
      return showDialog(
          context: context,
          builder: (context) {
            return AlertDialog(
              title: Text('Add optional parameters'),
              content: Column(
                children: <Widget>[
                  TextField(
                    controller: maxWidthController,
                    keyboardType: TextInputType.numberWithOptions(decimal: true),
                    decoration:
                        InputDecoration(hintText: "Enter maxWidth if desired"),
                  ),
                  TextField(
                    controller: maxHeightController,
                    keyboardType: TextInputType.numberWithOptions(decimal: true),
                    decoration:
                        InputDecoration(hintText: "Enter maxHeight if desired"),
                  ),
                  TextField(
                    controller: qualityController,
                    keyboardType: TextInputType.number,
                    decoration:
                        InputDecoration(hintText: "Enter quality if desired"),
                  ),
                ],
              ),
              actions: <Widget>[
                FlatButton(
                  child: const Text('CANCEL'),
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                ),
                FlatButton(
                    child: const Text('PICK'),
                    onPressed: () {
                      double width = maxWidthController.text.isNotEmpty
                          ? double.parse(maxWidthController.text)
                          : null;
                      double height = maxHeightController.text.isNotEmpty
                          ? double.parse(maxHeightController.text)
                          : null;
                      int quality = qualityController.text.isNotEmpty
                          ? int.parse(qualityController.text)
                          : null;
                      onPick(width, height, quality);
                      Navigator.of(context).pop();
                    }),
              ],
            );
          });
    }
  }

We will get output like below.

Dialog Button

When users click on Pick Button we will call _onImageButtonPressed()

  void _onImageButtonPressed(ImageSource source, {BuildContext context}) async {
    if (_controller != null) {
      await _controller.setVolume(0.0);
    }
    await _displayPickImageDialog(context,
        (double maxWidth, double maxHeight, int quality) async {
      try {
        final pickedFile = await _picker.getImage(
          source: source,
          maxWidth: maxWidth,
          maxHeight: maxHeight,
          imageQuality: quality,
        );
        setState(() {
          _imageFile = pickedFile;
        });
      } catch (e) {
        setState(() {
          _pickImageError = e;
        });
      }
    });
    // }
  }

Once the user clicks on the pick button after inserting desire height/width and quality can select an image from the gallery or camera image will be shown to the user like the below screenshot.

Picked-Image

Uploaded Image

Complete source code for this is as below :

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

  final String? title;

  @override
  _ImagePickerScreenState createState() => _ImagePickerScreenState();
}

class _ImagePickerScreenState extends State {
  PickedFile? _imageFile;

  dynamic _pickImageError;
  bool isVideo = false;
  VideoPlayerController? _controller;
  String? _retrieveDataError;

  final ImagePicker _picker = ImagePicker();
  final TextEditingController maxWidthController = TextEditingController();
  final TextEditingController maxHeightController = TextEditingController();
  final TextEditingController qualityController = TextEditingController();

  Future _playVideo(PickedFile file) async {
    if (file != null && mounted) {
      await _disposeVideoController();
      _controller = VideoPlayerController.file(File(file.path));
      await _controller?.setVolume(1.0);
      await _controller?.initialize();
      await _controller?.setLooping(true);
      await _controller?.play();
      setState(() {});
    }
  }

  void _onImageButtonPressed(ImageSource source,
      {required BuildContext context}) async {
    if (_controller != null) {
      await _controller?.setVolume(0.0);
    }
    await _displayPickImageDialog(context,
        (double maxWidth, double maxHeight, int quality) async {
      try {
        final pickedFile = await _picker.getImage(
          source: source,
          maxWidth: maxWidth,
          maxHeight: maxHeight,
          imageQuality: quality,
        );
        setState(() {
          _imageFile = pickedFile;
        });
      } catch (e) {
        setState(() {
          _pickImageError = e;
        });
      }
    });
  }

  @override
  void deactivate() {
    if (_controller != null) {
      _controller?.setVolume(0.0);
      _controller?.pause();
    }
    super.deactivate();
  }

  @override
  void dispose() {
    _disposeVideoController();
    maxWidthController.dispose();
    maxHeightController.dispose();
    qualityController.dispose();
    super.dispose();
  }

  Future _disposeVideoController() async {
    if (_controller != null) {
      await _controller?.dispose();
      _controller = null;
    }
  }

  Widget _previewImage() {
    final Text? retrieveError = _getRetrieveErrorWidget();
    if (retrieveError != null) {
      return retrieveError;
    }
    if (_imageFile != null) {
      return Image.file(File(_imageFile!.path));
    } 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,
      );
    }
  }

  Future retrieveLostData() async {
    final LostData response = await _picker.getLostData();
    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;
        });
      }
    } else {
      _retrieveDataError = response.exception?.code;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: defaultTargetPlatform == TargetPlatform.android
            ? FutureBuilder(
                future: retrieveLostData(),
                builder: (BuildContext context, AsyncSnapshot 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 _previewImage();
                    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,
                        );
                      }
                  }
                },
              )
            : (_previewImage()),
      ),
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: () {
              isVideo = false;
              _onImageButtonPressed(
                ImageSource.gallery,
                context: context,
              );
            },
            heroTag: 'image0',
            tooltip: 'Pick 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: 'image1',
              tooltip: 'Take a Photo',
              child: const Icon(Icons.camera_alt),
            ),
          ),
        ],
      ),
    );
  }

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

  Future _displayPickImageDialog(
      BuildContext context, OnPickImageCallback onPick) async {
    return showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text('Add optional parameters'),
            content: Column(
              children: [
                TextField(
                  controller: maxWidthController,
                  keyboardType: TextInputType.numberWithOptions(decimal: true),
                  decoration:
                      InputDecoration(hintText: "Enter maxWidth if desired"),
                ),
                TextField(
                  controller: maxHeightController,
                  keyboardType: TextInputType.numberWithOptions(decimal: true),
                  decoration:
                      InputDecoration(hintText: "Enter maxHeight if desired"),
                ),
                TextField(
                  controller: qualityController,
                  keyboardType: TextInputType.number,
                  decoration: const InputDecoration(
                      hintText: "Enter quality if desired"),
                ),
              ],
            ),
            actions: [
              TextButton(
                child: const Text('CANCEL'),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
              TextButton(
                  child: const Text('PICK'),
                  onPressed: () {
                    double? width = maxWidthController.text.isNotEmpty
                        ? double.parse(maxWidthController.text)
                        : null;
                    double? height = maxHeightController.text.isNotEmpty
                        ? double.parse(maxHeightController.text)
                        : null;
                    int? quality = qualityController.text.isNotEmpty
                        ? int.parse(qualityController.text)
                        : null;
                    onPick(width ?? 100, height ?? 100, quality ?? 100);
                    Navigator.of(context).pop();
                  }),
            ],
          );
        });
  }
}

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

Do not forget to comment if you need any assistance regarding this.
Keep Fluttering !!!

FlutterAgency.com is our portal Platform dedicated to Flutter Technology and Flutter Developers. The portal is full of cool resources from Flutter like Flutter Widget GuideFlutter ProjectsCode libs and etc.

FlutterAgency.com is one of the most popular online portal dedicated to Flutter Technology and daily thousands of unique visitors come to this portal to enhance their knowledge on Flutter.

Abhishek Dhanani

Written by Abhishek Dhanani

Abhishek Dhanani, a skilled software developer with 3+ years of experience, masters Dart, JavaScript, TypeScript, and frameworks like Flutter and NodeJS. Proficient in MySQL, Firebase, and cloud platforms AWS and GCP, he delivers innovative digital solutions.

Leave a comment

Your email address will not be published. Required fields are marked *

Discuss Your Project

Connect with Flutter Agency's proficient skilled team for your app development projects across different technologies. We'd love to hear from you! Fill out the form below to discuss your project.

Have Project For Us

Get in Touch

"*" indicates required fields

ready to get started?

Fill out the form below and we will be in touch soon!

"*" indicates required fields