PickedImage getting null - flutter

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(),)
),
],
),
),
),
);
}
}

Some things are wrong in your code:
The getImage() function is not inside your class, so the setState won't work.
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.
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
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(),
)),
],
),
),
),
);
}
}

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

Related

Image is not picked in imagepicker

I am trying to work with image picker where I will pick an image from gallery and it will change in aa given circle avatar when picked.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
//import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:image_picker/image_picker.dart';
class AddDoctor extends StatefulWidget {
#override
State<AddDoctor> createState() => AddDoctorState();
}
class AddDoctorState extends State<AddDoctor> {
File? _image;
pickImage() async {
final ImagePicker imagePicker = ImagePicker();
final galleryFile =
await imagePicker.pickImage(source: ImageSource.gallery) as File;
if (galleryFile == null) {
return galleryFile.readAsBytes();
} else {
print('No Image Selected');
}
setState(() {
_image = galleryFile;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFD9E4EE),
appBar: AppBar(
title: const Text('Add Doctor'),
actions: [
IconButton(
icon: const Icon(Icons.menu),
onPressed: () {},
),
],
),
body: ListView(
padding: const EdgeInsets.all(16),
children: <Widget>[
CircleAvatar(
radius: 40,
backgroundImage: _image != null ? FileImage(_image!) : null,
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: () {
pickImage();
},
child: const Text('Upload Image'),
),
const SizedBox(height: 10),
],
),
);
}
}
But in the debug console it's showing problem like Image not selected (which is set in my if condition at the beginning) and something like: Unhandled Exception: type 'XFile' is not a subtype of type 'File' in type cast
final galleryFile =
await imagePicker.pickImage(source: ImageSource.gallery) as File;
here "As file" type cast is causing the problem because
The image picked is type of Object XFile and you're declaring it a File object so it's giving error type 'XFile' is not a subtype of type 'File' in type cast.
you can convert or use file path like this
final pickedXFile = await imagePicker.pickImage(source: ImageSource.gallery);
final galleryFile = File(pickedXFile.path);

image_picker flutter error, how can I save image in application permanently?

so i am having trouble saving the users pfp when they navigate to a new section on my application, when the user clicks of from the current tab and comes back the pfp disappears, it kills the path it seems. i want to be able to upload a image as the pfp, and when i navigate to a new section within the application for it to still be there.
code below:
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart';
import 'save_pfp.dart';
File? image;
class BuildProfile extends StatefulWidget {
const BuildProfile({super.key});
#override
State<BuildProfile> createState() => _BuildProfileState();
}
class _BuildProfileState extends State<BuildProfile> {
Future getImage() async{
try{
image = (await ImagePicker().pickImage(source: ImageSource.gallery)) as File? ;
if (image == null) return;
final imagePermanent = await saveImagePermanently(image!.path);
setState(() => image = imagePermanent);
} on PlatformException catch (e) {
print('failed to pick image: $e');
}
}
Future<File> saveImagePermanently(String imagePath) async{
final directory = await getApplicationDocumentsDirectory();
final fileName = basename(imagePath);
image = File('${directory.path}/$fileName');
return File(imagePath).copy(image!.path);
}
#override
Widget build(BuildContext context) {
return CircleAvatar(
backgroundColor: Colors.grey.shade400,
backgroundImage: image == null ? null
: FileImage(image!),
radius: 56,
child: Align(
alignment:const Alignment(3.2, 0.73),
child: RawMaterialButton(
onPressed: () {
showModalBottomSheet(context: context,
backgroundColor: Colors.black38,
builder: (context) => Container(
height: 180,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('camera',
style: TextStyle(color: Colors.white)),
IconButton(
onPressed: () {
// _getFromCamera;
getImage();
},
icon: const Icon(CupertinoIcons.camera,
size: 26,
color: Colors.white,
)),
IconButton(
// pick from gallery
onPressed:() {
getImage();
I have tried different solutions, but run into the same error.
In my case, I'm using Provider strategy to save the picked photos and consume them whenever I want in whatever code I am, this is how I proceeded:
First, get the photos from the image picker:
void _setImageFromPhone(ImageSource imageSource) async {
final pickedFile = await ImagePicker().pickImage(source: imageSource, imageQuality: 75);
if (pickedFile == null) {
return;
}
log(pickedFile.path);
widget.formModel.addNewPhotos(pickedFile.path);
}
The provider where I save the picked photos:
class FormModel extends ChangeNotifier {
final List<String> _photos = [];
void addNewPhotos(String photo) {
_photos.add(photo);
notifyListeners();
}
//getter for photos
UnmodifiableListView<String> get photos => UnmodifiableListView(_photos);
}
After that you can consume the photos you add wherever you want :
#override
Widget build(BuildContext context) {
return Consumer<FormModel>(
key: formModelConsumerKey,
builder: (
context,
formModel,
child,
) {
return
Row(
children:[
]
)
Container(
height: MediaQuery.of(context).size.width * 0.35,
width: MediaQuery.of(context).size.width * 0.35,
decoration: BoxDecoration(
color: LIGHT_GREEN_COLOR,
borderRadius: const BorderRadius.all(
Radius.circular(10),
),
image: DecorationImage(
image: Image.file(File(formModel.photos.first!)).image,
fit: BoxFit.fill,
),
),
child: SizedBox();
);
}
);
}
Also do not forget to provide your Provider class in the top parent widgets, small example bellow :
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => FormModel()),
],
child: MaterialApp()
)

Why does my UI load the old image instead of the new one when I navigate to my route?

I am facing an issue with the state of my Flutter application.
A user can pick an image from their gallery using the Image Picker package from pub.dev. That goes smoothly, but I am then trying to save the picked image to local storage, with the help of the Path Provider package.
As demonstrated in this quick gif, you can see that picking an image indeed replaces the old one. However, when leaving the page and navigating back to it, the UI displays the old image instead of the new one.
Issue in this gif
Here is the code for the profile picture stateful widget:
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'custom_profile_picture.dart';
import 'default_profile_picture.dart';
class ProfilePicture extends StatefulWidget {
const ProfilePicture({
Key? key,
}) : super(key: key);
#override
State<ProfilePicture> createState() => _ProfilePictureState();
}
class _ProfilePictureState extends State<ProfilePicture> {
final ImagePicker _picker = ImagePicker();
XFile? pickedFile;
File? localImage;
#override
void initState() {
super.initState();
loadImage();
}
#override
Widget build(BuildContext context) {
return Column(
children: [
pickedFile != null
? CustomProfilePicture(image: File(pickedFile!.path))
: const DefaultProfilePicture(),
TextButton(
onPressed: pickImage,
child: const Text(
'Change picture',
style: TextStyle(fontSize: 12),
),
),
],
);
}
void pickImage() async {
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
if (image != null) {
print('New image picked');
setState(() {
pickedFile = image;
});
saveImage(image);
}
}
void saveImage(XFile img) async {
String path = (await getApplicationDocumentsDirectory()).path;
File convertedImg = File(img.path);
String fileName = "profilepicture";
File? localImage = await convertedImg.copy("$path/$fileName");
print("Image was saved under: $path/$fileName");
}
void loadImage() async {
String fileName = "profilepicture";
String path = (await getApplicationDocumentsDirectory()).path;
if (await File("$path/$fileName").exists()) {
print('The image exists. Loading image from:');
print('$path/$fileName');
setState(() {
pickedFile = XFile("$path/$fileName");
});
}
}
}
Here is the code for the Profile page, in which is located the profile picture component:
import 'package:flutter/material.dart';
import 'widgets/profile_picture.dart';
import 'widgets/custom_table.dart';
import 'widgets/about_me_content.dart';
import 'widgets/service_info_content.dart';
class ProfileView extends StatelessWidget {
const ProfileView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFe3e3e3),
appBar: AppBar(
foregroundColor: Colors.blue,
title: const Text(
'Profile view',
style: TextStyle(fontWeight: FontWeight.bold),
),
centerTitle: true,
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
),
body: SingleChildScrollView(
clipBehavior: Clip.none,
physics: const AlwaysScrollableScrollPhysics(),
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
child: Align(
alignment: Alignment.center,
child: Column(
children: [
const ProfilePicture(),
const Text(
'William Dupont',
style: TextStyle(fontSize: 26, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
CustomTable(tableTitle: 'About me', content: AboutMeContent(),),
const SizedBox(height: 20),
CustomTable(tableTitle: 'My service info', content: ServiceInfoContent(),),
],
),
),
),
),
);
}
}
Any question, let me know.
I have found a fix to this issue.
I evict the image at the current path using the evict method.
Code:
void loadImage() async {
String fileName = "profilepicture";
String path = (await getApplicationDocumentsDirectory()).path;
if (await File("$path/$fileName").exists()) {
print('The image exists. Loading image from:');
print('$path/$fileName');
await FileImage(File('$path/$fileName)).evict();
setState(() {
pickedFile = XFile("$path/$fileName");
});
}
}

Path for picked file is throwing null check error in flutter

I have a profile picture card which I am using in 3 different places. I am passing some parameters to the ProfilePictureCard to show camera button.
For an example if the ProfilePictureCard is used in a ProfilePage the camera button will be hidden and if the said card is shown in a EditProfilePage the camera button will be visible.
This is the code for ProfilePictureCard:
class ProfilePictureCard extends StatefulWidget {
final bool? isSetProfilePage;
final bool? isEditable;
File? imageFile;
final String? imageUrl;
ProfilePictureCard({
Key? key,
this.isSetProfilePage = false,
this.isEditable = false,
this.imageFile,
this.imageUrl,
}) : super(key: key);
#override
_ProfilePictureCardState createState() => _ProfilePictureCardState();
}
class _ProfilePictureCardState extends State<ProfilePictureCard> {
#override
void initState() {
super.initState();
print('Image Url: ${widget.imageUrl}');
}
#override
Widget build(BuildContext context) {
return Container(
height: 150,
width: 150,
child: Stack(
children: [
Stack(
children: [
Center(
child: _buildProfilePicture(),
),
widget.isEditable == true
? Positioned(
bottom: 0,
left: 55,
right: 55,
child: CameraButton(
onPressed: () {
_handleChangeProfilePicture();
},
),
)
: Container(),
],
),
],
),
);
}
Widget _buildProfilePicture() {
return Container(
height: 100,
width: 100,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: CustomColors.semiSecondary,
width: 1,
),
),
child: _buildImage(),
);
}
Widget _buildImage() {
return ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: widget.imageFile != null
? Image.file(
widget.imageFile!,
fit: BoxFit.cover,
)
: Image.network(
widget.imageUrl ?? 'https://picsum.photos/200',
fit: BoxFit.cover,
),
);
}
void _handleChangeProfilePicture() async {
final result = await showDialog(
context: context,
builder: (context) {
return CameraDialog();
},
);
print("Result: $result");
if (result != null) {
setState(() {
widget.imageFile = result;
});
print(widget.imageFile);
}
}
}
_handleChangeProfilePicture takes a picture from either gallery or camera and returns it using Navigator.of(context).pop(finalImage);
In my EditProfilePage I have a Update Button which for now only prints out the selected images path.
The EditProfilePage file looks like this:
class _EditProfilePage extends State<EditProfilePage {
File? imageFile;
#override
Widget build(BuildContext context){
...other widgets
ProfilePictureCard(
isEditable: true, // because of this line the camera button is visible
imageFile: imageFile,
imageUrl: 'https://picsum.photos/id/237/200/300', // previous profile picture, will be replaced with selected image
)
}
_handleUpdate(){
print(imageFile.path);
}
}
Inside the CameraDialog other than the basic UI stuff for returning the selected file I have this:
void _captureImage(context) async {
final XFile? pickedFile =
await _picker.pickImage(source: ImageSource.camera);
if (pickedFile != null) {
final File file = File(pickedFile.path);
Navigator.of(context).pop(file);
}
}
But when I press update I am getting: Null check operator used on a null value from the print(imageFile.path);
What is the reason for this issue, and how to solve this?
You should use function to return your image instead of passing object,
class ProfilePictureCard extends StatefulWidget {
final bool? isSetProfilePage;
final bool? isEditable;
final Function(File file) handleUpdate;
final String? imageUrl;
ProfilePictureCard({
Key? key,
this.isSetProfilePage = false,
this.isEditable = false,
this.handleUpdate,
this.imageUrl,
})
usage
if (result != null) {
widget.handleUpdate(result);
}
In editing profile
class _EditProfilePage extends State<EditProfilePage {
File? imageFile;
#override
Widget build(BuildContext context){
...other widgets
ProfilePictureCard(
isEditable: true, // because of this line the camera button is visible
handleUpdate: _handleUpdate,
imageUrl: 'https://picsum.photos/id/237/200/300', // previous profile picture, will be replaced with selected image
)
}
_handleUpdate(File file){
print(file.path);
setState(() {
imageFile = file;
});
}
}

flutter) There's a problem with using imagePicker

I'm using sdk 2.12.0 and image_picker 0.8.4 version.
I'm going to link my gallery to get an image.
However, when I press the Add Image button on my app, the app turns off immediately.
This is the code for the image_picker I used.
class CreatePage extends StatefulWidget {
const CreatePage({Key? key, required this.user}) : super(key: key);
final User user;
#override
_CreatePageState createState() => _CreatePageState();
}
class _CreatePageState extends State<CreatePage> {
//ImagePicker
final ImagePicker _picker = ImagePicker();
File? _imageFile;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: _buildAppbar(),
body: _buildBody(),
floatingActionButton: FloatingActionButton(
onPressed: _getImage,
backgroundColor: Colors.blue,
child: Icon(Icons.add_a_photo),
),
);
}
Future<void> _getImage() async {
//ImagePiker
var image = await _picker.pickImage(source: ImageSource.gallery);
setState(() {
_imageFile = File(image!.path);
});
}
And this is my full code about this page. (Firebase code is included)
import 'dart:io';
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class CreatePage extends StatefulWidget {
//user info
const CreatePage({Key? key, required this.user}) : super(key: key);
final User user;
#override
_CreatePageState createState() => _CreatePageState();
}
class _CreatePageState extends State<CreatePage> {
//input text
final TextEditingController createText = TextEditingController();
//ImagePicker
final ImagePicker _picker = ImagePicker();
File? _imageFile;
//_createPageState가 제거될 때 호출됨
#override
void dispose() {
// TODO: implement dispose
createText.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: _buildAppbar(),
body: _buildBody(),
floatingActionButton: FloatingActionButton(
onPressed: _getImage,
backgroundColor: Colors.blue,
child: Icon(Icons.add_a_photo),
),
);
}
_buildAppbar() {
return AppBar(
actions: [
IconButton(
icon: Icon(Icons.send),
onPressed: () {
_uploadPost(context);
},
),
],
);
}
_buildBody() {
return SingleChildScrollView(
child: Column(
children: [
_imageFile == null ? Text("No Image") : Image.file(_imageFile!),
TextField(
controller: createText,
decoration: InputDecoration(
hintText: "내용을 입력하세요",
),
)
],
),
);
}
//gallery image
Future<void> _getImage() async {
var image = await _picker.pickImage(source: ImageSource.gallery);
setState(() {
_imageFile = File(image!.path);
});
}
Future _uploadPost(BuildContext context) async {
final firebaseStorageRef = FirebaseStorage.instance
.ref()
.child('post')
.child('${DateTime.now().microsecondsSinceEpoch}.png');
final task = await firebaseStorageRef.putFile(
_imageFile!, SettableMetadata(contentType: "image/png")
);
final uri = await task.ref.getDownloadURL();
//database document
final doc = FirebaseFirestore.instance.collection('post').doc();
//json type
await doc.set({
'id': doc.id,
'photoUrl': uri.toString(), //storage file url
'contents': createText, //user input text
'email': widget.user.email, //user email
'displayName': widget.user.displayName, //user name
'userPhotoUrl': widget.user.photoURL, //user profile image
});
//return page
Navigator.pop(context);
}
}
Pressing the floatingActionButton turns off the app and outputs the following error.
Lost connection to device.
May I know the cause of this?
Thank you in advance.
try adding dependency
image_picker: ^0.8.3+2
import 'package:image_picker/image_picker.dart';
then add this code
String url = "";
ImagePicker image = ImagePicker();
File ? file;
getImage() async {
var img = await image.pickImage(source: ImageSource.gallery);
setState(() {
file = File(img!.path);
});
}
And add:
onTap: () {
getImage();
},
add code:
child: file != null ?
Column(
children: [
Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
color: Colors.black,
margin: EdgeInsets.only(top: 80),
width: double.infinity,
height: 250,
child: Image.file(
file!,
fit: BoxFit.contain,
),
),
),
My guess is that you don't have permission to access the Media on the device, thus aborting the app the moment you try to do so. Check out the permission handler package.