24

In Flutter how to save an image from network to the local directory.

I am new to encoding and decoding images. Can anyone point me in the right direction?

Aritra Dattagupta
  • 760
  • 1
  • 7
  • 22

10 Answers10

37

If all you want is to save an image (example: a .png) to the device, you can easily achieve this with a simple get (http/http.dart) and a File (dart:io).

To do so, you can base yourself in the example below:

var response = await http.get(imgUrl);
Directory documentDirectory = await getApplicationDocumentsDirectory();
File file = new File(join(documentDirectory.path, 'imagetest.png'));
file.writeAsBytesSync(response.bodyBytes); // This is a sync operation on a real 
                                           // app you'd probably prefer to use writeAsByte and handle its Future

Note that in the case above I’ve used the ‘path_provider’ package from the dart pub. In overall you would have imported at least these items:

import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
VipiN Negi
  • 2,994
  • 3
  • 24
  • 47
LuDanin
  • 709
  • 6
  • 8
28

There's a simple answer and a more complicated answer to this. I'll describe the complicated one and then give you the simple one.

The complicated answer is that you could manually cache images by using a NetworkImage, resolving it, and getting the image stream. Once you have the image stream, you could save it to the filesystem using flutter's file reading & writing capabilities (this is a good resource to learn more about that) - you also need to use a plugin called PathProvider which gets the right path for both iOS and Android which is described in that link. You'd also want to keep track of all the images you'd downloaded and probably delete them after certain amount of time. You'd also have to read the files back before using them to create Image widgets.

