48

I am really confused. Flutter is awesome but some time is stuck the mind

All the code are done. selected file also showing in preview but I try to save that file in local android storage. I can't get success in

  Future getImage(ImageSource imageSource) async {
    var image = await ImagePicker.pickImage(source: imageSource);

    setState(() {
      _image = image;
    });
  } 

Select file using this code and my file in _image now I try to store using path_provider and dart.io but I can't get save methodology.

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
Ravindra Bhanderi
  • 2,478
  • 4
  • 17
  • 29

10 Answers10

72

Using await ImagePicker.pickImage(...), you are already on the right track because the function returns a File.

The File class has a copy method, which you can use to copy the file (which is already saved on disk by either the camera or by lying in gallery) and put it into your application documents directory:

// using your method of getting an image
final File image = await ImagePicker.pickImage(source: imageSource);

// getting a directory path for saving
final String path = await getApplicationDocumentsDirectory().path;

// copy the file to a new path
final File newImage = await image.copy('$path/image1.png');

setState(() {
  _image = newImage;
});

You should also note that you can get the path of the image file from ImagePicker using image.path, which will also contain the file ending that you might want to extract and you can save your image path by using newImage.path.

creativecreatorormaybenot
  • 114,516
  • 58
  • 291
  • 402
  • 5
    How can I retrieve this file and display later to the user? Which file read operation can I use? – krishnakumarcn Aug 06 '18 at 10:11
  • 2
    We can save "Path" of the file as a string and later on can retrieve the file using a path like this File(path). Use this handy method to load the images FileImage(File(path)). – Vivek Bansal Mar 22 '19 at 15:06
  • 1
    @creativecreatorormaybenot I should have been more specific: does that add the photo to the camera roll on iOS? – qwertzguy Apr 08 '19 at 16:02
  • somebody has a complete example. I can't achieve it – Luiggi Sep 11 '19 at 11:30
  • @creativecreatorormaybenot you wrote the image taken is automatically saved to the gallery, but it does not happen to me. You know which could be the problem? – Giacomo M Nov 21 '19 at 08:37
  • @creativecreatorormaybenot, everything works fine but my file does not get renamed to 'image1.png'. whats the way out bro? thank you – Speedy11 Nov 24 '19 at 10:15
  • but what if image is not taken from camera and gallery ? https://stackoverflow.com/questions/59852587/how-to-convert-image-to-file – John Joe Jan 22 '20 at 04:26
  • Sorry but you need to add path_provider: ^1.6.9 to your pubspec.yaml in order to have this work. https://pub.dev/packages/path_provider – Bliv_Dev May 29 '20 at 00:20
37

@creativecreatorormaybenot answer is really helpful but it missed one important part i.e retrieving the image for later use.

Saving Image

// Step 1: Retrieve image from picker 
final XFile? image = await ImagePicker().pickImage(source: ImageSource.gallery);

// Step 2: Check for valid file
if (image == null) return;

// Step 3: Get directory where we can duplicate selected file.
final String duplicateFilePath = await getApplicationDocumentsDirectory().path;

// Step 4: Copy the file to a application document directory. 
final var fileName = basename(file.path);
final File localImage = await image.saveTo('$duplicateFilePath/$fileName');

Tip: you can retrieve file name from original file using basename(file.path). Make sure you import 'package:path/path.dart';

Retrieving/Loading Image

// Step 1: Save image/file path as string either db or shared pref
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('test_image', localImage.path)

