Uploading image into Floating Action Button, set state not working - flutter

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!

Related

Flutter: Async Await Function Not Waiting

I am doing some image processing and building in my app using the Image package. In this case, I am building a LinearProgressIndicator widget that is then converted into an image file, that is then merged into a larger image later on. However, the functions to build and take the image of the progress indicator widget isn't being waited on and the following error is observed
FileSystemException: Cannot open file, path = '/Users//Library/Developer/CoreSimulator/Devices//data/Containers/Data/Application//Library/Caches/bar.png' (OS Error: No such file or directory, errno = 2)
However, the progress indicator file is being created (I can see the cache), and then if I rerunning the image processing it works just fine (because now the file is there). However, I need it work on the first time. Here is what I have for image processing
import 'package:image/image.dart' as ui;
Future<File> instagramChalShare(BuildContext context, double progress) async {
//this is where it does not appear the progressBarPathFunct is being waited on
final testFile = await progressBarPathFunct(context, progress);
ui.Image tester = ui.decodeImage(testFile.readAsBytesSync());
//image is then resized
ui.Image progressResized = ui.Image(600, 90);
ui.drawImage(progressResized, tester);
//now progress bar is merged into larger image
final mergedImage = ui.Image(width, height);
ui.copyInto(mergedImage, progressResized, blend: true);
List<int> imageFile = ui.encodePng(mergedImage);
final imageFilePath = await File('${(await getTemporaryDirectory()).path}/temp.png').create();
print('picture finished $imageFilePath');
return imageFilePath.writeAsBytes(imageFile);
}
Here is the function to build the LinearProgressIndictor, convert it to an image, and create a file where the image is held.
Future<File> progressBarPathFunct(BuildContext context, double progress) async {
final progressFile = File('${(await getTemporaryDirectory()).path}/bar.png');
var indicator = LinearPercentIndicator(
percent: progress > 1.0 ? 1.0 : progress,
width: context.size.width,
lineHeight: 13,
),
barRadius: Radius.circular(20),
);
screenshotController.captureFromWidget(
InheritedTheme.captureAll(context, Material(child: indicator)),
).then((capturedProgress) async {
await progressFile.create(recursive: true);
await progressFile.writeAsBytes(capturedProgress);
});
print('progress bar path from functions ${progressFile.path}');
return progressFile;
}
This function is working, but it seems the top one creating the merged Image file is not waiting for this function to finish before attempting to complete.
Edit
I am following the example from the screenshot package to generate an image of a widget that is not on the screen.

ImagePicker never returning value flutter

I'm trying to prompt users of my app to upload photos. However, I'm have an issue. The function where I try to get the gallery image always returns null / never completes.
This i my code:
GestureDetector(
onTap: () async {
final image =
await ImagePicker().pickImage(source: ImageSource.gallery);
if (image != null) {
print(image.path);
print('exists');
}
},
It never prints 'exists' or image.path, I don't know whats wrong.
I have added this to info.plist
<key>NSCameraUsageDescription</key>
<string>For image upload and verification</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>For image upload and ID verification</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app uses the microphone for videos</string>
Package version: ^0.8.4
Would be really thankful for some input!

Why won't my image show up after picking from image_picker in Flutter?

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

loading spinner with image_picker_web (FLUTTER-WEB)

How can I incorporate a loading spinner in a image_picker_web application that only triggers as an image is selected?
The best I have been able to do is a spinner triggered by the 'select image' button, as below, but this shows a spinner when the the directory is opened i.e. before anything is selected, I need it to trigger when the user selects an image and hits 'open' in the directory.
Image pickedImage;
bool isWaiting = false;
pickImage() async {
setState(() {
isWaiting = true;
});
Image fromPicker =
await ImagePickerWeb.getImage(outputType: ImageType.widget);
if (fromPicker != null) {
setState(() {
isWaiting = false;
pickedImage = fromPicker;
});
}
}
RaisedButton(
onPressed: () => pickImage(),
child: isWaiting
? SizedBox(child: Image.asset('assets/spinner.gif'))
: Text('Select Image'),
),
(adapted from the original example: https://pub.dev/packages/image_picker_web/example)
You can use the default material CircularProgressIndicator(), if you want.
If you want it a little more colorful with google color accents, you can go with my ColoredCircularProgressIndicator() package.
If you want a custom spinner then you have to definitely go with this awesome package - flutter_spinkit
Your implementation is correct, you can also use GestureDetector with Material widget for input button customizations.
EDIT -
As mentioned in comment, if you want the spinner to kick-in AFTER the image is picked, you can use then() from getImage's future and a void callback to achieve this.
Image fromPicker = await ImagePickerWeb.getImage(outputType: ImageType.widget).then(() => /* do something after image is picked (change widget to spinner in setState) */ );
I really don't know why you want to kick-in the spinner AFTER not before.
Also, image_picker_web package is now deprecated so use the image_picker package with web check.

Flutter remove image after upload using image_picker package

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;
});
}