Flutter firebase database.set(object) issue - firebase

I have a class Product and it is in List plist
Now I need to call the firebase database.set(plist) this is working with Java but when I tried to do it with flutter dart it showing error anybody have the solution for this problem
From StackOverflow, I understand use database.set('{"a":"apple"}) but when I am dealing with List I can't use this solution
update error message
error called Invalid argument: Instance of 'Product'
My code
String table_name="order";
FirebaseAuth.instance.currentUser().then((u){
if(u!=null){
FirebaseDatabase database = FirebaseDatabase(app: app);
String push=database.reference().child(table_name).child(u.uid).push().key;
database.reference().child(table_name).child(u.uid).child(push).set( (productList)).then((r){
print("order set called");
}).catchError((onError){
print("order error called "+onError.toString());
});
}
});
}

We cannot directly set object in Firebase. Unfortunately in Flutter there is no easy solution like java json.
Data types that are allowed are String, boolean, int, double, Map, List. inside database.set().
We can have a look at the official documentation of Flutter https://pub.dev/documentation/firebase_database/latest/firebase_database/DatabaseReference/set.html
Try setting object like this
Future<bool> saveUserData(UserModel userModel) async {
await _database
.reference()
.child("Users")
.child(userModel.username)
.set(<String, Object>{
"mobileNumber": userModel.mobileNumber,
"userName": userModel.userName,
"fullName": userModel.fullName,
}).then((onValue) {
return true;
}).catchError((onError) {
return false;
});
}
I hope this code will be helpful.

Extending a little bit an answer given as a comment above
You basically have to create an auxiliary map beforehand:
Map aux = new Map<String,dynamic>();
And then iterate through the array that you have adding the corresponding map for each child that you want to add:
productList.forEach((product){
//Here you can set the key of the map to whatever you like
aux[product.id] = product.toMap();
});
Just in case, the function toMap inside the Product class should be something like:
Map toMap() {
Map toReturn = new Map();
toReturn['id'] = id;
toReturn['name'] = name;
toReturn['description'] = description;
return toReturn;
}
And then, when you are calling the set function to save to firebase you can do something like:
.set({'productList':aux,})
Hope this was helpful to someone.

Related

"The method 'cast' isn't defined for the type 'Object'" when reading from Firebase

I create banner in listview and get the data from firebase database.
Future<List<String>> getBaners(DatabaseReference bannerRef){
//The method 'cast' isn't defined for the type 'Object'.
return bannerRef.once().then((snapshot) => snapshot.snapshot.value!.cast<String>().toList());
}
Try using
Future<List<String>> getBaners(DatabaseReference bannerRef)async{
final DataSnapshot res = await bannerRef.get();
if(res.exists)
return res.value as List<String>;
return [];
}
I am willing to help more if you can provide more details about the structure of the data that bannerRef is pointing to.

Getting a list of friends and then displaying a friends list

I'm struggling with how I can get the data of a user's friends from Firebase's Realtime Database to build it out in Flutter. The structure of the database looks like this:
enter image description here
Essentially, in my code, I'm listening to changes in an user's friends list from the database and then getting the friend ID so that I can query the rest of the friend's metadata (image, name, etc.) to build out the friend's list in the app. The issue I'm running into is that I can't seem to get the Json data to map correct and I get the following error:
Unhandled Exception: type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'String'
Any insight would be appreciated
class _FriendsScreenState extends State<FriendsScreen> {
#override
void initState() {
getUserFriendsList();
super.initState();
}
getUserFriendsList() async {
rtdb.child('friends').child(widget.currentUserID).onValue.listen((event) {
final data = new Map<String, dynamic>.from(event.snapshot.value);
data.keys.forEach((element) async {
DataSnapshot userInfo = await usersRef.child(element).get();
User users = User.fromJson(json.decode(userInfo.value));
});
});
}
factory User.fromJson(Map<String, dynamic> parsedJson) {
return User(
imageUrl: parsedJson['userImageUrl'],
userFirstName: parsedJson['userFirstName'],
userLastName: parsedJson['userLastName'],
userID: parsedJson['userID'],
);
}
I think you have a problem in:
User users = User.fromJson(json.decode(userInfo.value));
why u have used JSON decode (json.decode & User.fromJson) twice?
However, for further analysis, you should provide data and log it.
There's no need to decode the json as DataSnaphot.value "returns the contents of [the] data snapshot as native types."
So userInfo.value does not return a String, it returns a Map instead.
Solution:
Convert the result to a Map<String, dynamic> from the _InternalLinkedHashMap<dynamic, dynamic> type it returns.
Change this line:
User users = User.fromJson(json.decode(userInfo.value));
to this:
User users = User.fromJson(Map<String, dynamic>.from(userInfo.value));

Kotlin Save Firestore query result for a variable

I have a problem with Firebase.
I also use Firestore on a website and in a Kotlin app.
On the website, I can save the result of the query to a variation in the following way:
const addStudentManu = async($this) => {
const userId = await db.collection('users').where('neptun','==',ASD123).get();
const getUserId = userId.docs.map(doc=>doc.id);
}
How can i do this in kotlin?
This is how it goes:
db.collection("users")
.whereEqualTo("neptun", "ASD123")
.get()
.addOnSuccessListener { documents ->
val list = mutableListOf<String>()
for (document in documents) {
Log.d(TAG, "${document.id}")
list.add(document.id)
}
println(list)
}
.addOnFailureListener { exception ->
Log.w(TAG, "Error getting documents: ", exception)
}
You can checkout the sample code snippets in the documentation.
While #Dharmaraj answer will work perfectly fine, when it comes to Kotlin, the most convenient way for saving the result of a query would be to use Kotlin Coroutines, We can create a suspend function and map all documents to their corresponding IDs, similar with your example. So please try the following lines of code:
private suspend fun getIdsFromFirestore(): List<String> {
val ids = db.collection("users").whereEqualTo("neptun", "ASD123").get().await()
return ids.documents.mapNotNull { doc ->
doc.id
}
}
As you can see, we have now an extension function called await() that will interrupt the Coroutine until the data from the database is available and then return it. That's almost the same thing when using async on the web.
Now we can simply call this from another suspend method like in the following lines of code:
private suspend fun getIds() {
try {
val ids = getIdsFromFirestore()
// Do what you need to do with the list of IDs
} catch (e: Exception) {
Log.d(TAG, e.getMessage()) //Don't ignore potential errors!
}
}

How do I properly cast a response from a Firebase Function call in my Flutter app

I'm teaching myself Flutter by building a simple meal planner app. Part of what I want to do is to use a Firebase function for making calls to the API. I'm using the Spoonacular API, and I do not want to store the API key on the app itself, hence the Firebase backend.
I've set up a file called cloud_functions.dart, which I plan on using to make calls to my Firebase functions. The call to get the recipes is as follows:
Future<SearchRecipesComplexResponseBody> getRecipes() async {
HttpsCallable callable = getCallable('searchRecipes');
try {
final HttpsCallableResult<SearchRecipesComplexResponseBody> results = await callable({'number': 20, 'offset': 0});
print('Results: ');
print(results);
print('Results data:');
print(results.data);
return results.data;
} catch (e) {
print('Error: ');
print(e);
return null;
}
}
HttpsCallable getCallable(String callableName) {
FirebaseFunctions functions = FirebaseFunctions.instance;
if (kDebugMode) {
print('Running in debug mode');
functions.useFunctionsEmulator(origin: 'http://localhost:5001');
}
return functions.httpsCallable(callableName);
}
The code for SearchRecipesComplexResponseBody is as follows:
import 'package:meal_planner/models/recipe.dart';
import 'package:json_annotation/json_annotation.dart';
part 'search_recipes_complex_response_body.g.dart';
#JsonSerializable()
class SearchRecipesComplexResponseBody {
final int offset;
final int number;
final List<Recipe> results;
final int totalResults;
SearchRecipesComplexResponseBody({this.offset, this.number, this.results, this.totalResults});
factory SearchRecipesComplexResponseBody.fromJson(Map<String, dynamic> json) {
return _$SearchRecipesComplexResponseBodyFromJson(json);
}
}
The code for Recipe is as follows:
#JsonSerializable()
class Recipe {
final int id;
#JsonKey(includeIfNull: false)
final int calories;
#JsonKey(includeIfNull: false)
final String carbs;
#JsonKey(includeIfNull: false)
final String fat;
final String image;
final String imageType;
#JsonKey(includeIfNull: false)
final String protein;
final String title;
Recipe({#required this.id, this.calories, this.carbs, this.fat, this.image, this.imageType, this.protein, #required this.title});
factory Recipe.fromJson(Map<String, dynamic> json) {
return _$RecipeFromJson(json);
}
}
While I do get the data back that I'm expecting, there's something going on with the casting that I get this error when running the code:
type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>'
When I went to debug the code, breaking on the print(results) line in the cloud_functions.dart file, I saw that the data does seem to match the format that I'm expecting
I've attempted to use the json_serializable utility to generate the JSON serialization code, but that didn't work either. I've tried removing the extraneous fields in the Recipe class to no avail.
I think the issue is something to do with the fact that I've got a property on the SearchRecipesComplexResponseBody that's a list of Recipes, but I can't seem to figure out what I'm doing wrong here. For all I know, I could be barking up the wrong tree. Does anyone have any ideas?
RESOURCES CHECKED:
Flutterfire Cloud Functions documentation - https://firebase.flutter.dev/docs/functions/usage/
Search on HttpCallableResult - https://duckduckgo.com/?q=flutterfire+httpcallableresult&ia=web
https://medium.com/codespace69/flutter-working-with-data-json-json-and-serialization-f90165b659d0
I figured it out
I updated the getRecipes function in cloud_functions.dart to be as follows:
Future<SearchRecipesComplexResponseBody> getRecipes() async {
HttpsCallable callable = getCallable('searchRecipes');
try {
final HttpsCallableResult results = await callable({'number': 20, 'offset': 0});
var convertedResult = Map<String, dynamic>.from(results.data);
SearchRecipesComplexResponseBody data = SearchRecipesComplexResponseBody.fromJson(convertedResult);
return data;
} catch (e) {
print('Error: ');
print(e);
return null;
}
}
I saw that I already had a fromJson function defined on my SearchRecipesComplexResponseBody class, but I hadn't been taking advantage of it. I needed to convert the response I got back from Firebase from an _InternalLinkedHashMap<dynamic, dynamic> to the Map<String, dynamic> type that fromJson uses.
I also needed to add anyMap: true inside my JsonSerializer attributes to get the nested list of Recipes in order for its fromJson. I'm not sure why that worked. Anyone have any thoughts?
You may use this to convert from _InternalLinkedHashMap to Map<String, dynamic>. This will get you overcome the error type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>':
HttpsCallable callable = functions.httpsCallable('your-function');
final results = await callable();
final data = Map<String, dynamic>.from(results.data);

I cannot access the data when extracting data from firebase in Flutter

My code is written like this, but I cannot access the data in the output I get?
void getMessages() async {
final messages = await _firestore.collection('mesajlar').get();
for (var message in messages.docs) {
print(message.data);
}
}
The problem is that you are calling data as a property when it is actually a function, you can see that in the documentation for DocumentSnapshot, which QueryDocumentSnapshot implements. So all you have to change is the print:
print(message.data());

Resources