I have this function that checks if a document exists or not, if exists returns true otherwise false :
Future<bool> checkMissingId(String id) async {
String str = id.toLowerCase();
String letter = str[0];
String path = letter + "/" + str;
try {
final snapShot =
await FirebaseFirestore.instance.collection(path).doc(str).get();
if (snapShot == null || !snapShot.exists) {
return true;
} else
return false;
} catch (e) {
print(e);
return false;
}
}
But when I call it from here (after save and validate form ) :
Future<void> _submit() async {
//Create Artist and send it to the database
if (_validateAndSaveForm()) {
await checkMissingId(userNameF);
}
}
All freezes and it opens a new file called "errors_patch.dart" with this exception :
static _doThrowNew(int assertionStart, int assertionEnd, Object? message)
native "AssertionError_throwNew";
I think the problem is that checkMissingId is of type Future and maybe I'm not handling futures in the right way...But the error highlights also await FirebaseFirestore.instance.collection(path).doc(str).get(); so I don't know exactly how isolate the problem.
This is the stack :
assert(isValidCollectionPath(collectionPath),
await FirebaseFirestore.instance.collection(path).doc(str).get();
with a red quotes :
_AssertionError ('package:cloud_firestore/src/firestore.dart': Failed assertion: line 74 pos 12: 'isValidCollectionPath(collectionPath)': a collection path must point to a valid collection.)
await checkMissingId(userNameF);
Maybe it depends by wrong document path from Firestore?
I think you are passing the wrong collection name in this line:
final snapShot = await FirebaseFirestore.instance.collection(path).doc(str).get();
because, your path variable is this:
String path = letter + "/" + str; // I guess here is something wrong
Your collection name would be a fixed string as shown below in this screenshot (comments, posts, users etc):
Related
I am trying to delete a specific item from a database given only its element values. However, I keep getting the error message "The type 'Stream<QuerySnapshot<Object?>>' used in the 'for' loop must implement Iterable" over the items.snapshot() section. What am I doing wrong there, because I thought that it would get me all the snapshots of the documents? The deleteName, Type, and Location are all String variables that I defined earlier
CollectionReference items = FirebaseFirestore.instance.collection('items');
Object deleteUser() {
// Call the user's CollectionReference to add a new user
if (name != "" && type != "" && location != "") {
for (var doc in items.snapshots()) {
if (doc.data['name'] == deleteName &&
doc.data['type'] == deleteType &&
doc.data['location'] == deleteLocation) {
doc.delete();
}
}
return items;
} else {
return "There was a null error";
}
}
Your code is a little confusing, if your getting items from Firestore, you will want to map it to an object for iterating through.
Item(
String id; //Give an id value for document ID in firestore
String name;
String type;
String location;
);
//Get Items
CollectionReference itemsCollection = FirebaseFirestore.instance.collection('items');
List<Item> items = itemsCollection.snapshots().map(itemsSnapshot)
//Map Items
List<Item> itemsSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map((DocumentSnapshot doc) {
Map<String, dynamic> data = doc.data() as Map<String, dynamic>;
return Item(
id: doc.reference.id, //this is your reference to firestore
name: data['name'],
type: data['type'],
location: data['location'],
);
}).toList();
}
//Iterate through list of items
for(Item item in items){
//do any checks you want here
if(item.name == "Nobody"){
//delete document
itemsCollection.doc(item.id).delete();
}
}
Your items is a CollectionReference object, and calling snapshots() returns a Stream, which is you can't loop over with a for loop.
My guess is that you're looking to use get instead of snapshots, so that you can then await the result and process it:
CollectionReference itemsCol = FirebaseFirestore.instance.collection('items');
var itemsSnapshot = await itemsCol.get();
for (var doc in itemsSnapshot.docs) {
...
}
I have a flutter app which tries to compare two sets of app version numbers, the first version number is stored locally using hive and the second version number is from firestore. I can fetch the data from firestore but I cannot get to compare both since it takes a while to fetch data from firestore.
This is the code to fetch data from firestore
late final Box detailsBox;
#override
void initState() {
super.initState();
detailsBox = Hive.box('appDetails');
updateApplication();
}
CollectionReference groceries =
FirebaseFirestore.instance.collection('updates');
late String? name;
late String? version;
late String? downloadUrl;
getData() {
groceries.orderBy('name').snapshots().listen((gets) {
try {
for (var gettt in gets.docs) {
name = gettt['name'] ?? 'null';
version = gettt['version'] ?? 'null';
downloadUrl = gettt['download url'] ?? 'null';
debugPrint('name: $name');
debugPrint('version: $version');
debugPrint('downloadUrl: $downloadUrl');
_addInfo(name!, version!, downloadUrl!); }
} catch (e) {
print(e);
}
});
}
This is the code to compare the version numbers
int getExtendedVersionNumber(String version) {
List versionCells = version.split('.');
if (kDebugMode) {
print(versionCells);
}
versionCells = versionCells.map((i) => int.parse(i)).toList();
return versionCells[0] * 10000 + versionCells[1] * 100 + versionCells[2];
}
Future compareData() async {
await getData();
String localName = detailsBox.get('name');
String localVersion = detailsBox.get('version');
String downloadLink = detailsBox.get('downloadLink');
debugPrint(
'Info retrieved from detailsBox below:\n $localName\n ($localVersion) \n $downloadLink');
debugPrint(
'Info retrieved from firebase below:\n $name\n ($version) \n $downloadUrl');
int version1Number = getExtendedVersionNumber(localVersion); // return 102003
int version2Number = getExtendedVersionNumber(version!); // return 102003
if (kDebugMode) {
print(version1Number == version2Number);
print(version1Number > version2Number);
print(version1Number < version2Number);
}
if (version2Number > version1Number) {
debugPrint('true');
debugPrint(downloadUrl);
}
}
When it gets to this point debugPrint( 'Info retrieved from firebase below:\n $name\n ($version) \n $downloadUrl'); I get the late initialization error [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: LateInitializationError: Field 'name' has not been initialized.
How can I modify the code such that when it runs I can account for the time it takes to get data then finally compare the versions
Just change
late String? name;
to
String? name;
As above I need create a function that returns "true" if a document exists, otherwise "false".
If the document doesn't exists then It need to be created before the function ends.
When I run it I have this exception :
Unhandled Exception: 'package:cloud_firestore/src/firestore.dart': Failed assertion: line 129 pos 12:
'isValidDocumentPath(documentPath)': a document path must point to a valid document.
Is pretty easy to understand that I'm not checking if the path exists before getting the collection but I don't know how to handle it.
This is the code:
Future<bool> checkMissingId(String id) async {
String str = id.toLowerCase();
String letter = str[0];
final snapShot =
await FirebaseFirestore.instance.collection(letter).doc(str).get();
if (snapShot == null || !snapShot.exists) {
//if not exists then create it
final _service = FirestoreService.instance;
_service.setData(
path: letter + str,
data: {'id': id},
);
return true;
} else // it already exists, return false
return false;
}
EDIT : new code but still doesn't work :
Future<bool> checkMissingId(String id) async {
String str = id.toLowerCase();
String letter = str[0];
String path = letter + "/" + str;
print(path);
try {
final snapShot =
await FirebaseFirestore.instance.collection(path).doc(str).get();
if (snapShot == null || !snapShot.exists) {
return true;
} else
return false;
} catch (e) {
print(e);
return false;
}
}
Future<bool> setId(String id) async {
String str = id.toLowerCase();
String letter = str[0];
String path = letter + "/" + str;
final _service = FirestoreService.instance;
try {
final snapShot =
await FirebaseFirestore.instance.collection(path).doc(str).get();
if (snapShot == null || !snapShot.exists) {
_service.setData(
path: path,
data: {'id': id},
);
return true;
} else
return false;
} catch (e) {
//print(e);
_service.setData(
path: path,
data: {'id': id},
);
return true;
}
}
Assuming id = "PaninoAvvelenato" :
I want to check if exists the document on path "p/paninoavvelenato", if not I need to create it.
Instead of using FirestoreService.
Future<bool> setId(String id) async {
String str = id.toLowerCase();
String letter = str[0];
try {
final snapShot = await FirebaseFirestore.instance.collection(letter).doc(str).get();
if (snapShot.exists) {
return false;
} else {
await FirebaseFirestore.instance.collection(letter).doc(str).set({'id': id});
return true;
}
} catch (e) {
// TODO: Do something clever.
return true;
}
}
It looks like document for path str is not exist and FirebaseFirestore.instance.collection(letter).doc(str).get(); throw exception
so better to place this code inside :
try {
// code that might throw an exception
FirebaseFirestore.instance.collection(letter).doc(str).get();
}
on Exception1 {
// code for handling exception
}
catch Exception2 {
// code for handling exception
}
I have this code where am supposed to upload an image and get the downloaded url but whenever i do that I get this error
my url is String url;. So please why is this not working as it is supposed to
PS
I checked other website to learn how to properly upload but it keeps giving me an error or is there a better way to do this.
My code image
uploadTask.whenComplete(()async{
url = await refs.getDownLoadURL();
....
});
Since it returns a Future you need to wait for it to be accessed
Example :
Future<String> createFolder(String folderName) async {
final dir = Directory(
'${(io.Platform.isAndroid ? await getExternalStorageDirectory() //FOR ANDROID
: await getApplicationSupportDirectory() //FOR IOS
)!.path}/$folderName');
var status = await Permission.storage.status;
if (!status.isGranted) {
await Permission.storage.request();
}
if ((await dir.exists())) {
return dir.path;
} else {
dir.create();
return dir.path;
}
}
Future<String> getIslamiSahittoBookFilePath(String savename) async {
Future<String> s = createFolder("Islami_Sahitto");
String filePath = await s;
Map<Permission, PermissionStatus> statuses = await [
Permission.storage,
//add more permission to request here.
].request();
io.File? f = null;
if (statuses[Permission.storage]!.isGranted) {
Directory? dir = await DownloadsPath.downloadsDirectory();
if (dir != null) {
String savePath = "${dir.path}/$filePath/$savename";
f = new io.File(savePath);
if (await f.exists()) {}
}
}
return f.toString();
}
Now this block You can use AnyWhere : Future String, to String :
bool isPreviousDownloaded = false;
String previousFilePath = "null";
getIslamiSahittoBookFilePath(fileNameToDownload).then((value) {
if (value != null) {
setState(() {
isPreviousDownloaded = true;
previousFilePath = value;
});
}
});
I just added a new "Translations" table to the database init and wrote all the crud methods. Commands db.query and db.insert work just fine, but as tried inside updateVersion() either db.delete or db.update throw the missing plugin error :
ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: MissingPluginException(No implementation found for method update on channel com.tekartik.sqflite).
After a while I realised that also older two tables are now throwing the same error on delete and update command. Looks like adding a new table broke it all.. I tried flutter clean but nothing changed, so I uninstalled and reinstalled the app, but I still get same errors. I then commented out new table and reinstalled the app but still get the errors..I also tried invalidating the cache a restart but still the same..
On iPhone dough it does not throw any error..
In iOS CoreData kinda behaves the same when you change something.. but with a fresh app install it all resets.. here it seems that the db is still written on disk..
How do I make sure I erased it? I tried await deleteDatabase(path);.
Any Idea of what's going on?
As always thank you very much for your time and help.
This is the db:
class DBProvider {
//1.Create a private constructor that can be used only inside the class :
DBProvider._();
static final DBProvider db = DBProvider._();
//2.Setup the database
//Next we will create the database object and provide it with a getter
//where we will instantiate the database if it’s not (lazy initialization).
static Database _database;
Future<Database> get database async {
if (_database != null) return _database;
// await Sqflite.devSetDebugModeOn(true);
// if _database is null we instantiate it
_database = await initDB();
return _database;
}
// If there is no object assigned to the database,
// we use the initDB function to create the database.
// In this function, we will get the path for storing the database
// and create the desired tables:
initDB() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "Fixit.db");
//TODO: tried deleting
// await deleteDatabase(path); // still errors
return await openDatabase(path, version: 1, onOpen: (db) {},
onCreate: (Database db, int version) async {
await db.execute("CREATE TABLE Route("
"routeId TEXT,"
"routeName TEXT,"
"routeDistance TEXT,"
"routeDuration TEXT,"
"coordinates TEXT"
")");
await db.execute("CREATE TABLE Alarm("
"alarmId TEXT,"
"alarmName TEXT,"
"enabled BOOL,"
"repeatWeekdays TEXT,"
"time TEXT,"
"sound TEXT,"
"routeName TEXT"
")");
// await db.execute("CREATE TABLE Translations("
// "version TEXT"
// ")");
});
}
}
and these are the CRUD methods:
class DefaultsDbRepository {
var database = DBProvider.db.database;
Future<int> checkVersion() async {
final db = await database;
try {
var res = await db.query('Translations');
assert(res != null);
int version = res != null ? int.parse(res.first['version']) : 0;
print('checkVersion() db version is: $version');
return version;
} catch (err) {
print(err);
}
}
saveVersion({int version}) async {
print('saveVersion() version to save is : $version');
assert(version != null);
final db = await database;
try {
Map<String, dynamic> map = {'version': version};
db.insert("Translations", map);
} catch (e) {
print(e);
}
}
updateVersion({int newVersion, int oldVersion}) async {
print('updateVersion() version to save is : $newVersion');
assert(newVersion != null);
final db = await database;
db.delete("Translations");
try {
// Map<String, dynamic> map = {'version': newVersion};
//
// db.insert("Translations", map);
// db.update("Translations", map); // [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: MissingPluginException(No implementation found for method update on channel com.tekartik.sqflite)
// db.update("Translations", map,
// where: "version = ?", whereArgs: [oldVersion]);
} catch (e) {
print(e);
}
}
}
Finally made it work again. After quite a few, uninstall, flutter clean and reinstalls I saw the problem. I was using ' instead of " in my db methods.
So I changed'ed from:
saveVersion({int version}) async {
print('saveVersion() version to save is : $version');
assert(version != null);
final db = await database;
try {
Map<String, dynamic> map = {'version': version};
db.insert("Translations", map);
} catch (e) {
print(e);
}
}
to:
saveVersion({int version}) async {
print('saveVersion() version to save is : $version');
assert(version != null);
final db = await database;
try {
Map<String, dynamic> map = {"version": version};
db.insert("Translations", map);
} catch (e) {
print(e);
}
}
and all commands are working as expected.
Hope this will be of help to others.
Cheers.