That gives you lots of control, but is a bit of work (although not a crazy amount, but if you're new to flutter maybe not something you want to do just now depending on why you want to save the images).

The simple answer is Packages to the rescue! Someone else has already come across this problem and written a plugin that solves it for you, so you don't have to think about it!

See cached_network_image package for information about the plugin.

You need to add to your dependencies in pubspec.yaml

dependencies:
  cached_network_image: "^0.3.0"

Import it:

import 'package:cached_network_image/cached_network_image.dart';

And use it!

new CachedNetworkImage(
   imageUrl: "http://imageurl.png",
   placeholder: new CircularProgressIndicator(),
   errorWidget: new Icon(Icons.error),
),

Note that this downloads & shows the image - if you want to do those seperately you can use a new CachedNetworkImageProvider(url) and show it using new Image.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
rmtmckenzie
  • 37,718
  • 9
  • 112
  • 99
  • I tried to use this package **cached_network_image** but this has a dependency on another package called **flutter_cache_manager** which fails when executing `flutter packages get` command. – Aritra Dattagupta Mar 24 '18 at 09:03
  • Weird. Not sure why that would be the case - I've just tested it out and had no problem. Maybe try a different version of cached_network_image, or set it to `cached_network_image: any` (although that's not really recommended). You could also try adding a dependency on just [flutter_cache_manager](https://pub.dartlang.org/packages/flutter_cache_manager) and seeing what happens. – rmtmckenzie Mar 25 '18 at 16:05
  • @mtmckenzie Hey I got it to work. The plugin version wasn't working because of Dart 2.0 compatibility. Now its updated and working fine. Thank you for the guidance. – Aritra Dattagupta Mar 25 '18 at 19:47
  • can you say how to save this cached image to a separate path – Jones Stephen Sep 12 '18 at 15:51
  • There's already a description of how to do that using NetworkImage in the answer. Take a look at the documentation and if you can't figure it out, you'd probably be best off opening a new question. The other option would be to look into CacheManger and where it saves the cache - I think it uses PathProvider's getTemporaryDirectory so the image files are probably saved in there somewhere. – rmtmckenzie Sep 12 '18 at 17:28
  • This avoids to, when the app is closed and opened again, download again the Image if already exists for that Url? – Alex Vergara Sep 13 '22 at 17:43
18

After wandering around the codes and flutter docs. I have found the methods and classes which would work for both iOS and Android and here it goes.

Helper Classs

import 'dart:async';
import 'dart:io' as Io;
import 'package:image/image.dart';

import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:path_provider/path_provider.dart';
class SaveFile {

  Future<String> get _localPath async {
    final directory = await getApplicationDocumentsDirectory();

    return directory.path;
  }
   Future<Io.File> getImageFromNetwork(String url) async {

     var cacheManager = await CacheManager.getInstance();
     Io.File file = await cacheManager.getFile(url);
     return file;
   }

   Future<Io.File> saveImage(String url) async {

    final file = await getImageFromNetwork(url);
    //retrieve local path for device
    var path = await _localPath;
    Image image = decodeImage(file.readAsBytesSync());

    Image thumbnail = copyResize(image, 120);

    // Save the thumbnail as a PNG.
    return new Io.File('$path/${DateTime.now().toUtc().toIso8601String()}.png')
      ..writeAsBytesSync(encodePng(thumbnail));
  }
}

Usage of class

class HomePageState extends State<HomePage>{

Future<Null> _launched ;

  Widget _showResult(BuildContext context, AsyncSnapshot<Null> snapshot){
    if(!snapshot.hasError){
      return Text('Image is saved');
    }
    else{
      return const Text('Unable to save image');
    }
  }

  Future<Null> _saveNetworkImage(String url) async{
    try{
       await SaveFile().saveImage(url);
    }
    on Error catch(e){
      throw 'Error has occured while saving';
    }
  }
   @override
   Widget Build(BuildContext context){
       return new Scaffold(
            key: _scaffoldKey,
            appBar: new AppBar(
                title: new Text('Image'),
            ),
            body: Column(
                  children: <Widget>[
                       IconButton(icon: Icon(Icons.save), onPressed: (){
                     setState(() {
                       _launched =_saveNetworkImage(url);
                     });
                    }),
                      new FutureBuilder<Null>(future: _launched ,builder: _showResult),
                  ],
            ),
          );
   }
}
Paras khandelwal
  • 1,345
  • 14
  • 13
3

To save the network image in local system you need to use ImagePickerSave dart plugin. Add the dart plugin in pub.yaml file: image_picker_saver: ^0.1.0 and call below code to save the image. URL is the image URL of network image

void _onImageSaveButtonPressed(String url) async {
  print("_onImageSaveButtonPressed");
  var response = await http
      .get(url);

  debugPrint(response.statusCode.toString());

  var filePath = await ImagePickerSaver.saveFile(
      fileData: response.bodyBytes);
  var savedFile= File.fromUri(Uri.file(filePath));
}
Google
  • 2,183
  • 3
  • 27
  • 48
  • can you tell me the location where the image is stored. I want to save my image in a specific directory. – Atul Chaudhary Nov 08 '19 at 03:37
  • It is saved under your app package name or you can find the installing File manger and check under images – Google Nov 08 '19 at 04:26
  • This plugin does not work if you are using image_picker at the same time. It makes the app crash when you try to access to the device's library. – Sean Sabe Sep 09 '20 at 23:46
2

Follow this url flutter save network image.

Code Snippet

import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:http/http.dart' as http;

class NetworkToLocalImage extends StatefulWidget{
 String url;

 NetworkToLocalImage(this.url);

 @override
 _LoadImages createState() => new _LoadImages(url);
 }

class _LoadImages extends State<NetworkToLocalImage>{

 String url;
 String filename;
 var dataBytes;

 _LoadImages(this.url){
 filename = Uri.parse(url).pathSegments.last;
 downloadImage().then((bytes){
  setState(() {
    dataBytes = bytes;
  });
});
}

Future<dynamic> downloadImage() async {
String dir = (await getApplicationDocumentsDirectory()).path;
File file = new File('$dir/$filename');

if (file.existsSync()) {
  print('file already exist');
  var image = await file.readAsBytes();
  return image;
} else {
  print('file not found downloading from server');
  var request = await http.get(url,);
  var bytes = await request.bodyBytes;//close();
  await file.writeAsBytes(bytes);
  print(file.path);
  return bytes;
}
}

 @override
 Widget build(BuildContext context) {
 // TODO: implement build
 if(dataBytes!=null)
 return new Image.memory(dataBytes);
 else return new CircularProgressIndicator();
}
}
Sher Ali
  • 5,513
  • 2
  • 27
  • 29
  • 2
    You should at least summarize the page behind the link, in case it disappears in the future. Also, it's a 404... – rmtmckenzie Jun 13 '18 at 08:41
0

I had a lot of trouble doing this, so I wanted to expound a little on the above answers with a simple example that downloads a file on startup, saves it to a local directory. I marked a couple of lines with the comment //%%% to show lines that can be commented out the second time that the app runs. (because it doesn't need to be downloaded to be displayed... it's already on the device itself:

import 'package:flutter/material.dart';
import 'package:http/http.dart' show get;
import 'dart:io';
import 'package:path_provider/path_provider.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Test Image',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Test Image'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  initState() {
    _downloadAndSavePhoto();
    super.initState();
  }

  _downloadAndSavePhoto() async {
    // Get file from internet
    var url = "https://www.tottus.cl/static/img/productos/20104355_2.jpg"; //%%%
    var response = await get(url); //%%%
    // documentDirectory is the unique device path to the area you'll be saving in
    var documentDirectory = await getApplicationDocumentsDirectory();
    var firstPath = documentDirectory.path + "/images"; //%%%
    //You'll have to manually create subdirectories
    await Directory(firstPath).create(recursive: true); //%%%
    // Name the file, create the file, and save in byte form.
    var filePathAndName = documentDirectory.path + '/images/pic.jpg';
    File file2 = new File(filePathAndName); //%%%
    file2.writeAsBytesSync(response.bodyBytes); //%%%
    setState(() {
      // When the data is available, display it
      imageData = filePathAndName;
      dataLoaded = true;
    });
  }

  String imageData;
  bool dataLoaded = false;

  @override
  Widget build(BuildContext context) {
    if (dataLoaded) {
      return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              // imageData holds the path AND the name of the picture.
              Image.file(File(imageData), width: 600.0, height: 290.0)
            ],
          ),
        ),
      );
    } else {
      return CircularProgressIndicator(
        backgroundColor: Colors.cyan,
        strokeWidth: 5,
      );
    }
  }
}

