I am making a flutter app using firebase. I have stored my database in firebase cloud firestore. It's a simple medicine reminder app, where the user can add medicines and set reminder for upcoming doses and the app reminds the user to take medicines by sending a notification at the time picked by the user. The data added to the database is not reflected in the app.However, the data is being added in the database, as shown in the firebase console. However, the app homescreen shows the "nothing to add" even if there's data in database. Also the document ID is different from the user ID. I want the document ID to be the same as user UID that's generated during authentication. So that each user's data is mapped with their ID. The document structure in firestore is : One main collection called "Medicines" This collection will have different documents with an ID which will be ther user's user ID. Corresponding to each of these documents, there's be a subcollection called "User-Medicines" which will have all the medicines of that particular user in the form of different documents.
The mediicnes I am adding are all being added in the "Medicines" collection, instead they should be stored in the subcollection "User-Medicines". Similarly for every app user, there should be a seperate "User-Medicines" sub-collection where only their mediicnes will be stored.
Here's the CRUD operations :
final FirebaseFirestore _firestore =
FirebaseFirestore.instance; //initializing firebase firestore
final CollectionReference _mainCollection = _firestore.collection(
'Medicines'); //defining the main collection where all the database information will be stored
class Database {
//this class contains all the crud functions
static String? userID;
//Create
static Future<void> addMedicine({
required String medicineName,
required String dosageAmount,
required int? frequency,
required List<TimeOfDay?> doseTime,
required String instructions,
required String additionalInformation,
}) async {
DocumentReference documentReferencer =
_mainCollection.doc(userID).collection('User-Medicines').doc();
//below line converts the dosetime to firestore compatible format
List<Map<String, int>> timesForFirestore =
MedicineDataConverter.timeOfDayListToMapList(doseTime);
Map<String, dynamic> medicine = {
"Medicine Name": medicineName,
"Dosage Amount": dosageAmount,
"Frequency": frequency,
"Date and Time": timesForFirestore,
"Instructions": instructions,
"Additional Information": additionalInformation,
};
await documentReferencer
.set(medicine)
.whenComplete(() => print('Medicine added to the database'))
.catchError((e) => print(e));
}
//Read
static Stream<QuerySnapshot> readMedicine() {
return _mainCollection.doc(userID).collection('User-Medicines').snapshots();
// CollectionReference medicineCollection =
// _mainCollection.doc(userID).collection('User-Medicines');
// return medicineCollection.snapshots();
}
//Update
static Future<void> updateMedicine({
required String medicineName,
required String dosageAmount,
required int? frequency,
required List<TimeOfDay?> doseTime,
required String instructions,
required String additionalInformation,
required String docID,
}) async {
DocumentReference documentReferencer =
_mainCollection.doc(userID).collection('Medicines').doc(docID);
List<Map<String, int>> timesForFirestore =
MedicineDataConverter.timeOfDayListToMapList(doseTime);
Map<String, dynamic> medicine = {
"Medicine Name": medicineName,
"Dosage Amount": dosageAmount,
"Frequency": frequency,
"Date and Time": timesForFirestore,
"Instructions": instructions,
"Additional Information": additionalInformation,
};
await documentReferencer.update(medicine);
// .whenComplete(() => print('Data updated'))
// .catchError((e) => print(e));
}
//Delete
static Future<void> deleteMedicine({
required String docID,
}) async {
DocumentReference documentReferencer =
_mainCollection.doc(userID).collection('Medicines').doc(docID);
await documentReferencer.delete();
// .whenComplete(() => print('Data deleted'))
// .catchError((e) => print((e)));
}
}
And here's the home screen code:
body: StreamBuilder<QuerySnapshot>(
stream: Database.readMedicine(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {
return const Center(
child: Text('Nothing to show. Add some medicines!'),
);
}
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot medicine = snapshot.data!.docs[index];
return Padding(
padding: const EdgeInsets.all(15.0),
child: ListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
selectedColor: Colors.deepPurple,
iconColor: Colors.white,
textColor: Colors.white,
splashColor: Colors.purple,
title: Text(medicine['medicineName']),
subtitle: Text(medicine['dosageAmount']),
trailing: IconButton(
onPressed: () {
String docID = medicine.id;
deleteConfirmationDialogue(context, docID);
},
icon: const Icon(Icons.delete),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MedicineDetailEditScreen(
medicineDocument: medicine),
),
);
},
),
);
},
);
},
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.deepPurple,
foregroundColor: Colors.white,
splashColor: Colors.purple,
onPressed: () {
//go to addmedicine page
Navigator.of(context).pushNamed(addMedicineRoute);
},
child: const Icon(Icons.add),
),
This question has two sub-questions:
Medicine
collection is same as user's uid, that's generated during authentication, and each user's data being stored in a separate User-Mediicne
sub-collection.I fixed the 2. issue.
In the Database
class, even tough I am accessing the current user's uid, that's not being passed to the addMedicine
function thus the userID
is auto-generated by firebase and as a result we've a new document for each medicine in Medicine
collection and that has a sub-collection User-Medicine
and then finally the data is getting stored in the document in User-Medicine
.
This was fixed by adding a parameter userID
in the addMedicine
function inside the Database class. So the modified addMedicine
function will be like this :
static String? get userID =>
FirebaseAuth.instance.currentUser?.uid;//getting current user's uid
static Future<void> addMedicine({
required String medicineName,
required String dosageAmount,
required int? frequency,
required List<TimeOfDay?> doseTime,
required String instructions,
required String additionalInformation,
required String? userID, //including userID
}) async {
//nothing changes here
}
And I accessed the current user's id from the Database
class and passed it in the addMedicine
function whenever I called it. Like this :
late final String? userId = Database.userID;
Database.addMedicine(
medicineName: medicineNameController.text,
dosageAmount: dosageAmountController.text,
frequency: frequency,
doseTime: doseTimes,
instructions: instructionsController.text,
additionalInformation:
additionalInformationController.text,
userID: userId,
);