close

how to upload image in flutter?

ImageFormField 

Handle image uploads in a Flutter Form.

Usage 

To completely tweak the photograph transfer field, a few callbacks and classes are required. Much of the time, you will blend photographs from a far off source and a nearby transfer. For these, a connector layer is helpful:

Readme
Changelog
Example
Installing
Versions
Scores
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.
  ImageInputAdapter({
    this.file,
    this.url
  }) : 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,
      );
    }
  }
}

At last, in a Flutter Form:

import 'package:image_form_field/image_form_field.dart';

ImageFormField<ImageInputAdapter>(
  previewImageBuilder: (_, ImageInputAdapter image) =>
    image.widgetize(),
  buttonBuilder: (_, int count) =>
    Container(
      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 model that incorporates transferring a picture,

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 {
  @override
  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,
    this.id
  });

  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)
      .getDocuments();

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

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

    return null;
  }

  Future delete() async {
    FirebaseStorage.instance.ref().child(storagePath).delete();
    return Firestore.instance.collection(collectionPath).document(id).delete();
  }
}

class _UploadForm extends StatefulWidget {
  _UploadForm(
    this.existingImages
  );

  final List<BlogImage> existingImages;

  @override
  State<StatefulWidget> createState() => _UploadFormState();
}

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

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

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

            BlogImage(
              storagePath: photo.refPath,
              originalUrl: photo.originalUrl,
              bucketName: photo.bucketName
            ).create();
          });

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

      } catch(e) {
        print(e);
        snackbarText = "Couldn't save. Please try again later.";
      } finally {
        Scaffold.of(context).showSnackBar(
          SnackBar(
            content: Text(snackbarText)
          )
        );
      }
    }
  }

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

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

class MyHomePage extends StatelessWidget {
  @override
  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>())
      ),
    );
  }
}

Parameters 

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

nametyperequireddescription
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 

Summery

It’s all About this issue. Hope all solution helped you a lot. Comment below Your thoughts and your queries. Also, Comment below which solution worked for you? Thank You.

Also Read

Leave a Comment