2

I want to save an image locally on Device after Picking the Image From Gallery By using Path provider package . but _image file variable gets null after Selecting an Image From Gallery . Thats Why the Screen keeps Stuck on CircularProgressIndicator Screen . Can You Please Help me out of this null _image file variable.

import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';

class SaveImage extends StatefulWidget {
  const SaveImage({ Key? key }) : super(key: key);

  @override
  _SaveImageState createState() => _SaveImageState();
}

File? _image;


Future<File?> getImage() async{
    var image = File(await ImagePicker.platform
        .pickImage(source: ImageSource.gallery)
        .then((value) => value.path));

final Directory directory = await getApplicationDocumentsDirectory();
final path=directory.toString();
final String fileName = basename(image.path);
// final String fileExtension = extension(image.path);



File newImage = await image.copy('$path/$fileName.jpg');


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



  

}

void setState(Null Function() param0) {
}





class _SaveImageState extends State<SaveImage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: Column( 
            children: [
              Text('Pick Image From'),
              SizedBox(height: 30,),
              ElevatedButton(onPressed: (){
                getImage();
              }, child: Text('From Gallery')),
              ElevatedButton(onPressed: (){
               
              }, child: Text('From Camera')),
              SizedBox(height: 50),
              Container(
                child: _image!=null?ClipRRect(
                              borderRadius: BorderRadius.circular(6),
                              child: Image.file(
                                _image!,
                                fit: BoxFit.cover,
                              )):Center(child: CircularProgressIndicator(),)
              ),
            ],
          ),
        ),
      ),
    );
  }
}
Rex
  • 250
  • 1
  • 3
  • 18

2 Answers2

3

Some things are wrong in your code:

  1. The getImage() function is not inside your class, so the setState won't work.

  2. You're not checking the return value of ImagePicker.platform.pickImage(), as it can be null. You have to check it before initialising a File with it.

  3. directory.toString() does not return the path of the directory, but it returns "Directory: '/something'". It is meant to be printed. If you want the actual directory path, you need directory.path

  4. If it's still not working, make sure you have done the right setup, as asked by image_picker (setting up your Info.plist for IOS...)

Here is your code, working as expected:

class SaveImage extends StatefulWidget {
  const SaveImage({Key? key}) : super(key: key);

  @override
  _SaveImageState createState() => _SaveImageState();
}

class _SaveImageState extends State<SaveImage> {
  File? _image;

  Future<File?> getImage() async {
      PickedFile? pickedFile =
          await ImagePicker.platform.pickImage(source: ImageSource.gallery);
      if (pickedFile == null) {
        return null;
      }
      final File file = File(pickedFile.path);
      final Directory directory = await getApplicationDocumentsDirectory();
      final path = directory.path;
      final String fileName = basename(pickedFile.path);
// final String fileExtension = extension(image.path);
      File newImage = await file.copy('$path/$fileName');
      setState(() {
        _image = newImage;
      });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: Column(
            children: [
              Text('Pick Image From'),
              SizedBox(
                height: 30,
              ),
              ElevatedButton(
                  onPressed: () {
                    getImage();
                  },
                  child: Text('From Gallery')),
              ElevatedButton(onPressed: () {}, child: Text('From Camera')),
              SizedBox(height: 50),
              Container(
                  child: _image != null
                      ? ClipRRect(
                          borderRadius: BorderRadius.circular(6),
                          child: Image.file(
                            _image!,
                            fit: BoxFit.cover,
                          ))
                      : Center(
                          child: CircularProgressIndicator(),
                        )),
            ],
          ),
        ),
      ),
    );
  }
}

If you want to store the image in the cache, you can use flutter_cache_manager library. It allows you to store and retrieve a file in cache.

