storing fetched data into a state component - firebase-storage

the submit function has another function named handleImageAdd() which will push the image collected from the form to the firebase storage.
const handleSubmit = async (e) => {
setSubmitLoading(true)
e.preventDefault()
console.log("submitting form...")
await handleImageAdd()
console.log("form submitted!")
setSubmitLoading(false)
}
this is the handleImageAdd() function,
when the image gets pushed to the storage, i am fetching its download url and storing it into a react state.
but the url don't get stored into the state on first call, it starts storing the url on second call. why is this happening?
const handleImageAdd = async(e) => {
console.log("adding image...")
const storageRef = storage.ref('images')
const fileRef = storageRef.child(image.name)
await fileRef.put(image)
const url = await fileRef.getDownloadURL()
console.log(url)
setImageUrl(url)
console.log("imageurl - ",imageUrl)
console.log("image added!")
}

Related

How to send expo push notification to multiple devices?

I'm trying to send Expo push notifications to multiple devices. I'm retrieving the Expo tokens from Firestore. When I enter the tokens manually, it works! It sends the notification to both devices I'm using, but when I retrieve the data from Firestore, it only sends the notification to one device.
async function sendPushNotification(readx) {
const message = {
to: readx,
sound: "default",
title: "Original Title",
body: "And here is the body!",
data: { someData: "goes here" },
};
const retrieveNetwork = async () => {
try {
//const querySnapshot = await getDocs(collection(db, "cities"));
const q = query(collection(db, "users"));
const querySnapshot = await getDocs(q);
setRead(querySnapshot.docs.map((doc) => doc.data().expoUser));
setReadx(JSON.stringify(read));
} catch (e) {
alert(e);
}
};
The retrieving of data from the firestore seems to be an issue , as your code is using the Snapshot for querying the data ,it should get the token id for both the devices in the loop and then return to the await sync to call the notification function.As per the Firebase documentation on reading multiple documents, you'll see that it uses the data() function on each DocumentSnapshot to get at the fields of that document.
So try to modify accordingly,like use doc.role and doc.token instead of doc.data().role and doc.data().token.
Check this example code below:
let tokenList = []; const userNotificationTokenDocs = await db.collection("userToken").doc(userId).get() .then(querySnapshot => { querySnapshot.forEach((doc) => { console.log(doc.data().Tokens); tokenList.push(doc.data().Tokens); }); return null; });
Also you may try adding the below to your code:
userToken.forEach((token) => { console.log(token); tokens.push(token); });
Checkout these following with similar implementation:
Push notification firestore
Triggering expo sdk to push notification to users
Notification to a collection of token
Array token sending notification
Just solved. Need to change
<Button
title="Press to Send Notification"
onPress={async () => {
await sendPushNotification(expoPushToken);
}}
/>
to
<Button
title="Press to Send Notification"
onPress={async () => {
await sendPushNotification(readx);
}}
/>

Fetch Data from an API and save in an sqlite database Flutter Dart Android

I have a insertUser method that works fine when i click a button or trigger it another way. I have a fetchUser method that fetches users data from an API. I call the fetch method in initState to fetch the data as soon as the app launches and that is also working fine. In my fetchUser method, i loop through the data collected, pass it to a user object and save in a database. I am able to create user objects for each user but cant save it in the database. Here is my code.
Future<List<User>> fetchUser() async {
final response =
await http.get(Uri.parse('https://jsonplaceholder.typicode.com/users'));
if (response.statusCode == 200) {
final parsed = json.decode(response.body).cast<Map<String, dynamic>>();
parsed.forEach((entry) {
String name = entry["name"];
String username = entry["username"];
String passcode = entry["username"];
print("id: $name, username: $username, passcode: $passcode");
var newUser = User(
name: name,
username: username,
passcode: name,
);
print(newUser.passcode);
UserDatabase.instance.insertUser(user); //this is not working
});
return parsed.map<User>((json) => User.fromMapp(json)).toList();
// return parsed.map<User>((json) => User.fromMap(json)).toList();
} else {
throw Exception('Failed to load users');
}
}
The insert method
insertUser(User user) async{
Database db = await instance.database;
return await db.insert(_table_field_agents, user.toUserMap(),conflictAlgorithm: ConflictAlgorithm.ignore
);
}
I call fetchUser in initState, everything works alright except saving the data to the database.

Returning a value from a response stream inside an upload function

I've created a function which uploads an imageFile to Node.js server & AWS Bucket
If I call response.stream.transform(utf8.decoder).listen((value) async {} the value is equal to the CloudFront URL where my picture is stored
I'm trying to extract this URL from my Upload function to use it in my app but I can't find how :
Future<String> upload(File imageFile) async {
String url = '';
[...] // some code
// add file to multipart
request.files.add(multipartFile);
// send
var response = await request.send();
// listen for response
response.stream.transform(utf8.decoder).listen((value) async {
print(value); // prints the URL
url = value; // this value is the url where my picture is stored
// I'd like to use it outside this function
// I thought of returning it but I get an empty string
});
return url;
}
response.stream is a ByteStream. Rather than calling listen, call bytesToString - details here.
So you replace the whole transform section with:
return await response.stream.bytesToString();

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.

Flutter: Refresh image after add

If I have added data to the flutter in firebase, but I want to add more, the image on the added page still appears before the image, how do I refresh it to return to blank as before?
DateTime now = DateTime.now();
String format = DateFormat('dd:mm:yy').format(now);
var fullImageName = 'foto-$format' + '.jpg';
var fullImageName2 = 'foto-$format' + '.jpg';
final StorageReference ref =
FirebaseStorage.instance.ref().child(fullImageName);
final StorageUploadTask task = ref.putFile(image);
// Wait upload task to complete
final StorageTaskSnapshot downloadUrl =
(await task.onComplete);
// Get image uRL
final String url = (await downloadUrl.ref.getDownloadURL());
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
DocumentReference ref = await db
.collection('resep')
.add({'nama': '$nama', 'resep': '$resep', 'image': '$url', 'email' : widget.email});
setState(() => id = ref.documentID);
Navigator.of(context).pop();
}
Let Say above code you have written is in Page2 which is navigated from Page1.
So when you upload the image on firebase storage and pop the page2. Then you can refresh the page as following,
When you push a new page2 from page1 you can have its pop callback.
// To check if you got your callback or not just pass bool data type with MaterialPageRoute
Navigator.push(context, MaterialPageRoute<bool>(builder: (context) => page2()),).then((bool res) {
// check here if you got your callback data or not
if(res!=null && res==true){
// fetch your updated content here
}
});
When you pop your page2 then,
Navigator.of(context).pop(true);