20

I'm trying to load image from server using networkimage() and I want to download the same once it is loaded.. can anyone suggest me some ideas.

CircleAvatar(
      backgroundImage: NetworkImage(url),
      maxRadius: 15.0,
              ); 

Here I'm loading image from my server. I want to save to the image to particular path after the image is loaded.

4

7 Answers 7

57

I recently battled this, and decided to solve it without plugins. I hope it helps someone.

The below program downloads a picture from the web, stores it in the device's local path, and then displays it when run. (note, it does not work for flutter web because you don't have access to the local file storage on that platform. Instead you would have to save the image to a local database using a plugin like sqflite, or hive from pub.dev.) Here's the code:

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 {
  // This widget is the root of your application.
  @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() {
    _asyncMethod();
    super.initState();
  }

  _asyncMethod() async {
    //comment out the next two lines to prevent the device from getting
    // the image from the web in order to prove that the picture is 
    // coming from the device instead of the web.
    var url = "https://www.tottus.cl/static/img/productos/20104355_2.jpg"; // <-- 1
    var response = await get(url); // <--2
    var documentDirectory = await getApplicationDocumentsDirectory();
    var firstPath = documentDirectory.path + "/images";
    var filePathAndName = documentDirectory.path + '/images/pic.jpg'; 
    //comment out the next three lines to prevent the image from being saved
    //to the device to show that it's coming from the internet
    await Directory(firstPath).create(recursive: true); // <-- 1
    File file2 = new File(filePathAndName);             // <-- 2
    file2.writeAsBytesSync(response.bodyBytes);         // <-- 3
    setState(() {
      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>[
              Image.file(File(imageData), width: 600.0, height: 290.0)
            ],
          ),
        ),
      );
    } else {
      return CircularProgressIndicator(
        backgroundColor: Colors.cyan,
        strokeWidth: 5,
      );
    }
  }
}

pubspec.yaml file: http: ^0.12.1 path_provider: ^1.6.5

flutter version: 1.20.0-3.0.pre.112 dart version 2.9.0-19.0.dev

Sign up to request clarification or add additional context in comments.

2 Comments

So, this works for a specific file, since you give the file specific name and hence can call it without downloading it again. I wonder if one can write something more general, so that I can deal with multiple images. Let's say a chat app. I would not want to download an image more than once. Furthermore, it must be stateless, because I don't want to download the images again after closing the app.
for the naming piece, since it's a string, you can make it whatever you want it to be. (numeric, random, whatever) I'm not sure what you mean by being stateless since I only made it stateful in order to call the real 'meat' of the widget, which is the _asyncMethod(). This method finds and downloads the files and stores them with the documentDirectory.path, which means that it is stored on the device. Therefore, to show those files, you can just point a widget (stateless or otherwise) at that directory to show desired files. I hope that helps... let me know if doesn't
4

I used image_downloader.
Use await ImageDownloader.downloadImage("url") of image_downloader package's method to download image using it's url.

Note : above method will return value as follows :-

  • imageId of the saved image if saving succeeded.

  • null if not been granted permission. for this you have to ask for storage permission, just add following line into android manifest file :

uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"

  • Otherwise it is a PlatformException.

Comments

3

I recommend 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);

2 Comments

Is the iOS photos library private? Or would photos there spam up the user's gallery app?
This plugin is not up to date. Consider using the default path_provider and your own implementation.
2

I tried many solution, but this is simplest solution for my... Just try it

STEP - 1
Add this package in your pubspec.yaml file

dependencies:
 image_downloader: ^0.20.1

STEP - 2
Add this in your dart file

import 'package:image_downloader/image_downloader.dart';  

STEP - 3
Write this code on press download button

ColButton(
   title: 'Download',
   icon: Icons.file_download,
   onTap: () async {
     try {
       showLoadingDialog(context);
       // Saved with this method.
       var imageId =
           await ImageDownloader.downloadImage("https://raw.githubusercontent.com/wiki/ko2ic/image_downloader/images/bigsize.jpg");
       if (imageId == null) {
          return;
       }
       // Below is a method of obtaining saved image information.
       var fileName = await ImageDownloader.findName(imageId);
       var path = await ImageDownloader.findPath(imageId);
       var size = await ImageDownloader.findByteSize(imageId);
       var mimeType = await ImageDownloader.findMimeType(imageId);
       Navigator.pop(context);
       showToast('Image downloaded.');
      } on PlatformException catch (error) {
          print(error);
        }
     },
   ),

Comments

2

flutter download an Image from url

