I have managed to upload an image using the image picker package as shown below:
final picker = ImagePicker();
Future selectPhoto() async {
final pickedFile = await picker.getImage(source: ImageSource.gallery);
setState(() {
_image = File(pickedFile.path);
});
}
The image has been displayed on the device where I have a remove button which I can use to remove the image and upload another before saving it.
child: Container(
height: height * 0.2,
width: width * 0.2,
child: Image.file(
_image,
fit: BoxFit.fill,
),
),
My question is how can I remove the image from cache as this piece is not working
onTap: () {
setState(() {
imageCache.clear();
print('removes $_image');
});
},
You have a misunderstanding of what the cache is compared to explicitly storing a file in memory with your code. Flutter may store images that you use in widgets in a cache so that they may load faster in the future. imageCache.clear(); does clear the cache and is likely doing it's work in your code. However, your method of checking if the image is still cached is flawed.
imageCache.clear(); only clears the cache, it does not delete the file that you're passing to the Image.file widget. This means that _image(the file variable you have stored in memory) will persist even over the cache clearing. To truly delete this image you could use the delete method or if you just want to stop showing the image, deference the file by setting the file reference to null:
_image = null;
Solved it by calling a method within the button which had set state method where the path was returned back to null
clearimage() {
setState(() {
_image = null;
});
}
Related
I have a floating action button that I want a user to click to take a picture with their camera and then have that image replace the camera icon on the floating action bar button.
Here is the code for my FAB, and including uploading the image to firestore storage.
floatingActionButton: FloatingActionButton.large(
heroTag: "add image",
backgroundColor: const Color(0xFF93C3B9),
child: (imageURL == ' ')
? const Icon(Icons.add_a_photo_outlined)
: Image.network(imageURL),
//open add gear page
onPressed: () async {
// todo: upload an image to Firebase Storage
//Take picture
ImagePicker imagePicker = ImagePicker();
XFile? file = await imagePicker.pickImage(source: ImageSource.camera);
if (file == null) return;
String uniqueFileName =
DateTime.now().millisecondsSinceEpoch.toString();
//Get reference to storage root
Reference referenceRoot = FirebaseStorage.instance.ref();
Reference referenceDirImages = referenceRoot.child('images/$userID');
Reference referenceImageToUpload =
referenceDirImages.child(uniqueFileName);
try {
//upload image
await referenceImageToUpload.putFile(File(file.path));
//get download URL
setState(() async {
imageURL = await referenceImageToUpload.getDownloadURL();
print(imageURL);
});
//upload path to fireStore database
} catch (error) {}
},
),
After the image uploads it's like the set state is not working to replace the icon with the image. The odd part is is I crtl-s and save in Visual Studio Code then the widgets seem to rebuild and then the image is visible there...
So after playing around with my code a bit I decided to edit the above code and take tha await function out of the setState() and make setState() not async anymore:
//get download URL
String tempUrl = await referenceImageToUpload.getDownloadURL();
setState(() {
print("--------- Set State -----------");
imageURL = tempUrl;
print("--------- Set State end -----------");
});
print("New image url $imageURL ------------");
not sure why it works, but this solves my issue.
By your description of the issue, I think you might be using StatelessWidget instead of StatefulWidget.
You see the button change when performing a hotreload because the value of imageURL is correctly changing internally, but you need a StatefulWidget to update the UI also.
Hope it helps!
I'm working on an editor which needs to work on both web and mobile. So the core functionalities will remain same but the UI part will change.
Within that core part I have a portion which works with images. As the File object comes from both dart:io and dart:html I am facing some issues with getter setters.
Where I want to show the image:
Widget buildImage() {
return Image.file(
widget.item!.imageFile,
fit: widget.item!.imageFit,
color: widget.item!.color,
colorBlendMode: widget.item!.blendMode,
alignment: widget.item!.alignment,
gaplessPlayback: true,
excludeFromSemantics: true,
);
}
widget.item!.imageFile is a getter setter that I worked on the mobile counter part:
io.File get imageFile => io.File(_image!.image.filename);
set imageFile(io.File value) => _image!.image.filename = value.path;
But as now I will have to make the code work on the web version as well I tried:
if (isWeb) {
await _pickImageWeb();
} else {
// some code for mobile
}
And the _pickImageWeb() is as below:
_pickImageWeb() async {
final ImagePicker _picker = ImagePicker();
XFile? image = await _picker.pickImage(source: ImageSource.gallery);
if (image != null) {
var imageBytes = await image.readAsBytes();
setState(() {});
}
}
At this point I'm completely lost and I think I went the wrong way. Since till now I was my images from the path using getter setter and now in web the image picking is completely different.
Should I have multiple getter setters for both mobile and web i.e: imageFileMobile and imageFileWeb?
Or how to solve the issue in general. I have seen file_picker but couldn't figure out how to integrate the package in this case.
dart:io doesn't work with web, and vice versa for dart:html, I would suggest using cross_file and store the result in XFile and use it as file.readAsBytes(), because flutter web gives fake paths when selecting files, so going with bytes seems to be the best option IMO.
I have been using image_picker: ^0.8.4+9 for the past couple of months with no problems. Today whenever I try to pick an image the gallery opens and I am able to pick an image, however once thats done the image doesn't appear on the screen. No errors and I update my github with it everyday and checked to see if something was changed but nothing was. I've tried using other versions only to have the same problem. I understand that this is an issue with M1 Macs sometimes, however why would it suddenly stop working using the same version? I have seen other questions similar like this. I have the correct permissions and there hasn't been a thing changed since it was last working. This could be a problem I cannot fix due to it being an iOS issue or something else but I wanted to see if anyone else is having this issue. Can't test on a physical device since I am on iOS beta but still it was working on the sim so that shouldn't matter.
Here is the basic function of getting am image and storing it:
File? pickedImage;
Future pickImage() async {
final _imagePicker = ImagePicker();
final pickedImage =
await _imagePicker.pickImage(source: ImageSource.gallery);
final imageTemp = File(pickedImage!.path);
setState(() => this.pickedImage = imageTemp);
}
Here is the button to initiate the gallery and pick the image:
ElevatedButton(
onPressed: () async {
final _imagePicker = ImagePicker();
final pickedImage = await _imagePicker.pickImage(
source: ImageSource.gallery);
final imageTemp = File(pickedImage!.path);
setState(() => this.pickedImage = imageTemp);
},
child: const Text('Add Image')),
const SizedBox(height: 20),
// The image should show here if not null else the 'No image' text
pickedImage != null
? Image.file(pickedImage!, height: 200, width: 200)
: Text('No Image'),
EDIT: As Vandad linked in the comment below, it seems this is a newer issue. Here is the discussion. github.com/flutter/flutter/issues/98569
By facing image quality not decreased after compressing it multiples time I thought that plugin is not working. But after dumping file on File Storage during debugging, It was Actually Compressed and Image Quality was decreased by 25 %.
But Image is not Updating after setting it's State. I've read many articles and things. I've used all the things I've Found. I tried to clear Flutter's Image Cache and Painting Binding and Stuff. I also tried to empty temporary directory.
imageCache.clearLiveImages();
PaintingBinding.instance.imageCache.clear();
var appDir = (await getTemporaryDirectory()).path;
Directory(appDir).delete(recursive: true);
Here's My Code. First _imagePath is the image it receives and it's displayed properly. When we compress and update the File Path, Image's State is not changed (Image is not changed).
CircleAvatar(
radius: 150,
backgroundImage: FileImage(File(_imagePath)),
),
TextButton(
child: Text(
'Compress',
style: TextStyle(fontSize: 22),
),
onPressed: () {
setState(() async {
// Compressing File
File compressedFile =
await FlutterNativeImage.compressImage(
_imagePath,
quality: 25,
);
_imagePath = compressedFile.path;
});
},
),
I used Value Notifier of flutter which notifies if value changes for image.
I have been trying to do a cache management system in my flutter app. Ideally I want to retrieve images from firebase storage and display them along with other details. I retrieve the snapshots from firestore and have used cachednetworkimage to display the images. But the amount of images I display is a lot and is causing my app to crash. I believe if I was caching the image locally, that problem would be solved. And besides that, I also want to cache json files so that in offline mode, my app will display both the cached images and the other details available in the cache memory.
I want to display posts, which contain username, user profile picture, the image post itself, caption and comments. So the way I retrieve the posts is according to the following...
void fetchFeed() async {
auth.User currentUser = await _repository.getCurrentUser();
User user = await _repository.fetchUserDetailsById(currentUser.uid);
setState(() {
this.currentUser = user;
});
setState(() {
loadingPosts = true;
});
Query query = _firestore.collection("users").doc(user.uid).collection("following").orderBy("uid").limit(perPage);
QuerySnapshot querySnapshot = await query.get();
for (var i = 0; i < querySnapshot.docs.length; i++) {
followingUIDs.add(querySnapshot.docs[i].id);
}
for (var i = 0; i < followingUIDs.length; i++) {
Query posts = _firestore.collection("users").doc(followingUIDs[i]).collection("posts").orderBy("time").limit(perUser);
QuerySnapshot postSnapshot = await posts.get();
lastPost = postSnapshot.docs[postSnapshot.docs.length -1];
for (var i = 0; i < postSnapshot.docs.length; i++) {
feedlist.add(postSnapshot.docs[i]);
}
}
setState(() {
loadingPosts = false;
});
}
And after I retrieved the posts, I put them in a listview and show them sequentially. The problem I am getting is that since the images are not cached locally, I use cached network image widget to display them. And whenever I navigate to another page and return, all the cached network images get reloaded and that puts a big load on the app, which causes it to crash.
CachedNetworkImage(
imageUrl: list[index].data()['imgUrl'],
placeholder: ((context, url) => Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/Black.png'),
fit: BoxFit.cover),
))),
fit: BoxFit.fitWidth,
),
Alternatively, I tried to download the images and save them locally using the following function. And I call the function for every image item I retrieve from firebase. But that just distorts the images for some reason.
Future <Null> downloadFile(String httpPath) async{
final StorageReference ref = await FirebaseStorage.instance.getReferenceFromUrl(httpPath);
final StorageFileDownloadTask downloadTask = ref.writeToFile(file);
final int byteNumber = (await downloadTask.future).totalByteCount;
print(byteNumber);
setState(() => _cachedFile = file);
}
The http path is a download url I got for each image. But I am not sure if this is the best way to download images. Since I don't have a way to know the image file names as they appear in firebase storage, this was my only option.
Can someone tell me an alternative way to download and cache images, and also json files (which contain username, comments, caption) in my case, so that I can show them offline?
For this purpose use cached_network_image package. which also support placeholders and fading images in as they’re loaded.
CachedNetworkImage( imageUrl: 'https://picsum.photos/250?image=9');
Complete Example
`
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: CachedNetworkImage(
placeholder: (context, url) => CircularProgressIndicator(),
imageUrl:
'https://picsum.photos/250?image=9',
),
),
),
);
`