image_form_field 0.0.2

  • Readme
  • Changelog
  • Example
  • Installing
  • 72

ImageFormField #

Handle image uploads in a Flutter Form.

Usage #

In order to fully customize the photo upload field, several callbacks and classes are required. In most cases, you will be mixing photos from a remote source and a local upload. For these, an adapter layer is useful:

class ImageInputAdapter {
  /// Initialize from either a URL or a file, but not both.
  }) : assert(file != null || url != null), assert(file != null && url == null), assert(file == null && url != null);

  /// An image file
  final File file;
  /// A direct link to the remote image
  final String url;

  /// Render the image from a file or from a remote source.
  Widget widgetize() {
    if (file != null) {
      return Image.file(file);
    } else {
      return FadeInImage(
        image: NetworkImage(url),
        placeholder: AssetImage("assets/images/placeholder.png"),
        fit: BoxFit.contain,

Finally, in a Flutter Form:

import 'package:image_form_field/image_form_field.dart';

  previewImageBuilder: (_, ImageInputAdapter image) =>
  buttonBuilder: (_, int count) =>
      child: Text(
        count == null || count < 1 ? "Upload Image" : "Upload More"
  initializeFileAsImage: (File file) =>
    ImageInputAdapter(file: file),
  initialValue: existingPhotoUrl == null ? null : (List<ImageInputImageAdapter>()..add(ImageInputImageAdapter(url: existingPhotoUrl))),
  // Even if `shouldAllowMultiple` is true, images will always be a `List` of the declared type (i.e. `ImageInputAdater`).
  onSaved: (images) _images = images,

For a full example that includes uploading an image, see example/lib/main.dart.

Parameters #

(T == declared display type, i.e. ImageFormField<T>)

previewImageBuilderWidget Function(BuildContext, T)*How the image is rendered below the upload button
buttonBuilderWidget Function(BuildContext, [int])*The display of the button. Do not use FlatButton; the button is already wrapped in a GestureRecognizer
initializeFileAsImageT Function(File)*Convert an upload to the adapter class
controllerImageFieldControllerDirect access to the images currently displayed or uploaded
initialValueListImages displayed on initial render; if initialValue is set in initState or by some other non-pass through method, do not render the field until the value is set.
onSavedVoidCallback Function(ListHandle the uploaded/remote images when the form is saved
validatorVoidCallback Function(ListHandle the uploaded/remote images when the form is validated
errorTextStyleTextStyleControl how text display when field is invalid; often it's best to use Theme.of(context).inputDecorationTheme.errorStyle
autoValidateboolIf field should autovalidate (defaults to false)
shouldAllowMultipleboolIf field permits more than one image upload (defaults to true)

Thanks #

Props to AllGo for providing the initial support for this project.

Unreleased #

[0.0.2] - 27 September 2018

  • Apply recommended formatting via flutter format
  • Add CHANGELOG (mettttaaaa)

[0.0.1] - 24 September 2018

  • Custom button builder and custom image display
  • Grab images via onSaved like other form fields
  • Error validation (validator / autovalidate)
  • Modal bottom sheet to select gallery or camera
  • Multiple image upload
  • Initial


import 'package:flutter/material.dart';

import 'package:image_form_field/image_form_field.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

import 'upload_button.dart';
import 'image_input_adapter.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Image Demo',
      home: new MyHomePage(),

class BlogImage {
  const BlogImage({
    @required this.storagePath,
    @required this.originalUrl,
    @required this.bucketName,

  final String storagePath;
  final String originalUrl;
  final String bucketName;
  final String id;

  static String get collectionPath => "blogImages";

  create() {
    return Firestore.instance.collection(collectionPath).document().setData({
      "storagePath" : storagePath,
      "originalUrl" : originalUrl,
      "bucketName" : bucketName

  static Future<BlogImage> fromUrl(String url) async {
    final images = await Firestore.instance.collection(collectionPath)
      .where("originalUrl", isEqualTo: url)

    if (images.documents.isNotEmpty) {
      final i =;

      return BlogImage(
        storagePath: i["storagePath"],
        originalUrl: i["originalUrl"],
        bucketName: i["bucketName"],
        id: images.documents.first.documentID

    return null;

  Future delete() async {
    return Firestore.instance.collection(collectionPath).document(id).delete();

class _UploadForm extends StatefulWidget {

  final List<BlogImage> existingImages;

  State<StatefulWidget> createState() => _UploadFormState();

class _UploadFormState extends State<_UploadForm> {
  final _formKey = GlobalKey<FormState>();
  List<ImageInputAdapter> _images;

  void submit() {
    if( _formKey.currentState.validate() ) {;
      var snackbarText = "Upload successful";

      try {
        // New images
          ?.where((i) => i.isFile)
          ?.forEach((i) async {
            final photo = await;

              storagePath: photo.refPath,
              originalUrl: photo.originalUrl,
              bucketName: photo.bucketName

        // Removed images
          ?.where((r) =>
            !_images.any((m) => m.url == r.originalUrl)
          ?.forEach((i) {
            BlogImage.fromUrl(i.originalUrl).then((b) => b?.delete());

      } catch(e) {
        snackbarText = "Couldn't save. Please try again later.";
      } finally {
            content: Text(snackbarText)

  Widget build(BuildContext context) {
    final bool shouldAllowMultiple = true;

    return Form(
      key: _formKey,
      child: ListBody(
        children: [
            shouldAllowMultiple: shouldAllowMultiple,
            onSaved: (val) => _images = val,
            initialValue: => ImageInputAdapter(url: i.originalUrl)).toList().cast<ImageInputAdapter>(),
            initializeFileAsImage: (file) =>
              ImageInputAdapter(file: UploadableImage(file, storagePath: "appImages")),
            buttonBuilder: (_, count) =>
                count: count,
                shouldAllowMultiple: shouldAllowMultiple
            previewImageBuilder: (_, image) => image.widgetize()
            onPressed: submit,
            child: const Text("Update Profile")

class MyHomePage extends StatelessWidget {
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: const Text("Upload Images")
      body: SingleChildScrollView(
        // Provide existing images as the first argument
        child: _UploadForm(List<BlogImage>())

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:

  image_form_field: ^0.0.2

2. Install it

You can install packages from the command line:

with Flutter:

$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:

import 'package:image_form_field/image_form_field.dart';
Describes how popular the package is relative to other packages. [more]
Code health derived from static analysis. [more]
Reflects how tidy and up-to-date the package is. [more]
Weighted score of the above. [more]
Learn more about scoring.

We analyzed this package on Jan 25, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.7.0
  • pana: 0.13.4
  • Flutter: 1.12.13+hotfix.5

Health suggestions

Format lib/src/preview.dart.

Run flutter format to format lib/src/preview.dart.

Maintenance issues and suggestions

Support latest dependencies. (-10 points)

The version constraint in pubspec.yaml does not support the latest published versions for 1 dependency (image_picker).

Package is getting outdated. (-32.60 points)

The package was last published 69 weeks ago.

The package description is too short. (-20 points)

Add more detail to the description field of pubspec.yaml. Use 60 to 180 characters to describe the package, what it does, and its target use case.

Package is pre-v0.1 release. (-10 points)

While nothing is inherently wrong with versions of 0.0.*, it might mean that the author is still experimenting with the general direction of the API.


Package Constraint Resolved Available
Direct dependencies
Dart SDK >=1.20.1 <3.0.0
flutter 0.0.0
image_picker ^0.4.10 0.4.12+1 0.6.3+1
Transitive dependencies
collection 1.14.11 1.14.12
meta 1.1.8
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies