flutter firebase image upload takes time to get file url - flutter

i'm trying to upload an image to the cloud firestore and the firebase storage. I'm saving the image url in a variable called imgUrl, this variable is later on passed inside a function called addIntervention(). The problem is that the upload task takes few time so if I upload and click the save button directly, imgUrl will be having null value cus the image is still getting uploaded.
Here is my code:
IconButton(
icon: Icon(
Icons.image,
color: Palette.primaryColor,
),
onPressed: () async {
ImagePicker imagePicker = ImagePicker();
XFile? file = await imagePicker.pickImage(
source: ImageSource.gallery);
if (file == null) return;
Reference referenceRoot =
FirebaseStorage.instance.ref();
Reference dirImages =
referenceRoot.child("iv_images");
Reference imgToUpload = dirImages.child(file.name);
try {
await imgToUpload.putFile(File(file.path));
var x = imgUrl = await imgToUpload.getDownloadURL();
imgUrl = x;
} catch (e) {}
},
),
And for the button I took this snippet:
if (imgUrl.isEmpty) {
QuickAlert.show(
context: context,
type: QuickAlertType.error,
title: 'Error',
text:
'Please upload an image');
} else {
await addIntervention(
imgUrl,
etatLabel,
numIntv,
intervention,
myPrice!,
note,
dateTime);
Noting that i'm using async/await for the save button as well, is there any way I can solve this? thanks in advance.

You can try these tips:
First thing to make your upload time a lot less is to compress a picture, you don't have to compress the image till it gets blurry but a small amount of compression will significantly reduce your upload time. Also if a use selects an image then he/she may want to crop it too. So it will be better if you add that functionality too.
Luckily there's a package called image_cropper(link), which you can use to crop as well as for compressing your image.
If you don't want to show any loading indicator then you can directly pass the image to the next screen and run your processes in the background(which is called optimistic updating), but if you want to show a loading indicator then you can use this package called flutter_spinkit. It has a very large variety of loading indicators which you will love.
When a user clicks on a button, you can show a progress indicator on the button itself to indicate how much percent has been uploaded, has to be uploaded before the user can click on the button.
In the firebase, you can get percentage like this:
Future getImage(BuildContext context) async {
final picker = ImagePicker();
final pickedFile = await picker.getImage(source: ImageSource.gallery);
setState(() {
_image = File(pickedFile.path);
});
StorageReference firebaseStorageRef = FirebaseStorage.instance.ref().child('profile/${Path.basename(_image.path)}}');
StorageUploadTask uploadTask = firebaseStorageRef.putFile(_image);
var dowurl = await (await uploadTask.onComplete).ref.getDownloadURL();
setState(() {
_imageURL = dowurl.toString();
});
print(_imageURL);
}
uploadTask.events.listen((event) {
setState(() {
_progress = event.snapshot.bytesTransferred.toDouble() /
event.snapshot.totalByteCount.toDouble();
});
}).onError((error) {
// do something to handle error
});
Then you can display progress like this:
Text('Uploading ${(_progress * 100).toStringAsFixed(2)} %')

Related

how to edit the taken picture with flutter

I use Image_picker and provider and some other things to access to camera and gallery and get the photo and save it with Sqlite . there is no problem here but I want to ask is there any solution for editing photo while we take the photo using camera? for example highlight a part of the photo
Future<void> _takePicture() async {
final picker = ImagePicker();
final imageFile =
await picker.pickImage(source: ImageSource.camera, maxHeight: 600);
if (imageFile == null) {
return;
}
setState(() {
_imageFile = File(imageFile.path);
});
this is my method
should I use Image_painter or Image_editor_plus?
you can use Image package in flutter, you can change the brightness using this method from image package
Image image = decodeImage(file.readAsBytesSync());
Image brightenedImage = adjustColor(image, brightness: 0.2);
you can read details of this package here.

Saving image permenantly after user upload it in flutter

In my flutter app, if the user is signing in for the first time, he will be directed to profile page where he gets to key in his personal details and upload his profile pic. now my issue is with the profile pic. First of all, Im using Image picker package.
Future pickImage(ImageSource source) async {
try {
final image = await ImagePicker().pickImage(
source: source);
if (image == null) return;
final UserImage = File(image.path);
setState(() => this.image = UserImage );
}on PlatformException catch (e){
Utils.showSnackBar(e.message);
}
}
But with this code alone, everytime the app gets restarted the image will be null. So I tried to upload the image to the Firebase Storage when the user picks an image and generate a url:
Future uploadImage () async {
FirebaseStorage storage = FirebaseStorage.instance;
Reference ref = storage.ref().child(userID.toString());
UploadTask uploadTask = ref.putFile(image!);
uploadTask.whenComplete(() async {
url = await ref.getDownloadURL(); }
).catchError((onError){
print(onError);
});
return url;
}
But again every time I restart the app, the url will be null.
What is the best way to save the image permenantly when the user signs in for the first time.
edit: I want to store the image locally so that the user doesnt need an internet connection to load the image everytime he open the app.
Your answers and responses are highly appreciated.
you need to get folder directory first
Future<String> getStorageDirectory() async {
if (Platform.isAndroid) {
return (await getExternalStorageDirectory()).path;
} else {
return (await getApplicationDocumentsDirectory()).path;
}
}
Add image in path
uploadImage() async{
String dir= getStorageDirectory();
File directory = new File("$dir");
if (directory.exists() != true) {
directory.create();
}
final image = await ImagePicker().pickImage(
source: source);
if (image == null) return;
final userImage = File(image.path);
var newFile = await userImage.writeAsBytes(/* image bytes*/);
await newFile.create();
}

Firebase Storage URL is FutureString

in my flutter app, the user picture is loaded by Cached Network image command, which gets its url by stream builder from firestore.
I am trying to add the functionality to the user of changing his pic by pressing on the pic as following:
Selecting his pic with image picker.
upload it to firebase storage.
updating firestore usercollection document with new image url.
I created the below code.
The problem is getDownloadURL() is not returning actual string, but "Instance of 'Future'".
so the new link stored in firestore is not correct to be used by Cached Network Image.
how can I get the actual URl String?
My Future Function Code:
Future ChangeProfilePic() async {
String newimageurl = "";
FirebaseStorage storage = FirebaseStorage.instance;
Reference ref =
storage.ref().child("ProfileImages/$globaluserid".toString());
CollectionReference userscollectionref =
FirebaseFirestore.instance.collection('UsersCollection');
final ImagePicker _picker = ImagePicker();
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
File imagefile = File(image!.path);
UploadTask uploadTask = ref.putFile(imagefile);
uploadTask.whenComplete(() {
newimageurl = ref.getDownloadURL().toString();
print("Image Uploaded");
userscollectionref
.doc(globaluserid)
.update({'User_image_link': newimageurl});
print("Link is Updated");
}).catchError((onError) {
print("Error");
print(onError);
});
}
Like many calls in your code `` is an asynchronous call, whose result won't be available immediately, so it returns a Future that will at some point contain the value. You can use await to wait for such a Future to complete and get its value, similar to what you already do in await _picker.pickImage.
await ref.getDownloadURL().toString();
Another change to consider is that putFile returns a Task, but that is actually also a Future, which means that you can await that too.
Combining these two fact, you can simplify your code to:
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
File imagefile = File(image!.path);
await ref.putFile(imagefile);
newimageurl = (await ref.getDownloadURL()).toString();
print("Image Uploaded");
userscollectionref
.doc(globaluserid)
.update({'User_image_link': newimageurl});
print("Link is Updated");

Dart/Flutter: Firebase and Firestore wait for async for loop to complete before continuing

I am trying to submit images to Firebase storage, and then also submit the links to the images to the new record that I am "linking" the images to.
My issue is that if I add image(s) to the data, it seems that the upload makes the rest of the firebase activity not happen (it doesn't make sense to me).
So essentially when I click the FAB then it's supposed to submit the data.
floatingActionButton: FloatingActionButton(
child: Icon(Icons.send),
// SUBMIT THE DATA
onPressed: () async {
setState(() {
// Show the modal spinner until submit is complete
showSpinner = true;
});
// upload images
List<StorageReference> fileRefs = [];
for (var image in imageFiles) {
fileRefs.add(await uploadPic(context, image));
}
// When there are images in the imageFiles array then the below part doesn't run
// but if no images was selected it runs fine, if images are selected they get uploaded
// to firebase storage, but no record gets added. :(
_firestore.collection('InspectionPoints').add({
'project': selectedProject,
// some other fields
'user': loggedInUser.email,
'photoIds': fileRefs.length == 0 ? [] : fileRefs,
'timestamp': DateTime.now(),
//'photoIds' : imageFiles;
});
setState(() {
// <--------------- this still runs
clearForm();
showSpinner = false;
});
} // onPressed
),
I also now tried to put the getting the file refs into an async formula, but it also doesn't work:
// upload images
List<StorageReference> fileRefs = await getFileRefs(context);
And the new function:
Future<List<StorageReference>> getFileRefs(BuildContext context) async {
List<StorageReference> fileRefs = [];
for (var image in imageFiles) {
fileRefs.add(await uploadPic(context, image));
}
return fileRefs;
}
Edit: My Actual uploading code:
Future<StorageReference> uploadPic(BuildContext context, File image) async {
StorageReference firebaseStorageRef = FirebaseStorage.instance.ref().child(basename(image.path));
StorageUploadTask uploadTask = firebaseStorageRef.putFile(image);
StorageTaskSnapshot taskSnapshot = await uploadTask.onComplete;
setState(() {
print('File: ${image.path} uploaded to the cloud');
showInSnackBar('File: ${image.path} uploaded to the cloud');
});
return taskSnapshot.ref;
}
To upload an image to Firebase Storage, you can use the code below.
Future upLoadImage(_providedFile, String folderName, String imageName) async{
//_providedFile -> FILE THAT CONTAINS IMAGE
//folderName -> FOLDER NAME IN FIREBASE STORAGE
//imageName -> NAME OF THE IMAGE THAT WILL BE SAVED ON FIREBASE STORAGE
//FILE UPLOAD LOCATION
//IF YOU DON'T WANT TO ADD IMAGE IN A FOLDER JUST REMOVE
//".child(folderName)" from the line
StorageReference reference = firebasestorage.ref().child(folderName).child('$imageName.jpg');
StorageUploadTask uploadTask = reference.putFile(_providedFile); await
uploadTask.onComplete;
}
I hope this will help you.
You cannot save the type StorageReference to firebase cloud, and due to that type failing to submit the whole submit fails, but because the images are uploaded separately from the data entry, they are already there by the time the data entry fails to submit.
The fix was to convert the StorageReference to string via the .path property.

How to pass the gallery image to another Statefull Screen in flutter

Here is my code:
Future getImage() async {
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
_image = image;
print('Image Path $_image');
});
}
The image returned from ImagePicker.pickImage(...) is a File object that you can pass around to other widgets, just like you would with any other object. For example, consider this snippet, where NewPage takes a File image object as a parameter:
Future<void> navigateOnImage() async {
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => NewPage(image)));
}
Another option may be to convert the File object to a more handy object for your use case, such as a UInt8List object, with the File.readAsBytes method. Then you can pass that around just like in the example above.
Check out this flutter.dev article for more info on passing data when navigating.