Firestore databse, upload and download files - flutter

I want to create button which will allow me to upload images to specific database collection, I manually created a collection and added some random pictures there which I'm using for slider in the app.
Here is code for fetching them from this database:
initSliderImages() async {
var result = await FirebaseFirestore.instance.collection('slider_images');
result.snapshots().listen((data) {
List imgs = [];
data.docChanges.forEach((change) {
var imageData = change.doc.data();
String image = imageData['url'];
imgs.add(CachedNetworkImage(fit: BoxFit.cover, imageUrl: image));
});
setState(() {
images = imgs;
});
});
}
And here is Slider code:
sList images = [];
Widget slider() {
return Container(
child: SizedBox(
height: 200.0,
width: fullWidth(context),
child: (images != null && images.length > 0)
? Carousel(
images: images,
dotSize: 3.0,
dotSpacing: 10.0,
dotColor: Colors.red,
indicatorBgPadding: 5.0,
dotBgColor: Colors.grey.withOpacity(0.5),
// dotBgColor: Colors.transparent,
borderRadius: true,
moveIndicatorFromBottom: 180.0,
noRadiusForIndicator: true,
)
: JumpingDotsProgressIndicator(
fontSize: 40.0,
color: Colors.blue,
),
),
);
}
So I want to create button which will alow me to upload images to this database collection, and also to alow me to remove them from database collection, maybe in some other page to create grid view which will display images from this database collection, and to be able to remove image from them.

Related

Flutter Firebase Pagination Problem to Scrolling Top

I made a social media application using Flutter Firebase, and like every social media application, I have a stream of posts shared by users on the home screen. At first, I didn't have any problems, but as the number of data increased, I started to have problems especially getting photos. Later I found out that this was because I was getting all the data at once and decided to use Pagination. I have successfully used Pagination and I also started using Cached Network Image to load my photos faster. But I still have such a problem in the flow. When I scroll the screen to the bottom, the data is loaded at the limit I set, in the example my limit is 12, so I have no problem when scrolling down the screen, but when I want to quickly scroll the screen up, it tries to load all the data again, the system is having too much difficulty, I can't load it at the end and the application gives a lost connection error and closes itself.
In my opinion, the same thing should happen when we swipe the screen up, just as the data is loaded piece by piece as much as the limit number we set when swiping down the screen.
Otherwise, this problem that I am experiencing occurs.
Do you know any solution for this?
This is my code for Pagination;
getData() async {
var Ref1 = (widget.post != null)
? _firestore
.collection("users")
.doc(widget.post["profileID"])
.collection("Datas")
.orderBy("uploadTime", descending: true)
.limit(perpage)
: null;
setState(() {
loadingProducts = true;
});
var reponse = await Ref1.get();
listt = reponse.docs;
lastDocument = reponse.docs[reponse.docs.length - 1];
setState(() {
loadingProducts = false;
});
}
getmoreData() async {
if (moreDataAvailable == false) {
return;
}
if (gettingmoreData == true) {
return;
}
setState(() {
gettingmoreData = true;
});
var Ref1 = (widget.post != null)
? _firestore
.collection("users")
.doc(widget.post["profileID"])
.collection("Datas")
.orderBy("uploadTime", descending: true)
.startAfterDocument(lastDocument)
.limit(perpage)
: null;
var reponse = await Ref1.get();
if (reponse.docs.length < perpage) {
moreDataAvailable = false;
}
lastDocument = reponse.docs[reponse.docs.length - 1];
listt.addAll(reponse.docs);
setState(() {});
setState(() {
gettingmoreData = false;
});
}
And this is my Builder;
GridView.builder(
controller: scrollController,
physics: ScrollPhysics(),
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
itemCount: listt.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () =>
navigateToDetail(listt[index]),
child: Hero(
tag: (listt[index]["foto"] != null)
? NetworkImage(
listt[index]["foto"])
: AssetImage(
"assets/images/n_image.jpg"),
child: Container(
child: Column(
mainAxisAlignment:
MainAxisAlignment.end,
children: [
Container(
height: size.height * 0.078,
width: double.infinity,
decoration: BoxDecoration(
borderRadius:
BorderRadius.only(
bottomRight:
Radius.circular(
10.0),
bottomLeft:
Radius.circular(
10.0),
),
color: Colors.grey[600]
.withOpacity(0.5)),
child: Center(
child: AutoSizeText(
"${listt[index]["name"]}",
textAlign:
TextAlign.center,
style: GoogleFonts.lora(
textStyle: TextStyle(
color: Colors.white,
fontSize: 15,
),
),
maxLines: 2,
),
),
),
],
),
margin: EdgeInsets.all(5.0),
decoration: BoxDecoration(
image: DecorationImage(
image: (listt[index]
["foto"] !=
null)
? OptimizedCacheImageProvider(
listt[index]["foto"])
: AssetImage(
"assets/images/n_image.jpg"),
fit: BoxFit.cover,
),
color: Colors.white,
borderRadius:
BorderRadius.circular(10.0),
),
),
),
);
},
),
And im listening controller in initstate with this;
scrollController.addListener(() {
double maxScroll = scrollController.position.maxScrollExtent;
double currentScroll = scrollController.position.pixels;
double delta = MediaQuery.of(context).size.height * 0.25;
if (maxScroll - currentScroll <= delta) {
getmoreTarif();
}
});
Your current code tracks the last document of the current results and then calls startAfterDocument with that document to get the next set of results. This works for scrolling forward, but not when scrolling backward. To paginate backwards, you'll also need to track the first document of the current results and then call endBeforeDocument with that document.

