Flutter Firebase Storage Upload Stream - firebase

I'm uploading multiple images to Cloud Storage, I've read I can tap into the stream to display an upload progress bar however I can't see a way to do this.
Future<dynamic> postImage(Asset imageFile) async {
String fileName = DateTime.now().toString();
StorageReference reference = FirebaseStorage.instance.ref().child(fileName);
StorageUploadTask _uploadTask =
reference.putData((await imageFile.getByteData()).buffer.asUint8List());
StorageTaskSnapshot storageTaskSnapshot = await _uploadTask.onComplete;
return storageTaskSnapshot.ref.getDownloadURL();
}

There's an example of how to use the firebase-storage plugin in the FlutterFire repo, that is pretty handy.
That example pretty much performs calls on the StorageUploadTask task that you have, to determine the upload state and progress.
String get status {
String result;
if (task.isComplete) {
if (task.isSuccessful) {
result = 'Complete';
} else if (task.isCanceled) {
result = 'Canceled';
} else {
result = 'Failed ERROR: ${task.lastSnapshot.error}';
}
} else if (task.isInProgress) {
result = 'Uploading';
} else if (task.isPaused) {
result = 'Paused';
}
return result;
}
For determining progress, it uses:
String _bytesTransferred(StorageTaskSnapshot snapshot) {
return '${snapshot.bytesTransferred}/${snapshot.totalByteCount}';
}
Which is then used in the build() method like this:
#override
Widget build(BuildContext context) {
return StreamBuilder<StorageTaskEvent>(
stream: task.events,
builder: (BuildContext context,
AsyncSnapshot<StorageTaskEvent> asyncSnapshot) {
Widget subtitle;
if (asyncSnapshot.hasData) {
final StorageTaskEvent event = asyncSnapshot.data;
final StorageTaskSnapshot snapshot = event.snapshot;
subtitle = Text('$status: ${_bytesTransferred(snapshot)} bytes sent');
} else {
subtitle = const Text('Starting...');
}
So this takes the events stream of the task and gets the progress information from there.

Related

Firebase Realtime Databae adds another layer of data

For some reason, my Firebase Realtime Database adds another layer when encoding my data. I am new to using Firebase services, so maybe I entered an incorrect link or smh. -N-1sGl-7VrhyIG7PdDa should not appear. I have a slight idea of why it's happening, but I don't know how to access that last part. Thanks in advance!
Future<void> AddUserGoals(
String userId, String kcal, String p, String c, String f, BuildContext context) async {
final url = Uri.parse(
'https://recipier-e1139-default-rtdb.europe-west1.firebasedatabase.app/usersData/$userId/userGoals.json');
try {
print(kcal);
final response = await http.post(
url,
body: json.encode(
{
'currentBalance': kcal,
'protein': p,
'carbs': c,
'fats': f,
},
),
);
var decodedData = json.decode(response.body) as Map<String, dynamic>;
print(decodedData['currentBalance']);
if (decodedData['error'] == null) {
balance = decodedData['currentBalance'];
} else {
showDialog(
context: context,
builder: (ctx) => const AlertDialog(
title: Text('An error accured'),
content: Text('Please try again later.'),
),
);
}
notifyListeners();
} catch (err) {
rethrow;
}
}
void didChangeDependencies() {
if (_runsForFirstTime == true) {
setState(() {
_isLoading = true;
});
User? user = FirebaseAuth.instance.currentUser;
Provider.of<RecipeProvider>(context).fetchProducts();
Map<String, dynamic> initialData =
ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>;
Provider.of<DiaryProvider>(context, listen: false)
.AddUserGoals(user!.uid, initialData['kcal']!, initialData['p']!,
initialData['c']!, initialData['f']!, context)
.then((_) {
setState(() {
_isLoading = false;
});
});
}
_runsForFirstTime = false;
super.didChangeDependencies();
}
When you call http.post() you tell the REST server to create a new resource (with a unique ID) under the path, so that's what Firebase does.
If you want the server to write the data you pass at the path, use http.put().
Also see:
What is the difference between POST and PUT in HTTP?

Firestore Listerner not working as expected

My web application requires me to use firestore listener for a document jkl. Instead of printing the updated value once, it is repeatedly printing the value even though there is no update in the document jkl.
void switchListener() async
{
_listener = Firestore.instance
.collection('abc')
.document('def')
.collection('ghi')
.document('jkl')
.snapshots()
.listen((data) => listenerUpdate(data));
}
void listenerUpdate(data)
{
String number = data['URL'];
setState(() {
_totalDocs = number;
});
}
Can I get some help on this.
Updated
The listener is activated only after clicking on a button.
onPressed: () {
switchListener();
},
void switchListener() async {
_listener = Firestore.instance
.collection('abc')
.document('def')
.collection('jkl')
.document('mno')
.snapshots()
.distinct()
.listen((data) => listenerUpdate(data));
_listener.cancel();
}
void listenerUpdate(data) {
String number = data['physicianNote'];
String url = data['signedURL'];
setState(() {
_totalDocs = number;
_signedurl = url;
});
print("totalDoc: "+_totalDocs);
print("url: "+_signedurl);
js.context.callMethod("open", [signedurl]);
}
You can try to add the distinct() method after the snapshots() method which
skips data events if they are equal to the previous data event. You can find out more from the official docs.
void switchListener() async
{
_listener = Firestore.instance
.collection('abc')
.document('def')
.collection('ghi')
.document('jkl')
.snapshots()
.distinct() // Will only emit if `snapshots()` emits different data
.listen((data) => listenerUpdate(data));
}
void listenerUpdate(data)
{
String number = data['URL'];
setState(() {
_totalDocs = number;
});
}

how do I create a progress bar in flutter firebase

I am having trouble in creating a progress bar to indicate the process of me uploading an image to firebase storage.
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);
}
This is the code that I have written to upload the image and getting the image URL.
Hope someone can help me up thanks!
you can listen to the events on your uploadTask.
Here:
uploadTask.events.listen((event) {
setState(() {
_progress = event.snapshot.bytesTransferred.toDouble() /
event.snapshot.totalByteCount.toDouble();
});
}).onError((error) {
// do something to handle error
});
Now you can just display the progress like this:
Text('Uploading ${(_progress * 100).toStringAsFixed(2)} %')
To create a progress bar:
LinearProgressIndicator(
value: _progress,
)
Hope that helps.
The answer by Ayush Shekhar is correct, but outdated on some parts due to the rapid updating on firebase plugins.
On top of state class
double? _progress;
...
In the upload method, you can setup like this.
uploadTask.snapshotEvents.listen((event) {
setState(() {
_progress =
event.bytesTransferred.toDouble() / event.totalBytes.toDouble();
print(_progress.toString());
});
if (event.state == TaskState.success) {
_progress = null;
Fluttertoast.showToast(msg: 'File added to the library');
}
}).onError((error) {
// do something to handle error
});
You can get the download link like this.
uploadTask.then((snap) async {
final downloadURL = await snap.ref.getDownloadURL();
print(downloadURL);
});
...
Use the _progress in UI like this.
if (_progress != null)
LinearProgressIndicator(
value: _progress,
minHeight: 2.0,
color: Constants.primaryColor,
),
Use Future Builder and pass this getImage inside future builder Future Builder Example
or You can use Modal Progress HUD
Check this, it will work for both video and images:
final fil = await ImagePicker().pickVideo(source: ImageSource.gallery);
final file = File(fil!.path);
final metadata = SettableMetadata(contentType:"video/mp4");
final storageRef = FirebaseStorage.instance.ref();
final uploadTask = storageRef
.child("images/path/to/video")
.putFile(file, metadata);
// Listen for state changes, errors, and completion of the upload.
uploadTask.snapshotEvents.listen((TaskSnapshot taskSnapshot) {
switch (taskSnapshot.state) {
case TaskState.running:
final progress =
100.0 * (taskSnapshot.bytesTransferred / taskSnapshot.totalBytes);
print("Upload is $progress% complete.");
break;
case TaskState.paused:
print("Upload is paused.");
break;
case TaskState.canceled:
print("Upload was canceled");
break;
case TaskState.error:
// Handle unsuccessful uploads
break;
case TaskState.success:
print("Upload is completed");
// Handle successful uploads on complete
// ...
break;
}
});

How to store image in fire base and store url in firestore

i want to send coupon card to fire store that contain ( name - address - coupon ) and i want to make user set an specific image for every single card
that's my FireStoreService file
class FireStoreService {
FireStoreService._internal();
static final FireStoreService firestoreService = FireStoreService._internal();
Firestore db = Firestore.instance ;
factory FireStoreService() {
return firestoreService;
}
Stream<List<Coupon>> getCoupon() {
return db.collection('coupon').snapshots().map(
(snapshot) => snapshot.documents.map(
(doc) => Coupon.fromMap(doc.data, doc.documentID),
).toList(),
);
}
Future<void> addCoupon(Coupon coupon) {
return db.collection('coupon').add(coupon.toMap());
}
Future<void> deleteCoupon(String id) {
return db.collection('coupon').document(id).delete();
}
Future<void> updateCoupon(Coupon coupon) {
return db.collection('coupon').document(coupon.id).updateData(coupon.toMap());
}
}
and this is Coupon Model file
class Coupon {
final String id;
final String storeName;
final String storeLink;
final String couponCode;
Coupon(
{this.id, this.storeName, this.storeLink, this.couponCode});
Coupon.fromMap(Map<String, dynamic> data, String id)
: storeName = data["storeName"],
storeLink = data['storeLink'],
couponCode = data["couponCode"],
id = id;
Map<String, dynamic> toMap() {
return {
"storeName": storeName,
'storeLink': storeLink,
'couponCode': couponCode,
};
}
}
and this is Image Picker code and it's work fine and picked up the image
Future getImage() async {
try {
File image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
image = image;
});
} catch (e) {
print(e);
}
}
any help ?
This is a function that asks for an imageFile. If you run your code (getImage function): pass the image variable to the uploadImage function.
String myUrl = await uploadImage(file);
Then you can use setData or updateData to put the url in the database.
Firestore.instance.collection('books').document()
.setData({ 'title': 'title', 'url': '$myUrl' })
final StorageReference storageRef = FirebaseStorage.instance.ref();
Future<String> uploadImage(imageFile) async {
StorageUploadTask uploadTask =
storageRef.child("myPath&Name.jpg").putFile(imageFile);
StorageTaskSnapshot storageSnap = await uploadTask.onComplete;
String downloadURL = await storageSnap.ref.getDownloadURL();
return downloadURL;
}

