Firebase database is getting structured incorrectly - firebase

I am developing a Flutter app with Firebase Database. This is a chat app. The database structure I need is as the following.
So, I first create a chat room and then add its members. Here please consider the key 900 as a key in chat_room (i know its not in this example), acting as the foreign key in members node. According to the current structure, I can add any number of members to a chat room and I can easily find them without downloading lot of data. This is achievable by the following code.
fire_database_service.dart
import 'package:firebase_database/firebase_database.dart';
class FireDatabaseService
{
final DatabaseReference _chatRoomReference = FirebaseDatabase.instance.reference().child('chat_room');
//Create chat_room
Future<String> createChatRoom({String lastMessage, String chatMode}) async {
var newChatRoomRef = _chatRoomReference.push();
var newChatRoomKey = newChatRoomRef.key;
await newChatRoomRef.set({
'last_message': lastMessage,
'chat_mode': chatMode,
'timestamp': DateTime.now().millisecondsSinceEpoch,
});
return newChatRoomKey;
}
Future<void> addChatMembers(List<ChatMember> chatMembers, String chatRoomID) async
{
for(int i=0; i<chatMembers.length; i++)
{
ChatMember chatMemeber = chatMembers[i];
var newChatMemberReference = FirebaseDatabase.instance.reference().child('members/$chatRoomID/').push();
var newChatMemberKey = newChatMemberReference.key;
await newChatMemberReference.set({
'email': chatMemeber.email,
'userID': chatMemeber.userID,
'user_role': chatMemeber.userRole,
});
}
return null;
}
}
chat.dart
void initState() {
super.initState();
FireDatabaseService firebaseDatabase = FireDatabaseService();
String chatRoomID="";
firebaseDatabase.createChatRoom(
lastMessage: "Test",
chatMode: "Supplier").then((value) async{
print("CHAT ID: "+value);
ChatMember member1 = new ChatMember(
email: "someone#test.com",
userRole: "customer",
userID: 30
);
ChatMember member2 = new ChatMember(
email: "anotherone#test.com",
userRole: "supplier",
userID: 50
);
List<ChatMember> chatMemberList = [member1, member2];
print("BEFORE");
await firebaseDatabase.addChatMembers(chatMemberList, "900");
print("AFTER");
});
}
Please note that in this line await firebaseDatabase.addChatMembers(chatMemberList, "900"); I am hardcoding the value 900. Instead of this, if I pass the unique key of the chat room node, I get this structure, which is completely incorrect of what i need.
I actually need this unique key of chat room to be in members node, so it will act as the foreign key in members node.
Why is this happening and how can I fix this?

I fixed this issue. It was because the chatRoomID is not getting the unique key, it was empty. I fixed it in the following way.
void initState() {
super.initState();
FireDatabaseService firebaseDatabase = FireDatabaseService();
firebaseDatabase
.createChatRoom(lastMessage: "Test", chatMode: "Supplier")
.then((value) async {
print("CHAT ID: " + value);
ChatMember member1 = new ChatMember(
email: "someone#test.com",
userRole: "customer",
userID: 30,
profilePic: "");
ChatMember member2 = new ChatMember(
email: "anotherone#test.com", userRole: "supplier", userID: 50);
List<ChatMember> chatMemberList = [member1, member2];
print("BEFORE");
await firebaseDatabase.addChatMembers(chatMemberList, value);
print("AFTER");
});
}

Related

Flutter - How to retrieve data from firebase and save in sharedpreference

I'm trying to retrieve user name, photourl and email from user and save this info to shared preferences.
What I tried so far:
Future<bool> _saveNameAndEmailPreference(String name, String email) async {
final pref = await SharedPreferences.getInstance();
await pref.setString('name', name);
await pref.setString('email', email);
return pref.commit();
}
Firebase
DatabaseReference reference =
FirebaseDatabase.instance.reference().child("users").child(uid);
reference.once().then((DataSnapshot dataSnapShot) {
var keys = dataSnapShot.value.keys;
var values = dataSnapShot.value;
for (var key in keys) {
Users userinformation = new Users(
values[key]["name"],
values[key]["email"],
values[key]["photourl"],
);
userinformationList.add(userinformation);
}
setState(() {
nametxt = userinformationList[0].name;
emailtxt = userinformationList[1].email;
_saveNameAndEmailPreference(nametxt, emailtxt);
});
});