There is a problem converting the unint8list image in Flutter

I am working on uploading the uint8List type image to Firestore and get it again.
I have converted to String to upload to Firestore.
Future _pageDrawScreen() async {
// screenshot pakage
Uint8List? _previewImage = await _testController.capture(); // get uint8List image data
if (_previewImage == null) {
logger.d("_previewImage null");
}
// convert to String
_pageModel!.previewImage = _previewImage.toString();
// Firestore update
}
Then, I take the image data again, convert it, and output it as a memory image.
Widget _pageContainer(int index, PageModel pageModel) {
// get image and convert
Uint8List? previewImage;
if (pageModel.previewImage != null) {
List<int> list = pageModel.previewImage!.codeUnits;
previewImage = Uint8List.fromList(list);
// logger.d(previewImage);
}
return ListTile(
title: pageModel.previewImage != null
? ExtendedImage.memory(
previewImage!,// error!
width: 125,
height: 125,
fit: BoxFit.cover,
// check image state
loadStateChanged: (state) {
switch (state.extendedImageLoadState) {
case LoadState.loading:
return const SizedBox(
width: 50,
height: 50,
child: CircularProgressIndicator(
color: Colors.red,
),
);
case LoadState.completed:
return null;
// Failed error!!
case LoadState.failed:
return const Material(
color: Colors.white,
child: Icon(
Icons.cancel,
color: Colors.red,
size: 50,
),
);
}
},
)
: Container(
width: 125,
height: 125,
color: Colors.white,
child: const Center(
child: CircularProgressIndicator(),
),
),
);
}
However, this image is checked as an error state. Is the conversion of Uint8List wrong?
Expected Results (When using the uint8list image as it is,
):
Actual Results:
import
import 'dart:convert';
for uploading
Uint8List? _previewImage = await _testController.capture();
if(_previewImage != null){
String previewImage = base64.encode(_previewImage);
}
after fetching the image data
Uint8List previewImage = base64.decode(previewImage);

how to show circular progress indicator while uploading multiple images to firebase using two screens