// Step 2: Loading image by using the path that we saved earlier. We can create a file using path 
//         and can use FileImage provider for loading image from file.
CircleAvatar(
          backgroundImage: FileImage(File(prefs.getString('test_image')),
          radius: 50,
          backgroundColor: Colors.white)
Vivek Bansal
  • 1,301
  • 13
  • 21
  • how can multiple images select and temporary show in one activity, also user can remove selected images and add more images again? if you have any source code about my case please help me – Shadeeka Nimesh Apr 18 '19 at 10:32
  • ShadeekaNimesh, I don't have logic for multiple images you can check on image_picker plugin documentation. – Vivek Bansal Apr 23 '19 at 18:30
  • but what if image is not taken from camera and gallery ?https://stackoverflow.com/questions/59852587/how-to-convert-image-to-file – John Joe Jan 22 '20 at 04:27
  • @VivekBansal I tried implementing your solution but ` final String path = await getApplicationDocumentsDirectory().path;` gives me problems. It says `path` is not defined for `getAppl..`. What am I missing here? – Chris Aug 28 '21 at 11:45
  • @Chris use this import 'package:path_provider/path_provider.dart'; – Vivek Bansal Aug 28 '21 at 17:00
  • @Chris Maybe add parentheses like so: (await getApplicationDocumentsDirectory()).path ? – Kasia K. Jan 04 '23 at 13:58
  • The saveTo method returns void so this answer won't work – alfietap Apr 11 '23 at 06:12
19

As of image_picker 0.6.7

pickImage, pickVideo and retrieveLostData are deprecated.
https://pub.dev/packages/image_picker#-changelog-tab-

Those methods have to be replaced by

  • getImage
  • getVideo
  • getLostData

An example usage of the getImage() method:

...
File _storedImage;
...

void _takePicture() async {
  // 1. Create an ImagePicker instance.
  final ImagePicker _picker = ImagePicker();

  // 2. Use the new method.
  //
  // getImage now returns a PickedFile instead of a File (form dart:io)
  final PickedFile pickedImage = await _picker.getImage(...)

  // 3. Check if an image has been picked or take with the camera.
  if (pickedImage == null) {
    return;
  }

  // 4. Create a File from PickedFile so you can save the file locally
  // This is a new/additional step.
  File tmpFile = File(pickedFile.path);

  // 5. Get the path to the apps directory so we can save the file to it.
  final String path = await getApplicationDocumentsDirectory().path;
  final String fileName = basename(pickedFile.path); // Filename without extension
  final String fileExtension = extension(pickedFile.path); // e.g. '.jpg'

  // 6. Save the file by copying it to the new location on the device.
  tmpFile = await tmpFile.copy('$path/$fileName$fileExtension');

  // 7. Optionally, if you want to display the taken picture we need to update the state
  // Note: Copying and awaiting the file needs to be done outside the setState function.
  setState(() => _storedImage = tmpFile);
}

A slightly more compact example:

File _image;
final picker = ImagePicker();

Future getImage() async {
  final File pickedImage = await picker.getImage(source: ImageSource.camera);

  if (pickedImage == null) return;

  File tmpFile = File(pickedImage.path);
  tmpFile = await tmpFile.copy(tmpFile.path);

  setState(() {
    _image = tmpFile;
  });
}
Ilker Cat
  • 1,862
  • 23
  • 17
  • Thank you for using the new methods. There's a slight syntactic error. You meant to write pickedFile instead of pickedImage. Also, I tried your method, and it takes the image, but do not save it anywhere. Oddly, the second block of code, shows the emulator to check the taken image and delete, but the first block of code, gives you the option to cancel or go back to taking the image. They were not equivalent. Please update as I need to implement this in my code. – MAA Jul 13 '20 at 06:19
7

Hope to help and see people。give a like。

RepaintBoundary will help you。

final GlobalKey _repaintKey = new GlobalKey();


//  Image Widget


Widget _buildQrImage() {
_avatar = RepaintBoundary(
  key: _repaintKey,
  child: Image.asset('assets/ifredom.jpg')
);

return Column(
  children: <Widget>[
    _avatar,
    (imageFile == null)
        ? Image.asset('assets/default.jpg')
        : Image.file(imageFile),
    FlatButton(
      child: Text("save"),
      onPressed: () {
        _saveScreenShot(context);
      },
    ),

  ],
);
}



void _saveScreenShot(BuildContext context) {
RenderRepaintBoundary boundary = _repaintKey.currentContext.findRenderObject();

// ScreenShot and save
saveScreenShot(boundary, success: () {
  saveScreenShot2SDCard(boundary, success: () {
    showToast('save ok');
  }, fail: () {
    showToast('save ok');
  });
}, fail: () {
  showToast('save fail!');
});
}

this file is utils.

Flutter provides a RepaintBoundaryWidget to implement the screenshot function.

RepaintBoundary is used to wrap the part that needs to be intercepted.

RenderRepaintBoundary can be used to intercept the part that is wrapped by RepaintBoundary.

Then it is converted into a ui.Image object by using the boundary.toImage () method, and then image.toByteData ( ) Convert the image to byteData;

finally store it as a file object via File (). WriteAsBytes ():

import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui' as ui;

import 'package:flutter/rendering.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';

import 'package:oktoast/oktoast.dart';

final String scrawlImagePath = '/screen_shot_scraw.png';


Future<File> getScreenShotFile() async {
  Directory tempDir = await getTemporaryDirectory();
  String tempPath = '${tempDir.path}$scrawlImagePath';
  File image = File(tempPath);
  bool isExist = await image.exists();
  return isExist ? image : null;
}

Future saveScreenShot2SDCard(RenderRepaintBoundary boundary,
    {Function success, Function fail}) async {
  // check storage permission.
  PermissionHandler().requestPermissions([PermissionGroup.storage]).then((map) {
    if (map[PermissionGroup.storage] == PermissionStatus.granted) {
      capturePng2List(boundary).then((uint8List) async {
        if (uint8List == null || uint8List.length == 0) {
          if (fail != null) fail();
          return;
        }
        Directory tempDir = await getExternalStorageDirectory();
        _saveImage(uint8List, Directory('${tempDir.path}/flutter_ui'),
            '/screen_shot_scraw_${DateTime.now()}.png',
            success: success, fail: fail);
      });
    } else {
      showToast('请打开SD卡存储权限!');
//      if (fail != null) fail();
      return;
    }
  });
}

void saveScreenShot(RenderRepaintBoundary boundary,
    {Function success, Function fail}) {
  capturePng2List(boundary).then((uint8List) async {
    if (uint8List == null || uint8List.length == 0) {
      if (fail != null) fail();
      return;
    }
    Directory tempDir = await getTemporaryDirectory();
    _saveImage(uint8List, tempDir, scrawlImagePath,
        success: success, fail: fail);
  });
}

void _saveImage(Uint8List uint8List, Directory dir, String fileName,
    {Function success, Function fail}) async {
  bool isDirExist = await Directory(dir.path).exists();
  if (!isDirExist) Directory(dir.path).create();
  String tempPath = '${dir.path}$fileName';
  File image = File(tempPath);
  bool isExist = await image.exists();
  if (isExist) await image.delete();
  File(tempPath).writeAsBytes(uint8List).then((_) {
    if (success != null) success();
  });
}

Future<Uint8List> capturePng2List(RenderRepaintBoundary boundary) async {
  ui.Image image =
      await boundary.toImage(pixelRatio: ui.window.devicePixelRatio);
  ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
  Uint8List pngBytes = byteData.buffer.asUint8List();
  return pngBytes;
}
ifredom
  • 979
  • 9
  • 6
5

To save the file locally, we need to add some dependencies

dependencies:
  flutter:
    sdk: flutter
  path_provider:
  path:

path_provider

Finds the correct paths to store images.

path

Creates paths that work on any platform.


Example:

final pickedFile = await picker.getImage(source: ImageSource.camera);
_image = File(pickedFile.path);

// getting a directory path for saving
final Directory extDir = await getApplicationDocumentsDirectory();
String dirPath = extDir.path;
final String filePath = '$dirPath/image.png';

// copy the file to a new path
final File newImage = await _image.copy(filePath);
setState(() {
  if (pickedFile != null) {
    _image = newImage;
  } else {
    print('No image selected.');
  }
});
Jitesh Mohite
  • 31,138
  • 12
  • 157
  • 147
3

The following code is null safe and stores the image in the external storage of the device which can be found at /storage/emulated/0/Android/data/<your-package>/files/name.png.

Create this method:

Future<File?> captureAndSaveImage() async {
  final pickedImage = await ImagePicker().getImage(source: ImageSource.camera);
  if (pickedImage == null) return null;
  
  try {
    final directory = await getExternalStorageDirectory();
    if (directory != null) return File(pickedImage.path).copy('${directory.path}/name.png');
  } catch (e) {
    return null;
  }
}

Usage:

File? file = await captureAndSaveImage();
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
3

I had some issues with the above answers due to the libraries updates and the below code should be working without a problem.

import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart' as Path;
import 'package:path_provider/path_provider.dart';


  final XFile? image =
      await _picker!.pickImage(source: ImageSource.gallery);
  File imageFile = File(image!.path);
  Directory appDocDir =
      await getApplicationDocumentsDirectory();
  String appDocPath = appDocDir.path;
  final fileName = Path.basename(imageFile.path);
  final File localImage =
      await imageFile.copy('$appDocPath/$fileName');
  setState(() {
    pickedImagePath= image.path;
  });
0

Here is the updated code:

void getImage({required ImageSource source}) async {
    final XFile? file = await ImagePicker().pickImage(
        source: source, maxWidth: 640, maxHeight: 480, imageQuality: 70 //0-100
        );
    // getting a directory path for saving
    final Directory path = await getApplicationDocumentsDirectory();
    final String imgpath = path.path;
    // File temp = file as File;
    String date = DateFormat("yyyy_MM_dd_hh_mm_ss").format(DateTime.now());
    await file!.saveTo('$imgpath/$date.jpeg');

    if (file?.path != null) {
      setState(() {
        imageFile = File(file.path);
        // imageFile = newImage;
      });
    }
  }
Adriaan
  • 17,741
  • 7
  • 42
  • 75
  • Please [edit] your post to add code and data as text ([using code formatting](https://stackoverflow.com/editing-help#code)), not images. Images: A) don't allow us to copy-&-paste the code/errors/data for testing; B) don't permit searching based on the code/error/data contents; and [many more reasons](https://meta.stackoverflow.com/a/285557). Images should only be used, in addition to text in code format, if having the image adds something significant that is not conveyed by just the text code/error/data. See [mcve] on what code is required. – Adriaan Nov 02 '22 at 07:43
0

This is the updated code, referring to creativecreatorormaybenot's answer above. I also had a problem with saving image data, but with the above answer and my developed code, I could solve the problem! Hope my code works for whom has the same problem :)

Future<dynamic> previewImage(ImageSource imageSource) async {

try {
  // save the selected image in a variable from ImagePicker
  final pickedFile = await ImagePicker().pickImage(source: imageSource);

  // save the image <File> in a variable
  if (pickedFile != null) {
    _storeImage = File(pickedFile.path);

    // use getApplicationDocumentsDirectory() to get a directory inside the app
    final appDir = await getApplicationDocumentsDirectory();
    // get the image's directory
    final fileName = basename(pickedFile.path);
    
    // copy the image's whole directory to a new <File>
    final File localImage =
        await _storeImage.copy('${appDir.path}/$fileName');

return localImage;
-1

Due to the libraries updates , you should use XFile and the below code will work :

Future<void> getUserImage() async {
    XFile? profileimage;
    final pickedFile = await picker.pickImage(source: ImageSource.gallery);
    if (pickedFile != null) {
      profileimage = await XFile(pickedFile.path.);
    } else {
      print("No image selected");
    }
  }