So I'm writing an quiz app and I need to get defined amount of questions from firestore. So i created two files data provider file and repository file, and that's how they look:
Data Provider
class TestProvider {
FirebaseFirestore _firestore = FirebaseFirestore.instance;
Future<Map> getQuestionFromFirebase(String documentId) async {
Map mappedQuestion;
await _firestore
.collection('questions')
.doc(documentId)
.get()
.then((DocumentSnapshot snapshot) => mappedQuestion = snapshot.data());
return mappedQuestion;
}
}
Repository
class TestRepository {
final int amountOfQuestions;
TestRepository({
#required this.amountOfQuestions,
});
TestProvider _testProvider;
Future listOfQuestions() async {
List<int> range = numberInRange(amountOfQuestions);
List<Question> listOfQuestions;
for (int i = 1; i <= amountOfQuestions; i++) {
listOfQuestions.add(Question.fromMap(
await _testProvider.getQuestionFromFirebase(range[i].toString())));
}
return listOfQuestions;
}
}
The error i get:
E/flutter ( 5186): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: NoSuchMethodError: The method 'getQuestionFromFirebase' was called on null.
E/flutter ( 5186): Receiver: null
E/flutter ( 5186): Tried calling: getQuestionFromFirebase("3")
The funny thing about it is the fact that when i call fuction from provider i don't get error. Problem begins when I'm using it from repo class. Any idea how to fix it? I don't want to use FutureBuilder any time I want to use it in widget. I want to transform snapshot into my question model.
In your TestRepository code it's not clear where you are instantiating TestProvider _testProvider; member.
The error message is clear The method 'getQuestionFromFirebase' was called on null. So there is no instance of TestProvider class and _testProvider is a null pointer.
Grant TestProvider _testProvider = TestProvider(); in your code as said by #ikerfah and you will have no issues in your code.
Related
I try to make an app about health blog with Flutter. I want to store some data with arrays that contain map data. Although I can manually perform this on the Firestore, I'm getting some errors in coding.
Here is Firestore screenshot
Here is the code which I try to add map data to the array.
Future<bool> updateUserCases(String userId, Map newCase) async {
await _firestoreDB.collection("users").doc(userId).update({
"userCases" : FieldValue.arrayUnion([newCase])
});
return true;
}
I can add my map data to Firestore, but when I try to add it to the array, I get this error.
E/flutter (10661): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: [cloud_firestore/unknown] Invalid data. FieldValue.serverTimestamp() can only be used with set() and update()
And this is my "Case Model" which I want to add into the array
class CaseModel {
final String caseId;
final String caseTitle;
final String caseBody;
final Map caseOwner;
Timestamp caseDate;
bool caseSolve;
List<String> casePhotos;
String caseTag;
CaseModel(
{#required this.caseOwner,
this.caseId,
this.caseTitle,
this.caseBody,
this.caseDate,
this.caseTag});
Map<String, dynamic> toMap() {
return {
"case_id": caseId,
"case_title": caseTitle,
"case_body": caseBody,
"case_owner": caseOwner,
"case_date": caseDate ?? FieldValue.serverTimestamp(),
"case_solve": caseSolve,
"case_photos": casePhotos,
"case_tag": caseTag,
};
}
Could you help if there is a way I can fix this problem? Thank you.
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'm trying to add data to firestore collection. Update function works good,
var kontrol;
DatayiAl(kontrol);
if(kontrol == null){
print(5);
verileriKaydet();
}
In this section if part works good, because I can see the printed 5.
But verileriKaydet() method is not working.
void verileriKaydet() async{
var firebaseUser = await FirebaseAuth.instance.currentUser();
firestoreInstance.collection("skorlar").document(firebaseUser.uid).setData(
{
"user" : googleSignIn.currentUser,
"skor" : marks
}
).then((_){
print("success!");
});
}
And this is the getting data function:
void DatayiAl(var skor) async{
var firebaseUser = await FirebaseAuth.instance.currentUser();
firestoreInstance.collection("skorlar").document(firebaseUser.uid).get().then((value){
print(value.data);
skor = value;
});
}
At the end I see the 5 printed but after that this error comes:
[ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: Invalid argument: Instance of 'GoogleSignInAccount'
This line is causing the problem
"user" : googleSignIn.currentUser,
you already have the currentUser in the previous line var firebaseUser = await FirebaseAuth.instance.currentUser();
I suggest you try reusing it.
You can also try creating a model, like AppUser, or just a Map from firebaseUser, so that it becomes more usable for your app.
on the other hand, if you still want to use the googleSignIn.currentUser , then you need to create a model or Map and then assign it to your object.
I get the error:
I/flutter ( 4091): The following NoSuchMethodError was thrown building Builder:
I/flutter ( 4091): Class 'Future' has no instance method '[]'.
I/flutter ( 4091): Receiver: Instance of 'Future'
I/flutter ( 4091): Tried calling: []("main")
And trace it back to:
void updateUI(dynamic weatherData) {
setState(() {
if (weather Data==null) {
//error checking code that's not called; weatherData object isn't null
}
temperature = weatherData['main']['temp'];
weatherMessage = weather.getMessage(temperature);
var condition = weatherData['weather'][0]['id'];
weatherIcon = weather.getWeatherIcon(condition);
cityName = weatherData['name'];
});
}
The variables are initialized elsewhere. The problem's linked to something I clearly don't understand about Flutter Futures and/or asynch programming. The weatherData object is a JSON string pulled down from the openWeatherMaps API. If I call print(weatherData) where it's created, I get the expected JSON string. If I call print(weatherData) in the method above I get `Instance of Future'.
It looks like you're passing in a future which needs to be resolved before you can use it.
void updateUI(Future<dynamic> weatherDataFuture) async {
final weatherData = await weatherDataFuture;
...
The actual problem of this error is from the method where the data is fetched is not marked as await. For example
NetworkHelper networkHelper = new NetworkHelper(url);
var weatherData = await networkHelper.getData();
In the above, the networkHelper is marked as await.
The Flutter setState function not updating the list after retrieving from Firebase.
I am trying to develop a Flutter app. I am not getting updating the list in setState() function. The list is successfully retrieving from firebase. I have written the firebase connections in Services.dart file.
But my method _getList() is not getting the value in main.dart file.
main.dart
class DetailsPageState extends State<DetailsPage> {
List<Product> list;
#override
void initState() {
_checkUser(); // for getting user id from firebase auth
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Container(
child:new Text("data");
);
)
}
void _checkUser(){
debugPrint("Entering in _checkUser");
this.firebaseAuth.onAuthStateChanged.listen((firebaseUser)async{
_getList(firebaseUser.uid);
});
}
void _getList(String id)
debugPrint("Entering in _getList");
Services.retrieveItems(firestore, uid).then((onValue){
setState(() {
list=onValue;
debugPrint("items list:"+onValue.length.toString());
debugPrint("items list:"+listCart.length.toString());
});
});
}
}
Services.dart
static Future<List> retrieveItems(Firestore firestore, String userId) async {
List<Product> items = new List<Product>();
try {
firestore.collection("Items").document(userId)
.collection("ItemsMain").snapshots().listen((QuerySnapshot snapshot) {
List docList = snapshot.documents;
items = snapshot.documents.map((documentSnapshot) => Product.fromMap(documentSnapshot.data)).toList();
debugPrint("items:"+items.length.toString());
//return items;
});
} on Exception catch (e) {
print (e.toString());
}
debugPrint("items 2:"+items.length.toString());
return items;
}
Expected results:
Entering in _checkUser
Entering in _getList
items:6
items 2:6
items list:6
items list:6
Actual results:
Entering in _checkUser
Entering in _getList
items list:0
items list:0
items 2:0
items:6
You're returning the items before they are loaded. The simplest way to fix this is to use await in retrieveItems to wait for the data to be loaded from Firestore:
static Future<List> retrieveItems(Firestore firestore, String userId) async {
List<Product> items = new List<Product>();
var snapshot = await firestore.collection("Items").document(userId)
.collection("ItemsMain").getDocuments()
List docList = snapshot.documents;
items = snapshot.documents.map((documentSnapshot) => Product.fromMap(documentSnapshot.data)).toList();
debugPrint("items:"+items.length.toString());
return items;
}
You'll note that I:
Call get() instead of listen(). Since listen() starts actively monitoring the collection, it is impossible to say when it is "done". A get() on the other hand, returns the documents once, and is then done.
Removed the exception handling, just to make the code a bit more readable. But I also recommend only adding exception handlers in functional code like this if you're actually handling the exception. Leave "log and continue" handlers for higher-level code, such as your main method.