Future<void> saveImage({required BuildContext context,required String image}) async {
    String? message;
    final scaffoldMessenger = ScaffoldMessenger.of(context);
    try {
      // Download image
      final http.Response response = await http.get(Uri.parse(image));
      // Get temporary directory
      final dir = await getTemporaryDirectory();
      // Create an image name
      var filename = '${dir.path}/image.png';
      // Save to filesystem
      final file = File(filename);
      await file.writeAsBytes(response.bodyBytes);
      // // Ask the user to save it
      final params = SaveFileDialogParams(sourceFilePath: file.path);
      final finalPath = await FlutterFileDialog.saveFile(params: params);
    } catch (e) {
      message = 'An error occurred while saving the image';
    }
    if (message != null) {
      scaffoldMessenger.showSnackBar(SnackBar(content: Text(message)));
    }
  }

To do this task we will use the following dependencies:

flutter_file_dialog: We will use it to request the operating system to ask the user where they want to save the image and to perform said saving.

path_provider: We will need to find the directory of temporary files to create the first temporary copy of the file.

http: It will be necessary to download the image in the temporary directory.

Comments

-1

While using image_downloader if any one is getting this

Error : (No implementation found for method downloadImage on channel plugins.ko2ic.com/image_downloader)

than follow below steps

Note: in the case of android, to use this feature, the following settings are required.

Add the following within tag in AndroidManifest.xml .

<provider
        android:name="com.ko2ic.imagedownloader.FileProvider"
        android:authorities="${applicationId}.image_downloader.provider"
        android:exported="false"
        android:grantUriPermissions="true">
    <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
</provider>

Add provider_paths.xml in android/app/src/main/res/xml/

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

Comments

-1

I solved it natively in Kotlin and published it as a package. Since you're using NetworkImage and want to trigger the image download only after it's loaded, use Image.network with the loadingBuilder callback. This lets you detect when the image finishes loading and then trigger the download using 3 dependencies:

  • permission_handler 11.0.1
  • device_info_plus 10.0.0
  • flutter_universal_downloader 0.0.3

Example that also handles Android permissions based on the SDK version:

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_universal_downloader/flutter_universal_downloader.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:permission_handler/permission_handler.dart';

class DownloadableAvatar extends StatefulWidget {
  final String imageUrl;

  const DownloadableAvatar({super.key, required this.imageUrl});

  @override
  State<DownloadableAvatar> createState() => _DownloadableAvatarState();
}

class _DownloadableAvatarState extends State<DownloadableAvatar> {
  bool _downloadStarted = false;
  int _androidSdkVersion = 0;

  @override
  void initState() {
    super.initState();
    _getAndroidSdkVersion();
  }

  Future<void> _getAndroidSdkVersion() async {
    if (Platform.isAndroid) {
      try {
        final androidInfo = await DeviceInfoPlugin().androidInfo;
        if (mounted) {
          setState(() => _androidSdkVersion = androidInfo.version.sdkInt);
        }
      } catch (e) {
        debugPrint('Error getting SDK version: $e');
      }
    }
  }

  Future<void> _downloadImageWithPermission(String url) async {
    try {
      if (Platform.isAndroid) {
        final PermissionStatus status;

        if (_androidSdkVersion >= 33) {
          status = await Permission.notification.request();
        } else if (_androidSdkVersion >= 29) {
          status = PermissionStatus.granted;
        } else {
          status = await Permission.storage.request();
        }

        if (!status.isGranted) {
          _showSnackBar('❌ Permission denied. Cannot download.');
          return;
        }
      }

      final result = await FlutterUniversalDownloader.foregroundDownload(
        url,
        fileName: 'profile_image.jpg',
      );

      _showSnackBar(
        result ? '⬇️ Download started!' : '❌ Failed to start download.',
      );
    } on PlatformException catch (e) {
      debugPrint('Platform error: ${e.message}');
      _showSnackBar('⚠️ ${e.message}');
    } catch (e) {
      debugPrint('Error: $e');
      _showSnackBar('⚠️ Unexpected error: $e');
    }
  }

  void _showSnackBar(String message) {
    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
    }
  }

  @override
  Widget build(BuildContext context) {
    return CircleAvatar(
      radius: 25,
      child: ClipOval(
        child: Image.network(
          widget.imageUrl,
          fit: BoxFit.cover,
          width: 50,
          height: 50,
          loadingBuilder: (context, child, loadingProgress) {
            if (loadingProgress == null) {
              if (!_downloadStarted) {
                _downloadStarted = true;
                _downloadImageWithPermission(widget.imageUrl);
              }
              return child;
            } else {
              return const Center(
                child: SizedBox(
                  width: 20,
                  height: 20,
                  child: CircularProgressIndicator(strokeWidth: 2),
                ),
              );
            }
          },
          errorBuilder: (context, error, stackTrace) =>
              const Icon(Icons.error),
        ),
      ),
    );
  }
}

Update AndroidManifest.xml:

<!-- For Android < 10 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<!-- For Android 13+ notifications -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.