And heres my pubspec.yaml file:

http: ^0.12.0+2
  path_provider: 1.5.0
William Terrill
  • 3,484
  • 4
  • 31
  • 41
0

inspired by Paras,CacheManager.getInstance() is deprecated

final DefaultCacheManager defaultCacheManager = DefaultCacheManager();
final fileInfo = await defaultCacheManager.getFileFromCache(imageUrl);
ImageGallerySaver.saveImage(image);

below is saveImage function

// save raw data to gallery with permission
static Future<void> saveImage(Uint8List image) async {
  try {
    if (Platform.isIOS) {
      await PermissionHandler().requestPermissions([PermissionGroup.photos]);
      PermissionStatus permission = await PermissionHandler().checkPermissionStatus(PermissionGroup.photos);
      if (permission == PermissionStatus.granted) {
      } else {
        throw 'denied';
      }
    } else if (Platform.isAndroid) {
      await PermissionHandler().requestPermissions([PermissionGroup.storage]);
      PermissionStatus permission = await PermissionHandler().checkPermissionStatus(PermissionGroup.storage);
      if (permission == PermissionStatus.granted) {
      } else {
        throw 'denied';
      }
    }
    await ImageGallerySaver.saveImage(image);
  } catch (e) {
    throw e;
  }
}
user3044484
  • 463
  • 5
  • 7
0

You can use like that

var response = await http.get(Uri.parse(imageUrl));
Directory documentDirectory = await getApplicationDocumentsDirectory();
File file = File(join(documentDirectory.path, 'imagetest.png'));
file.writeAsBytesSync(response.bodyBytes);
await GallerySaver.saveImage(file.path);
Eray Hamurlu
  • 657
  • 7
  • 9
0

If you want to save network image to a gallery of the device(IOS or Anroid) you can use image_gallery_saver package that specifically created for this. You just need url of the network image. Here's example:

_save() async {
   var response = await Dio().get(
           "https://ss0.baidu.com/94o3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=a62e824376d98d1069d40a31113eb807/838ba61ea8d3fd1fc9c7b6853a4e251f94ca5f46.jpg",
           options: Options(responseType: ResponseType.bytes));
   final result = await ImageGallerySaver.saveImage(
           Uint8List.fromList(response.data),
           quality: 60,
           name: "hello");
   print(result);
  }
-1

You can use image_downloader.

  • For ios, image is saved in Photo Library.
  • For Android, image is saved in Environment.DIRECTORY_DOWNLOADS or specified location. By calling inExternalFilesDir(), specification of permission becomes unnecessary.
  • By callback(), you can get progress status.

The following is the simplest example. It will be saved.

await ImageDownloader.downloadImage(url);
ko2ic
  • 1,977
  • 14
  • 20