i am making a house management app i have to upload images of the property alongside other data related to the property so i am using two screens one for the general info about the house and the second one specifically to upload images
Form screen
Image Upload Screen
from the upload screen i am returning back a list of images to the form screen
// i am waiting for the list in the form screen
images = await Navigator.push(context, MaterialPageRoute(builder: (context) => AddPictures()));
// i am returning the list back from the upload screen
Navigator.pop(context,imageStrings);
I am failing to show circular progress indicator for some reason beyond my capacity to know itried all ways i know
this is the rest of the code
//outiside the widdget build i have two lists
List<XFile> imagesXFiles = []; //for raw image files from the gallery or camera
List<String> imageStrings = []; //for image links from the firebase storage
body: isLoading == true ? CircularProgressIndicator() : Column(
children: [
Expanded(
//the first grid is a button to let the user access camera or gallery
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 2.0,
mainAxisSpacing: 2.0
),
itemCount: imagesXFiles.length + 1,
itemBuilder: (BuildContext context, int index) {
return index == 0 ? GestureDetector(
onTap: (){
// a function to pick images and add store them to the list "imagesXFiles"
_showPicker(context);
},
child: Container(
decoration: BoxDecoration(
color: Colors.black12,
borderRadius: BorderRadius.circular(5.0),
),
child: Icon(
Icons.add,
color: Colors.black,
size: 30.0,
),
),
): Container(
child: Image(
image: FileImage(File(imagesXFiles[index-1].path)),
fit: BoxFit.fill
),
);
},
),
),
TextButton(
onPressed: ()async{
// for some reason the circular progress doesn't work i dont understand why
setState(() {
isLoading = true;
});
imageStrings = await uploadImages(imagesXFiles).whenComplete(() {
setState(() {
isLoading = false;
Navigator.pop(context,imageStrings);
});
});
},
child: Text("Upload",style: TextStyle(color: Colors.black,fontSize: 25),)),
],
),
here is the upload function that uploads the images to firebase
Future<List<String>> uploadImages(List<XFile> imagesXFiles) async {
imagesXFiles.forEach((image) async {
final storageRef = storage.ref().child(Random().nextInt(100).toString());
await storageRef.putFile(File(image.path));
String imageURL = await storageRef.getDownloadURL();
imageStrings.add(imageURL);
firebaseFirestore
.collection("housePictures")
.add({
"imageURL" : imageURL,
});
});
return imageStrings;
}
You can use forEach with Future as below.
await Future.forEach(imagesXFiles, (image) async {
final storageRef = storage.ref().child(Random().nextInt(100).toString());
await storageRef.putFile(File(image.path));
String imageURL = await storageRef.getDownloadURL();
imageStrings.add(imageURL);
FirebaseFirestore.instance
.collection("housePictures")
.add({
"imageURL" : imageURL,
});
});
You can’t use forEach statement in an async operation. It is not going to wait. Use a normal for statement. Example: for(var item in items) etc. That should fix your issue. If you really want to use a for each you need to use Future. foreach see this thread -> How to Async/await in List.forEach() in Dart

Upload multiple file to fire store with progress bar in flutter

I would like to upload more than one file to fire-store while showing a progress indicator. Am trying to achieve this by looping over the files chosen by the user. The files are picked using the flutter filePicker package.
for (var file in choosenFiles) {
String fileName = file.name;
String? filePath = file.path;
File creatFile = File(filePath!);
UploadTask task = FirebaseStorage.instance
.ref('post_files/${value.id}-$fileName')
.putData(creatFile.readAsBytesSync());
task.snapshotEvents.listen((event) {
setState(() {
progress = ((event.bytesTransferred.toDouble() /
event.totalBytes.toDouble() *
100)
.roundToDouble());
});
});
}
for the progress indicator, i use a circularProgressIndicator to listen to the value of variable progress.
Center(
child: SizedBox(
height: 100,
width: 100,
child: CircularProgressIndicator(
value: progress / 100,
backgroundColor: Colors.green,
color: Colors.red,
strokeWidth: 8,
),
),
)
What could be the problem?. Will be grateful for any help. thanks.

flutter, trying to update a picture from Storage

