How can I save data to permanent storage and retrieve it? - flutter

I want to save data on device which it should be visible to user when he/she went to the desired path from file manager.
now I have two problem:
1-
like social networks which save images in storage ( not cache images ) , I want to save my images into desired path storage and the images should be accessable like cacheNetworkImage.
so what should I do ?
because I loads many images , caching them is not a good solution because it occupy a high amount of RAM.
2-
this scenario is like Previous but I download some media into my storage with some IDs.
but I a want to read only IDs and if user click on that file , execute it.
so How can I access name of existing files in that file of storage path ?
thanks to community. any help is appreciated.

You can chose between basic path_provider library and more advanced network_to_file_image library.
In the first one you can list your files and read them this way:
void checkDirs() async{
Directory tempDir = await getApplicationDocumentsDirectory();
List<FileSystemEntity> directory = tempDir.listSync();
directory.forEach((x) => debugPrint(x.path));
}
Future<File> readImage(String path) async{
return File(path);
}
And render it this way:
Container(child: FutureBuilder<File>(
future: readImage("some_path"),
builder: (BuildContext context,
AsyncSnapshot<File> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.data != null) {
return Image.file(
snapshot.data,
);
} else if (snapshot.error != null) {
return const Text(
'Error Picking Image',
textAlign: TextAlign.center,
);
} else {
return const Text(
"No photo"
);
}
},
))
The second one is well documented on the link.

Related

Issue in firestore database

I want to show current user data in my flutter app. But it print on screen " No data found".
This my database data
That error also happened error
My security rule
enter image description here
Here is my code
Container(
child: StreamBuilder(
stream: FirebaseFirestore.instance.collection("user3").where("id",isEqualTo:FirebaseAuth.instance.currentUser!.uid).snapshots(),
builder: (BuildContext context,AsyncSnapshot<QuerySnapshot> snapshot){
if(!snapshot.hasData){
return Text("Loading please wait........");
}
if (snapshot.hasData && snapshot.data!.docs.length > 0) {
DocumentSnapshot userData = snapshot.data!.docs[0];
// Build the widget using the userData
} else {
return Center(child: Text("No data found"));
}
return Container();
},
),
),
The long numeric values (e.g. "167582...") in your database screenshot do not look like a UID that any of the Firebase Authentication providers would generate.
Add this code right before you query the database:
print(FirebaseAuth.instance.currentUser!.uid)
This will show you the value that you're querying for, which (given my opening statement) probably looks quite different from the value in your database.
If that is indeed the case, the problem starts when you write the document. At that point you'll want to make sure that you write the value of FirebaseAuth.instance.currentUser!.uid to the id field.

Flutter getx: How to send data between pages from FirestoreQueryBuilder

I'd Like to get data in home screen of my flutter app, where I have list of OfferCards, these are generated from firestore via FirestoreQueryBuilder in my homeView like this
FirestoreQueryBuilder<OfferData>(
pageSize: 10,
query: FirebaseFirestore.instance
.collection('Offers')
.orderBy('CreatedAt', descending: true)
.withConverter<OfferData>(
fromFirestore: ((snapshot, options) =>
OfferData.fromJson(snapshot.data()!)),
toFirestore: (value, options) => value.toJson()),
builder: (context, snapshot, _) {
if (snapshot.isFetching) {
return const Center(
child: CircularProgressIndicator(color: Colors.greenAccent),
);
} else if (snapshot.hasError) {
return const Center(
child: Text('Server error'),
);
} else if (snapshot.docs.isEmpty) {
return const Center(
child: Text('No offers'),
);
} else {
return ListView.builder(
itemBuilder: (context, index) {
final hasReachEnd = snapshot.hasMore &&
index + 1 == snapshot.docs.length &&
!snapshot.isFetchingMore;
if (hasReachEnd) {
snapshot.fetchMore();
}
final post = snapshot.docs[index].data();
homeController.offers[index] = post;
return OfferCardView();
},
itemCount: snapshot.docs.length);
}
},
)
As on the end of this example, inside HomeController I have Map of int and UserData, which is filled with all offers. Each offerCardView has Get.find to HomeController to have access to this map. And here's my question, how do I determine inside of OfferCardView and later in OfferView(after tapping on given OfferCardView) which entry from map is being clicked on/view filled with. I don't know how to acomplish this, I'm aware that using Map here is bad decision, but I don't have clue how this should be done
The better practice is passing each document data with its index to the OfferView() constructor, so for every OfferCardView() that will be clicked, OfferView() will be opened with that data.
This ensures that your data will not rely on the GetxController availability, since depending on GetxController to exchange data like this could simply break.
For example :
While your app is growing and somewhere the controller is deleted either by Getx or manually using Get.delete() ( or you needed to call multiple controllers with different tags ), then Get.find() will not find that controller or mistake it, this leads to unexpected behaviors, which will put you in a hard time to find out what went wrong in your project.
Using GetPage, if you're required to assign the model data property, you could make a placeholder model for that data by default where we would say like :
There is no data so we showed you that placeholder alternative data page with this data.
This gives the user at least an overview of what's happening, not just a direct crash for the app.
I would say it's a good practice for the user experience.
You can share variables from other controllers onto another controller by using GetX Dependency Injection
On binding , add the controller you want to add as a dependency
Get.lazyPut<OfferCardsController>(() => OfferCardsController());
then in the controller
var offerCardsController = Get.find<OfferCardsController>();
you can now access variables from the OfferCardsController onOfferController
e.g
offerCardsController.variableFromCardsController;

Does Streambuilder store the data after app restarts?

I have a simple streambuilder that reads the users document, and I use it to show some of the user's data. My question is, would this streambuilder re-read the document everytime the user restarts the app? If, yes is there any way to prevent the streambuilder from re-reading it everytime the user restarts the app unless there is a change in the document?
StreamBuilder(
stream: _firestore
.collection('users')
.doc(_auth.currentUser!.uid)
.snapshots(),
builder:
(context, AsyncSnapshot<DocumentSnapshot<Object?>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator(
color: isDarkMode ? Colors.white : Colors.black,
);
}
if (snapshot.hasData) {
if (snapshot.data!.exists) {
snapshot.data!['serviceEnabled'] == true
? startServices()
: null;
return Center(
child: Column(
This streambuilder is on the homepage of the app, I show some of the user's data on the homepage.
How would the database know whether there's a change in the document without reading that document?
If you can answer that, you can probably write a query to match that same condition.
For example, if each document has a lastUpdated field, you could just get the updated document with:
_firestore
.collection('users')
.where('lastUpdated', '>', timestampWhenYouLastReadDocuments)
.get()
Aside from that query to update the cache, you could then get the documents from the cache in other places in your app.

How do I cache images from firebase in flutter?

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',
),
),
),
);
`

Display Firestore map data in Flutter

I am building a password manager and I'm trying to save on Firestore reads by displaying data in Map. The idea is to have a document (for each password category) and have the passwords stored as maps in it. See example screenshots here:
The idea is that users click on each password (map) and the app takes them to the relevant one using indexes. I then want the data from each map to be sorted separately so that they can live in their own Text widgets. But I haven't seen any possibility of displaying map data in Flutter yet.
I've tried some of the examples from here but haven't been able to display the data from the map - https://github.com/fireship-io/flutter-base/blob/master/lib/main.dart
Any suggestions would be much appreciated.
new StreamBuilder(
stream: Firestore.instance
.collection('users')
.document(userDocSTRING.toString())
.collection(userPassSTRING.toString())
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
// If no data is present, display a waiting indicator to the user
// If Null, red screen displays
return Center(child: CircularProgressIndicator());
} else {
// Debug print for Ryan
// print(snapshot.data[0].data.toString());
return new ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot documentSnapshot =
snapshot.data.documents[index];
return ListTile(
leading: Container(
child: IconButton(
icon: Icon(Icons.content_copy),
color: lightCardCOLOR,
title: Text(documentSnapshot['Name']
As you can see from the code above, fetching fields from documents is easy enough but I don't want each password to have it's own document. I want a document to hold multiple Maps which corresponds to a password. So documentSnapshot['Name'] I want to be the name or value of a map
I'll suggest that your attempt to "save on Firestore reads" is making it more difficult for you to model and display the data. It also won't scale, since the maximum size of a document is 1MB. Consider storing each password in a different document to make this easier. Document reads are not really very expensive, and the Firebase SDK will cache them locally to avoid the cost of reads when queried.