Upload multi images to firestore using flutter and get it's download URLs and save all of URLs to firebase

I have a form that have images to upload , when the user try to press on "Submit" button i'm trying to upload list of images to firestore and get all of its URLs and then submit a form to "x" collection in firebase but the writing on "x" collocation done before upload the images and get it's URLs.
I thinks the problem with (async,await).
Appreciate to help me.
List<File> imagePaths= new List() ;
List<String> imageURL= new List() ;
Future<FirebaseUser> getUser() async {
return await _auth.currentUser();
}
Future<void> uploadPic(File _image) async {
String fileName = basename(_image.path);
StorageReference firebaseStorageRef =
FirebaseStorage.instance.ref().child(Random().nextInt(10000).toString()+fileName);
StorageUploadTask uploadTask = firebaseStorageRef.putFile(_image);
var downloadURL = await(await uploadTask.onComplete).ref.getDownloadURL();
var url =downloadURL.toString();
imageURL.add(url); // imageURL is a global list that suppose to contain images URLs
print("\n ---------------------------\n downloadURL :"+ url);
print("\n ---------------------------\n imageURL :"+ imageURL.toString());
}
Submit(BuildContext context) {
//imagePaths is list of file
imagePaths.add(Front_image);
imagePaths.add(Back_image);
imagePaths.forEach((x) => {
uploadPic(x)
});
getUser().then((user) {
crudObj.addNew({
'uid': user.uid,
'name': name,
'images':imageURL,
}).then((result) {
Navigator.pop(context);
}).catchError((e) {
print(e);
});
});
}
You should call your Submit only once your upload task is complete. I would recommend implementing something like this:
Stream uploadImageToFirebaseStorage(File image, String fullPath) {
final StorageReference firebaseStorageRef =
FirebaseStorage.instance.ref().child(fullPath);
StorageUploadTask task = firebaseStorageRef.putFile(image);
return task.events;
}
And then listen to this Stream and only then submit:
uploadImageToFirebaseStorage(
image, 'path/imagename.jpg'
).listen((data) async {
StorageTaskEvent event = data;
if (data.type == StorageTaskEventType.success) {
String downloadUrl = await event.snapshot.ref.getDownloadURL();
await Submit(title, imageUrl: downloadUrl);
return true;
}
if (data.type == StorageTaskEventType.failure) return false;
});
Please take note that I did not re-write your code, I am sharing a possible implementation.

Resources