I'm trying to upload, and update a picture from firebase storage. But for some reason this is not working properly.
This would be the scenario; A user take a picture from his camera, so this picture is uploaded to my storage, so after that I get the url picture, so I can use this like NetworkImage(url) to update the picture.
This is what's happening currently:
Let's say I don't have any picture.
I update my profile with a new picture. (This works perfectly).
Now let's say I want to update my profile picture, so I take another picture, let's call it A so I upload this one.
Nothing happens, picture doesn't change.
But If I try again with a B picture, for some reason the picture is updated with the A picture.
This would be my code and how I'm facing this feature.
I have the next method which is invoked when a user click over his picture. I wait for the result (value) so when I got the result I upload the picture. Then, when the picture is uploaded, I just call to setState and save the url into the _imageUrl variable.
After that, I change the "profilePic" attribute from my data base to true.
String _imageUrl = 'assets/profileDefault.jpg';
bool profilePicture = false;
io.File profilePic;
Future getFromCamara() async {
await ImagePicker().getImage(source: ImageSource.camera).then((value) {
profilePic = io.File(value.path);
FirebaseStorage.instance.ref().child('picture').putFile(profilePic);
}).then((result) {
var ref = FirebaseStorage.instance.ref().child('picture');
ref.getDownloadURL().then((loc) => setState(() => _imageUrl = loc));
});
try {
FirebaseFirestore.instance
.collection('Usuarios')
.doc(uid)
.update({'profilePic': true});
} catch (e) {
print(e.toString());
}
So now, using a StreamBuilder, I get the result of profilePic from my DataBase, if is True, I download the URL, and if don't, I just use the Asset default's pic.
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection('Usuarios').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot1) {
if (!snapshot1.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
List<DocumentSnapshot> users = snapshot1.data.docs;
for (DocumentSnapshot user in users) {
if (user.id == userID) {
profilePicture = user['profilePic'];
}
}
if (profilePicture) {
FirebaseStorage.instance
.ref()
.child('picture')
.getDownloadURL()
.then((loc) => _imageUrl = loc);
} else {
_imageUrl = 'assets/profileDefault.jpg';
}
return Stack(
alignment: Alignment.center,
children: <Widget>[
Positioned(
top: 0,
child: Container(
color: Color.fromRGBO(255, 0, 0, 70),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * .55,
),
),
Positioned(
top: MediaQuery.of(context).size.height * .015,
left: 15,
right: 15,
child: Container(
width: MediaQuery.of(context).size.height * .90,
height: 300,
padding: EdgeInsets.only(bottom: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GestureDetector(
onTap: () => _setPic(context),
child: CircleAvatar(
radius: 79,
backgroundColor: Color(0xFFFFFFFFF),
child: CircleAvatar(
radius: 75,
backgroundImage: profilePicture
? NetworkImage(_imageUrl)
: AssetImage(_imageUrl),
),
),
),
// More code...
What I'm doing wrong? Why my picture isn't getting updated?
Updated:
I tried this, so I can save in pictureTemp the picture's bytes, calling to setState to rebuild the widget and put the picture with Image.memory(pictureTemp). But it doesn't seem to work.
Uint8List pictureTemp;
io.File profilePic;
Future getFromCamara() async {
var pickedFile = await ImagePicker().getImage(source: ImageSource.camera);
var image = await pickedFile.readAsBytes();
FirebaseStorage.instance
.ref()
.child('picture')
.putFile(io.File(pickedFile.path));
setState(() {
pictureTemp = image;
});
child: CircleAvatar(
radius: 75,
backgroundImage: profilePicture
? pictureTemp == null
? AssetImage(_imageUrl)
: Image.memory(pictureTemp)
: AssetImage(_imageUrl),
),
Here when the image is picked you should directly set it or you can set it after the firebase upload is finished successfully.
But rather than loading an image from Firebase URL after the upload, you can directly load from the picked file as follows;
image = Image.memory(await pickedFile.readAsBytes())
This will instantly set the image and will save you a read call to Firebase. You should always try to minimize the Firebase Reads whenever possible.