Ive created a flutter app for keeping recipes.
Every recipe has a list of ingredient.
I create the recipe using a Recipe model and inside i have a list of ingredient.
By default when i save the above Recipe model to Firebase Firestore the Ingredient list is saved as an array.
I plan on expanding the concepts over time and would like to store the ingredient in a sub collection.
I am obviously able to iterate through the questions and add them as a document to their own collection, however this seems messy and likely to cause me problems in the future.
Is there a way in which i can specify that child models are created as a sub collection rather than an array?
Below is what im using currently to write the data
class FirestoreService {
FirestoreServiceNew._();
static final instance = FirestoreServiceNew._();
Future<void> setData({
#required String path,
#required Map<String, dynamic> data,
bool mergeBool = false,
}) async {
try {
final reference = FirebaseFirestore.instance.doc(path);
print('$path: $data');
await reference.set(data);
} catch (e) {
print('error: $e');
}
}
Future<void> bulkSet({
#required String path,
#required List<Map<String, dynamic>> datas,
bool merge = false,
}) async {
final reference = FirebaseFirestore.instance.doc(path);
final batchSet = FirebaseFirestore.instance.batch();
print('$path: $datas');
}
Future<void> deleteData({#required String path}) async {
final reference = FirebaseFirestore.instance.doc(path);
print('delete: $path');
await reference.delete();
}
Stream<List<T>> collectionStream<T>({
#required String path,
#required T builder(Map<String, dynamic> data, String documentID),
Query queryBuilder(Query query),
int sort(T lhs, T rhs),
}) {
Query query = FirebaseFirestore.instance.collection(path);
if (queryBuilder != null) {
query = queryBuilder(query);
}
final Stream<QuerySnapshot> snapshots = query.snapshots();
return snapshots.map((snapshot) {
final result = snapshot.docs
.map((snapshot) => builder(snapshot.data(), snapshot.id))
.where((value) => value != null)
.toList();
if (sort != null) {
result.sort(sort);
}
return result;
});
}
Stream<List<T>> requestStream<T>({
#required String path,
#required T builder(Map<String, dynamic> data, String documentID),
Query queryBuilder(Query query),
int sort(T lhs, T rhs),
}) {
Query query = FirebaseFirestore.instance.collection(path);
if (queryBuilder != null) {
query = queryBuilder(query);
}
final Stream<QuerySnapshot> snapshots = query.snapshots();
return snapshots.map((snapshot) {
final result = snapshot.docs
.map((snapshot) => builder(snapshot.data(), snapshot.id))
.where((value) => value != null)
.toList();
if (sort != null) {
result.sort(sort);
}
return result;
});
}
Stream<T> documentStream<T>({
#required String path,
#required T builder(Map<String, dynamic> data, String documentID),
}) {
final DocumentReference reference = FirebaseFirestore.instance.doc(path);
final Stream<DocumentSnapshot> snapshots = reference.snapshots();
return snapshots.map((snapshot) => builder(snapshot.data(), snapshot.id));
}
}
and my model
class Recipe {
String id;
List<Ingredient> ingredients;
String title;
String description;
Timestamp createdOn;
Recipe(
{this.id = '',
this.ingredients,
this.title,
this.description,
this.createdOn});
Recipe.fromData(Map<String, dynamic> json, String docid) {
id = docid ?? '';
title = json['title'];
description = json['description'];
createdOn = json['createdOn'];
if (json['questions'] != null) {
ingredients= new List<Ingredients>();
json['ingredients'].forEach((v) {
questions.add(new Ingredients.fromData(v, null));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['description'] = this.description;
data['title'] = this.title;
data['createdOn'] = this.createdOn;
if (this.ingredients != null) {
data['ingredients'] = this.ingredients.map((v) => v.toJson()).toList();
}
return data;
}
}
You can build a path to documents nested in subcollections by alternating calls to collection() and doc(), which return CollectionReference and DocumentReference objects respectively.
FirebaseFirestore.instance
.collection("top-level-collection")
.doc("document-id-1")
.collection("nested-subcollection")
.doc("document-id-2")
Related
I created a class called 'tasks' and implement it. I edit gradle files for connecting with firebase. I gain some errors in my code. So please help me to solve this error.
class _MyHomePageState extends State<MyHomePage> {
late List<Task> items;
FirestoreService fireServ = new FirestoreService();
late StreamSubscription<QuerySnapshot> todoTasks;
#override
void initState() {
super.initState();
items= [];
todoTasks.cancel();
todoTasks=fireServ.getTaskList().listen((QuerySnapshot snapshot){
final List<Task> tasks=snapshot.docs
.map((documentSnapshot) => Task. fromMap(documentSnapshot.data))
.toList();
setState(() {
this.items = tasks;
});
});
}
This is my firestore class service
class FirestoreService {
Future<Task> createTODOTask(String taskname, String taskdetails,String taskdate,String tasktime,String tasktype) async {
final TransactionHandler createTransaction = (Transaction tx) async {
final DocumentSnapshot ds = await tx.get(myCollection.doc());
final Task task = new Task(taskname, taskdetails,taskdate,tasktime,tasktype);
final Map<String, dynamic> data = task.toMap();
await tx.set(ds.reference, data);
return data;
};
return FirebaseFirestore.instance.runTransaction(createTransaction).then((mapData) {
return Task.fromMap(mapData);
}).catchError((error) {
print('error: $error');
return null;
});
}
Stream<QuerySnapshot> getTaskList({int offset=0, int limit=0}) {
Stream<QuerySnapshot> snapshots = myCollection.snapshots();
if (offset != null) {
snapshots = snapshots.skip(offset);
}
if (limit != null) {
snapshots = snapshots.take(limit);
}
return snapshots;
}
}
You need to change:
documentSnapshot.data
to this:
documentSnapshot.data()
.data() is a method and not a property of the DocumentSnapshot object.
i'm trying to query a firestore collection with a dynamic path (user specific), it works hardcoded, but not dynamic with a variable, someone know the issue and can help?
Thanks in advance
final CollectionReference addressCollection =
Firestore.instance.collection('users/r9qClctByGXinYAmB2MqQNctgd53/addresses');
works.
This not:
final CollectionReference addressCollection =
Firestore.instance.collection('users/$userId/addresses');
userId is = r9qClctByGXinYAmB2MqQNctgd53
Full FirestoreDatabase code:
class FirestoreDatabase {
final _service = FirestoreService.instance;
static String userId;
void setUserId(uid) {
userId = uid;
}
final CollectionReference addressCollection =
Firestore.instance.collection('users/$userId/addresses');
// Adresses List Stream
Stream<List<Address>> get addressesStream {
return addressCollection.snapshots().map(_addressListFromSnapshot);
}
List<Address> _addressListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.documents.map((doc) {
return Address.fromMap(doc.data);
}).toList();
}
}
I'm having a simple problem which is how to get specific values from database Firebase.
For example, I want to get the value of "name" and put it in text. How can I do that? Can you write a detailed code?
class _HomePageState extends State<HomePage> {
String myuid;
FirebaseUser currentUser;
// To get id
void _loadCurrentUser() {
FirebaseAuth.instance.currentUser().then((FirebaseUser user) {
setState(() { // call setState to rebuild the view
this.currentUser = user;
});
});
}
#override
void initState() {
super.initState();
_loadCurrentUser();
}
#override
Widget build(BuildContext context) {
myuid = currentUser.uid;
var getname;
Future<void> getName() async {
DocumentSnapshot ds =
await Firestore.instance.collection('users').document(myuid).get();
getname = ds.data['name'];
}
Try
String name;
Future<null> getName() async {
DocumentSnapshot document = await Firestore.instance.collection('users').document(FirebaseUser().uid).get();
name = document.data['name']
}
This is how you can get data from the Firestore Database Document once
val docRef = db.collection("users").document("mhPtwy..........")
docRef.get()
.addOnSuccessListener { document ->
if (document != null) {
Log.d(TAG, "DocumentSnapshot data: ${document.data}")
} else {
Log.d(TAG, "No such document")
}
}
.addOnFailureListener { exception ->
Log.d(TAG, "get failed with ", exception)
}
This is a kind of cheeky way to get the data and store it in a variable
var name;
Future<void> getName(){
DocumentSnapshot ds = await
Firestore.instance.collection('users').document(uid).get();
name = ds.data['name']
}
then just throw that in your text field
Text(name);
I am building an app in which a user can create multiple projects.
I want each project to have its unique database, since although columns (there will be 6-7 columns) name will be the same, but the values under columns for a new project will be different.
How to create new SQFlite database each time when a user creates a new project?
Or is there some other better way to achieve to achieve this?
Here is my code for the database that i have manually created if you want to go through it:
class DatabaseHelper {
static final DatabaseHelper _instance = new DatabaseHelper.internal();
factory DatabaseHelper() => _instance;
final String tableUser = "userTable";
final String columnId = "id";
final String columnRuns = "runs";
static Database _db;
Future<Database> get db async {
if (_db != null) {
return _db;
}
_db = await initDb();
return _db;
}
DatabaseHelper.internal();
initDb() async {
Directory documentDirectory = await getApplicationDocumentsDirectory();
String path = join(documentDirectory.path,
"maindbnew.db"); //home://directory/files/maindb.db
var ourDb = await openDatabase(path, version: 1, onCreate: _onCreate);
return ourDb;
}
void _onCreate(Database db, int newVersion) async {
await db.execute(
"CREATE TABLE $tableUser($columnId INTEGER PRIMARY KEY, $columnRuns INTEGER)");
}
//CRUD - CREATE, READ, UPDATE, DELETE
//Insertion
Future<int> saveUser(User user) async {
var dbClient = await db;
int res = await dbClient.insert("$tableUser", user.toMap());
return res;
}
//Get Users
Future<List> getAllUsers() async {
var dbClient = await db;
var result = await dbClient.rawQuery("SELECT * FROM $tableUser");
return result.toList();
}
Future<int> getCount() async {
var dbClient = await db;
return Sqflite.firstIntValue(
await dbClient.rawQuery("SELECT COUNT(*) FROM $tableUser"));
}
Future<User> getUser(int id) async {
var dbClient = await db;
var result = await dbClient
.rawQuery("SELECT * FROM $tableUser WHERE $columnId = $id");
if (result.length == 0) return null;
return new User.fromMap(result.first);
}
Future<int> deleteUser(int id) async {
var dbClient = await db;
return await dbClient
.delete(tableUser, where: "$columnId = ?", whereArgs: [id]);
}
Future<int> updateUser(User user) async {
var dbClient = await db;
return await dbClient.update(tableUser, user.toMap(),
where: "$columnId = ?", whereArgs: [user.id]);
}
Future close() async {
var dbClient = await db;
return dbClient.close();
}
}
class User {
int _runs;
int _id;
User(this._runs);
User.map(dynamic obj) {
this._runs = obj['runs'];
this._id = obj['id'];
}
int get runs => _runs;
int get id => _id;
Map<String, dynamic> toMap() {
var map = new Map<String, dynamic>();
map["runs"] = _runs;
if (id != null) {
map["id"] = _id;
}
return map;
}
User.fromMap(Map<String, dynamic> map) {
this._runs = map["runs"];
this._id = map["id"];
}
}
P.S. I just have one column right now, I'll add more later.
I have object "Lawer" has List of objects "maker"
class Lawer{
final int x1;
final int x2;
final int x3;
final List<Maker>objects
Lawer({this.x1,this.x2,this.x3 ,this.objects});
Map<String, dynamic> toJson() =>
{
'x1':1 ,
'x2':1,
'x3':2,
};
}
class Maker{
final String lawerID;
final String customerID;
final bool connect;
Maker({this.lawerID,this.customerID,this.connect=false});
Map<String, dynamic> toJson() =>
{
'lawerID':lawerID,
'customerID':customerID,
'connect':connect,
};
}
CollectionReference dPReplies=FirebaseFirestore.instance.collection('lawers'); FirebaseFirestore.instance.runTransaction((Transaction tx)async {
var result=await dPReplies.add(lawer.toJson())
});
how to save this object in firebase"save object have some variables and list of objects"
Change this in your class:
Map<String, dynamic> toJson() =>
{
'x1':x1,
'x2':x2,
'x3':x3,
};
You need to capitalize Lawer
var result= await dPReplies.add(Lawer.toJson()) //instead of lawer.toJson