Here is the code, updated to store the file in cache. Notice that we use a key to identify our file in cache (I set it to the path of the file but you can set it to basically any String as long as it's unique to this file). If you want to use the file app-wide, you would probably need to store the key somewhere that can be accessed there.

class SaveImage extends StatefulWidget {
  const SaveImage({Key? key}) : super(key: key);

  @override
  _SaveImageState createState() => _SaveImageState();
}

class _SaveImageState extends State<SaveImage> {
  File? _image;
  String? cachedFileKey;

  Future<File?> getImage() async {
    PickedFile? pickedFile =
        await ImagePicker.platform.pickImage(source: ImageSource.gallery);
    if (pickedFile == null) {
      return null;
    }
    final File file = File(pickedFile.path);
    final Uint8List fileBytes = await file.readAsBytes();
    final cachedFile = await DefaultCacheManager()
        .putFile(pickedFile.path, fileBytes, key: pickedFile.path);
    setState(() {
      cachedFileKey = pickedFile.path;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: Column(
            children: [
              Text('Pick Image From'),
              SizedBox(
                height: 30,
              ),
              ElevatedButton(
                  onPressed: () {
                    getImage();
                  },
                  child: Text('From Gallery')),
              ElevatedButton(onPressed: () {}, child: Text('From Camera')),
              const SizedBox(height: 50),
              Container(
                  child: cachedFileKey != null
                      ? 
                      FutureBuilder<FileInfo?>(future: DefaultCacheManager().getFileFromCache(cachedFileKey!), builder: (context, snapShot) {
                        if (snapShot.hasData && snapShot.data != null) {
                      return ClipRRect(
                          borderRadius: BorderRadius.circular(6),
                          child: Image.file(snapShot.data!.file,
                            fit: BoxFit.cover,
                          ));
                        } else {
                          return const Center(
                          child: CircularProgressIndicator(),
                        );
                        }
                      })
                      : const Center(
                          child: CircularProgressIndicator(),
                        )),
            ],
          ),
        ),
      ),
    );
  }
}
jeremynac
  • 1,204
  • 3
  • 11
  • Thanks . If I want to Store locally as a memory what I have to do – Rex Jan 16 '22 at 09:43
  • 1
    You mean you want to access it outside the scope of the class ? I see 2 ways: 1. If you only need to access it a few times, store the path globally (using a state management library for example) and simply retrieve the image from the path when you need it. 2. If you need to store the image without relying on storing a file, you can use some state management library, and store your image file there. There are many good state management options in Flutter, including: Provider, Riverpod, Bloc. I personally use Mobx as it's pretty powerful and does not need too much boilerplate – jeremynac Jan 16 '22 at 10:16
  • Mobx link if you need it: https://pub.dev/packages/mobx – jeremynac Jan 16 '22 at 10:19
  • Thanks it's working but image is not loading after reopening the app . Is any method can add to initialise it – Rex Jan 16 '22 at 19:29
  • 1
    Well, since you’re using the cache, the data does not stay when restarting the app. If you want to persist data between restarts, you could use packages like SQLite or share_preferences, but I think the best way is to simply store your image as a file on disk (as in the code above). If you still want to be able to access your file in cache, you could store the file on disk for long term storage, and put it into the cache whenever you restart the app. And you would then be able to access it using DefaultCacheManager(). – jeremynac Jan 16 '22 at 20:21
  • And if I want to Use multiple Images and save in flutter cache manager so how to use it because in future builder future takes String key for default cache manager – Rex Jan 23 '22 at 06:57
  • You could simply use multiple FutureBuilder(), in a ListView or in a Grid, each with a different key leading to a different image – jeremynac Jan 23 '22 at 09:54
0

use image type PickedFile type. Ex:

PickedFile _image;

  Future pickImageFromGallery(ImageSource source, BuildContext context) async {
    var _image = await ImagePicker.platform.pickImage(source: source);
    image = _image;
 
    _uploadImage(image, context);
  }

**for taking image path:

imageFile.path
shorol
  • 790
  • 5
  • 11