Updating field in SQLite (Flutter)

Let's use this code snippet as an example. The data model is very simple:
class Dog {
final int id;
final String name;
final int age;
Dog({this.id, this.name, this.age});
}
To update the information, I'm using this function:
Future<void> updateDog(Dog dog) async {
// Get a reference to the database.
final db = await database;
// Update the given Dog.
await db.update(
'dogs',
dog.toMap(),
// Ensure that the Dog has a matching id.
where: "id = ?",
// Pass the Dog's id as a whereArg to prevent SQL injection.
whereArgs: [dog.id],
);
}
await updateDog(Dog(id: 0, name: 'Fido', age: 42));
It works really fine and there aren't any problems. Now the question is, how to update only the field age without using name? So basically I want to do something like this
await updateDog(Dog(id: 0, age: 35));
and expect as a result "name: Figo, age: 35". But instead it removes Fido in null. So I get this as a result: "name: null, age: 35".
The example in the documentation looks like this:
// Update Fido's age and save it to the database.
fido = Dog(
id: fido.id,
name: fido.name,
age: fido.age + 7,
);
await updateDog(fido);
You either approach it with the raw SQL query like in chunhunghan's answer, or
query the Dog with the id, then override the fields, then update.
Why?
Lets look at your update code:
await updateDog(Dog(id: 0, age: 35));
When the update line is called, Dog.toMap() will be called and it will look like you are updating the name to null.
For you to do what you want here is the code:
Future<Dog> getDog(int id) async {
List<Map> result = await database.query(..... whereArgs: [id]);
if (result.length > 0) {
return new Dog.fromMap(result.first);
}
return null;
}
// Now in code
fido = await getDog(id);
// Update Fido's age and save it to the database.
fido = Dog(
id: fido.id,
name: fido.name,
age: 35, //<--
);
await updateDog(fido);
You can copy paste run full code below
Example code has two records to demo update effect
Solution 1 : You can use rawUpdate
code snippet
int count = await db.rawUpdate('UPDATE dogs SET age = ? WHERE id = ?', [35, 0]);
Solution 2 : You can revise toMap to only return id and age
Future<void> updateDog1(Dog dog) async {
// Get a reference to the database.
final db = await database;
// Update the given Dog.
await db.update(
'dogs',
dog.toMap1(),
...
Map<String, dynamic> toMap1() {
return {
'id': id,
'age': age,
};
}
fido = Dog(
id: fido.id,
name: "not care",
age: 35,
);
await updateDog1(fido);
output
I/flutter ( 6570): [Dog{id: 0, name: Fido, age: 42}, Dog{id: 1, name: abc, age: 10}]
I/flutter ( 6570): updated: 1
I/flutter ( 6570): [Dog{id: 0, name: Fido, age: 35}, Dog{id: 1, name: abc, age: 10}]
full code solution 1
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
void main() async {
// Avoid errors caused by flutter upgrade.
// Importing 'package:flutter/widgets.dart' is required.
WidgetsFlutterBinding.ensureInitialized();
final database = openDatabase(
// Set the path to the database. Note: Using the `join` function from the
// `path` package is best practice to ensure the path is correctly
// constructed for each platform.
join(await getDatabasesPath(), 'doggie_database.db'),
// When the database is first created, create a table to store dogs.
onCreate: (db, version) {
return db.execute(
"CREATE TABLE dogs(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)",
);
},
// Set the version. This executes the onCreate function and provides a
// path to perform database upgrades and downgrades.
version: 1,
);
Future<void> insertDog(Dog dog) async {
// Get a reference to the database.
final Database db = await database;
// Insert the Dog into the correct table. Also specify the
// `conflictAlgorithm`. In this case, if the same dog is inserted
// multiple times, it replaces the previous data.
await db.insert(
'dogs',
dog.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<List<Dog>> dogs() async {
// Get a reference to the database.
final Database db = await database;
// Query the table for all The Dogs.
final List<Map<String, dynamic>> maps = await db.query('dogs');
// Convert the List<Map<String, dynamic> into a List<Dog>.
return List.generate(maps.length, (i) {
return Dog(
id: maps[i]['id'],
name: maps[i]['name'],
age: maps[i]['age'],
);
});
}
Future<void> updateDog(Dog dog) async {
// Get a reference to the database.
final db = await database;
// Update the given Dog.
await db.update(
'dogs',
dog.toMap(),
// Ensure that the Dog has a matching id.
where: "id = ?",
// Pass the Dog's id as a whereArg to prevent SQL injection.
whereArgs: [dog.id],
);
}
Future<void> deleteDog(int id) async {
// Get a reference to the database.
final db = await database;
// Remove the Dog from the database.
await db.delete(
'dogs',
// Use a `where` clause to delete a specific dog.
where: "id = ?",
// Pass the Dog's id as a whereArg to prevent SQL injection.
whereArgs: [id],
);
}
var fido = Dog(
id: 0,
name: 'Fido',
age: 42,
);
var fido1 = Dog(
id: 1,
name: 'abc',
age: 10,
);
// Insert a dog into the database.
await insertDog(fido);
await insertDog(fido1);
// Print the list of dogs (only Fido for now).
print(await dogs());
/*
// Update Fido's age and save it to the database.
fido = Dog(
id: fido.id,
name: fido.name,
age: fido.age + 7,
);
await updateDog(fido);
// Print Fido's updated information.
print(await dogs());*/
final Database db = await database;
int count =
await db.rawUpdate('UPDATE dogs SET age = ? WHERE id = ?', [35, 0]);
print('updated: $count');
print(await dogs());
/*// Delete Fido from the database.
await deleteDog(fido.id);
// Print the list of dogs (empty).
print(await dogs());*/
}
class Dog {
final int id;
final String name;
final int age;
Dog({this.id, this.name, this.age});
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'age': age,
};
}
// Implement toString to make it easier to see information about
// each dog when using the print statement.
#override
String toString() {
return 'Dog{id: $id, name: $name, age: $age}';
}
}
full code solution 2
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
void main() async {
// Avoid errors caused by flutter upgrade.
// Importing 'package:flutter/widgets.dart' is required.
WidgetsFlutterBinding.ensureInitialized();
final database = openDatabase(
// Set the path to the database. Note: Using the `join` function from the
// `path` package is best practice to ensure the path is correctly
// constructed for each platform.
join(await getDatabasesPath(), 'doggie_database.db'),
// When the database is first created, create a table to store dogs.
onCreate: (db, version) {
return db.execute(
"CREATE TABLE dogs(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)",
);
},
// Set the version. This executes the onCreate function and provides a
// path to perform database upgrades and downgrades.
version: 1,
);
Future<void> insertDog(Dog dog) async {
// Get a reference to the database.
final Database db = await database;
// Insert the Dog into the correct table. Also specify the
// `conflictAlgorithm`. In this case, if the same dog is inserted
// multiple times, it replaces the previous data.
await db.insert(
'dogs',
dog.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<List<Dog>> dogs() async {
// Get a reference to the database.
final Database db = await database;
// Query the table for all The Dogs.
final List<Map<String, dynamic>> maps = await db.query('dogs');
// Convert the List<Map<String, dynamic> into a List<Dog>.
return List.generate(maps.length, (i) {
return Dog(
id: maps[i]['id'],
name: maps[i]['name'],
age: maps[i]['age'],
);
});
}
Future<void> updateDog(Dog dog) async {
// Get a reference to the database.
final db = await database;
// Update the given Dog.
await db.update(
'dogs',
dog.toMap(),
// Ensure that the Dog has a matching id.
where: "id = ?",
// Pass the Dog's id as a whereArg to prevent SQL injection.
whereArgs: [dog.id],
);
}
Future<void> updateDog1(Dog dog) async {
// Get a reference to the database.
final db = await database;
// Update the given Dog.
await db.update(
'dogs',
dog.toMap1(),
// Ensure that the Dog has a matching id.
where: "id = ?",
// Pass the Dog's id as a whereArg to prevent SQL injection.
whereArgs: [dog.id],
);
}
Future<void> deleteDog(int id) async {
// Get a reference to the database.
final db = await database;
// Remove the Dog from the database.
await db.delete(
'dogs',
// Use a `where` clause to delete a specific dog.
where: "id = ?",
// Pass the Dog's id as a whereArg to prevent SQL injection.
whereArgs: [id],
);
}
var fido = Dog(
id: 0,
name: 'Fido',
age: 42,
);
var fido1 = Dog(
id: 1,
name: 'abc',
age: 10,
);
// Insert a dog into the database.
await insertDog(fido);
await insertDog(fido1);
// Print the list of dogs (only Fido for now).
print(await dogs());
// Update Fido's age and save it to the database.
fido = Dog(
id: fido.id,
name: "not care",
age: 35,
);
await updateDog1(fido);
// Print Fido's updated information.
print(await dogs());
/*final Database db = await database;
int count =
await db.rawUpdate('UPDATE dogs SET age = ? WHERE id = ?', [35, 0]);
print('updated: $count');
print(await dogs());*/
/*// Delete Fido from the database.
await deleteDog(fido.id);
// Print the list of dogs (empty).
print(await dogs());*/
}
class Dog {
final int id;
final String name;
final int age;
Dog({this.id, this.name, this.age});
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'age': age,
};
}
Map<String, dynamic> toMap1() {
return {
'id': id,
'age': age,
};
}
// Implement toString to make it easier to see information about
// each dog when using the print statement.
#override
String toString() {
return 'Dog{id: $id, name: $name, age: $age}';
}
}

I want to fetch current user data from firebase database in my flutter application

My Question is that i want to fetch data of the current user only. but this code below is fetching data of all the users present in my Database. how can i fetch the data of only and only current user.
This is the code with which i am fetching data from firebase(I am using Realtime DataBase).
in this 'node-name' is the field under which my data is being stored.
class ShowDataPage extends StatefulWidget {
#override
_ShowDataPageState createState() => _ShowDataPageState();
}
class _ShowDataPageState extends State<ShowDataPage> {
List<myData> allData = [];
#override
void initState() {
DatabaseReference ref = FirebaseDatabase.instance.reference();
ref.child('node-name').once().then((DataSnapshot snap) {
var keys = snap.value.keys;
var data = snap.value;
allData.clear();
for (var key in keys) {
myData d = new myData(
data[key]['name'],
data[key]['message'],
data[key]['profession'],
);
allData.add(d);
}
setState(() {
print('Length : ${allData.length}');
});
});
}
This is the code from which i am uploading my data to the firebase under the name of 'node-name'. this code is stored in another file and is having another necessary fields also but this is the field which uploads my data to the firebase.
_sendToServer() {
if (_key.currentState.validate()) {
_key.currentState.save();
DatabaseReference ref = FirebaseDatabase.instance.reference();
var data = {
"name": name,
"profession": profession,
"message": message,
};
ref.child('node-name').push().set(data).then((v) {
_key.currentState.reset();
});
} else {
setState(() {
_autovalidate = true;
});
}
}
My data base in firebase looks like given below.
Use the user uid:
ref.child('node-name').child("M5CCSXQo3Upq5OC7y3lw").once()
.then((DataSnapshot snap) {...}
If you don't know the uid and didn't use it, then perform a query by the name fore example.
#override
void initState() {
FirebaseAuth.instance.currentUser().then((user){
fetchUser(user);
});
}
fetchUser(FirebaseUser user)
{
DatabaseReference ref = FirebaseDatabase.instance.reference();
ref.child('node-name').child(user.uid).once().then((DataSnapshot snap) {
var keys = snap.value.keys;
var data = snap.value;
allData.clear();
for (var key in keys) {
myData d = new myData(
data[key]['name'],
data[key]['message'],
data[key]['profession'],
);
allData.add(d);
}
setState(() {
print('Length : ${allData.length}');
});
});
}
you can use like this
...
ref.child('node-name').child('/** current_user_key **/').once()
...

i want to send the data under the userid and also fetch the same data

i am not able to send it under the current userid and also not able to fetch it for current userid.
basically i want to send the data under the userid and also fetch the same data.
So for that i want to change the current document name as the user id. but whenever i do that and i call _sendToServer() in an on pressed button it gives me error.
_sendToServer() {
if (_key.currentState.validate()) {
_key.currentState.save();
DatabaseReference ref = FirebaseDatabase.instance.reference();
final Firestore _db = Firestore.instance;
var data = {
"name": name,
"profession": profession,
"message": message,
};
_db
.collection('Profile')
.document('KoMna0Hv7VXoeABwFTGH7LTo1No2')
.setData(data)
.then((v) {
_key.currentState.reset();
});
}
}
also while fetching data i am not able to do this. as i am getting error in the below code.
fetchUser() async{
Future<List<Text>> getAllProfiles() async {
List<Text> returnList = [];
final Firestore _db = Firestore.instance;
await _db.collection("profile").getDocuments().then((QuerySnapshot snapshot) {
snapshot.documents.forEach((doc) {
var keys = snapshot.value.keys;
var data = snapshot.value;
allData.clear();
for (var key in keys) {
myData d = new myData(
data[key]['name'],
data[key]['message'],
data[key]['profession'],
);
allData.add(d);
}
setState(() {
print('Length : ${allData.length}');
});
});
});
return returnList;
}
}
i must provide these key value pair for fetching the data but unfortunately i am not able to do so.
I have added the orderByChild('id') and equalTo('${user.uid}') filed in the code with firebase user. and i also one more item to my list which is data[key]['id'], my current user id. this way everytime the user tries to fetch the data it will look into the list item for current userid and if it matches it will fetch that particular database only.
#override
// ignore: must_call_super
void initState() {
FirebaseAuth.instance.currentUser().then((user) {
fetchUser(user);
});
}
fetchUser(FirebaseUser user) {
DatabaseReference ref = FirebaseDatabase.instance.reference();
ref
.child('node-name')
.orderByChild('id')
.equalTo('${user.uid}')
.once()
.then((DataSnapshot snap) {
var keys = snap.value.keys;
var data = snap.value;
print(snap.value.toString());
allData.clear();
for (var key in keys) {
myData d = new myData(
data[key]['name'],
data[key]['number'],
data[key]['address'],
data[key]['id'],
data[key]['location'],
data[key]['website'],
);
allData.add(d);
}
setState(() {
print('Length : ${allData.length}');
});
});
}
_sendToServer() async{
FirebaseUser user = await FirebaseAuth.instance.currentUser();
if (_key.currentState.validate()) {
_key.currentState.save();
DatabaseReference ref = FirebaseDatabase.instance.reference();
var data = {
"id": user.uid,
"name": name,
"number": number,
"address": address,
"location":location,
"website":website,
};
ref.child('node-name').child(user.uid).set(data).then((v) {
_key.currentState.reset();
});
} else {
setState(() {
_autovalidate = true;
});
}
}

A simple Query in flutter/firebase database

I try to experience Firebase Live database with flutter.
I just would like to get a value in the datasnapshot of the firebase response.
My Firebase
My Code
static Future<User> getUser(String userKey) async {
Completer<User> completer = new Completer<User>();
String accountKey = await Preferences.getAccountKey();
FirebaseDatabase.instance
.reference()
.child("accounts")
.child(accountKey)
.child("users")
.childOrderBy("Group_id")
.equals("54")
.once()
.then((DataSnapshot snapshot) {
var user = new User.fromSnapShot(snapshot.key, snapshot.value);
completer.complete(user);
});
return completer.future;
}
}
class User {
final String key;
String firstName;
Todo.fromJson(this.key, Map data) {
firstname= data['Firstname'];
if (firstname== null) {
firstname= '';
}
}
}
I got Null value for firstname.
I guess I should navigate to the child of snapshot.value. But impossible to manage with foreach, or Map(), ...
Kind regards, Jerome
You are querying with a query and the documentation for Queries (here in JavaScript, but it is valid for all languages), says that "even when there is only a single match for the query, the snapshot is still a list; it just contains a single item. To access the item, you need to loop over the result."
I don't know exactly how you should loop, in Flutter/Dart, over the children of the snapshot but you should do something like the following (in JavaScript):
snapshot.forEach(function(childSnapshot) {
var childKey = childSnapshot.key;
var childData = childSnapshot.val();
// ...
});
and assuming that your query returns only one record ("one single match"), use the child snapshot when you do
var user = new User.fromSnapShot(childSnapshot.key, childSnapshot.value);
This will give you Users in reusable dialog. There might be slight disservice to yourself if you don't use stream and stream-builders, the solution below is a one time fetch of the users' collection on FirebaseDB.
class User {
String firstName, groupID, lastName, pictureURL, userID;
User({this.firstName, this.groupID, this.lastName, this.pictureURL, this.userID});
factory User.fromJSON(Map<dynamic, dynamic> user) => User(firstName: user["Firstname"], groupID: user["Group_id"], lastName: user["Lastname"], pictureURL: user["Picturelink"], userID: user["User_id"]);
}
Future<List<User>> users = Firestore.instance.collection("users").snapshots().asyncMap((users) {
return users.documents.map((user) => User.fromJSON(user.data)).toList();
}).single;

Resources