Flutter FutureBuilder always return null - flutter

FutureBuilder keep returning null even when the http request successfully fetch the data from api.
I use json_serializable and freezed package in my data model, is this error related to them or it is purely coming from flutter/dart?
Http request
Future<VitalSignResponse> getLatestVitalSign(String medicalNo) async {
final String url = Api.baseUrl +
'VitalSignByMedicalNoLatest?AccessKey=${Api.accessKey}&MedicalNo=$medicalNo';
Response res = await Dio().get(url);
print('api res: ${res.data}'); // 'api res: the correct data from api'
print('serialize: ${VitalSignResponse.fromJson(json.decode(res.data))}'); // print out nothing
return VitalSignResponse.fromJson(json.decode(res.data));
}
data model
#freezed
abstract class VitalSignResponse with _$VitalSignResponse {
#JsonSerializable(explicitToJson: true)
const factory VitalSignResponse(
final String status,
final String errorCode,
final List<VitalSign> data,
) = _VitalSignResponse;
factory VitalSignResponse.fromJson(Map<String, dynamic> json) =>
_$VitalSignResponseFromJson(json);
}
future builder logic
FutureBuilder(
future: service.getLatestVitalSign(patientBloc.patient.medicalNo),
builder: (
BuildContext context,
AsyncSnapshot snapshot,
) {
print(snapshot.data); // null
if (snapshot.connectionState == ConnectionState.waiting)
return Center(child: CircularProgressIndicator());
if (snapshot.hasData) {
// show data
}
return Container();
},
),

I simply forgot to run my project with debugging
i leave the question here as a personal reminder and for new developer out there to not making the same mistake i did. And for my friends to laugh at me haha

Related

How to save data from API in device Flutter

In my application a lot of data received from the api, how to save data on device when app starts, and take this data from device and take data from the device without making an api call every time.
I can show on the example of my futureBuilder, every time when I go to UserPage, my user data is loaded, how can I save it?
late Future<User> userFuture = getUser();
static Future<User> getUser() async {
var url = '${Constants.API_URL_DOMAIN}action=user_profile&token=${Constants.USER_TOKEN}';
print(Constants.USER_TOKEN);
final response = await http.get(Uri.parse(url));
final body = jsonDecode(response.body);
return User.fromJson(body['data']);
}
FutureBuilder<User>(
future: userFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return buildProfileShimmer();
// Center(child: CircularProgressIndicator());
} else if (snapshot.hasData) {
final user = snapshot.data!;
return buildUser(user);
} else {
return Text("No widget to build");
}
},
)

need help in API integration

I hope you all are well.
I got a problem i am learning API integration in flutter now a days the problem I am facing is i can't get data here is the code below:
class _AppState extends State<App> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: getuser(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return CircularProgressIndicator();
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data[index].title),
);
},
);
}
},
));
}
}
it is only showing me circular indicator i am using API 'https://jsonplaceholder.typicode.com/posts'.
I tried to check if the API is working so i check it by passing a hello in list tile and getting the hello by the length of API given in item count and actually that showed me output according to length please help me out so that i can move forward.
Thank You.
Here is the function also:
import 'package:apiintegration/model/user_model.dart';
import 'package:http/http.dart' as http;
getuser() async {
var url = Uri.parse('https://jsonplaceholder.typicode.com/posts');
var response = await http.get(url);
var responsedata = jsonDecode(response.body);
return UserModel.fromJson(responsedata);```
You should continue step by step.
As you said if you have success response and not null data, you might have parsing problem.
You should go to your url => https://jsonplaceholder.typicode.com/posts again and copy the json data.
Open https://app.quicktype.io/ site and paste your json data here
to create related parsing methods.
Make http request again. If you parse the json data correctly check out getUser method in view file.
When you get response, be sure that you re-draw(setState etc.) the ui
for displaying parsed json data.
If everything works well you should handle all the states
that you can have from FutureBuilder such as:
if(snapshot.connectionState == ConnectionState.none) {...}
else if(snapshot.connectionState == ConnectionState.waiting) {...}
else if(snapshot.connectionState == ConnectionState.done) {
if(snapshot.hasError) {...}
if(snapshot.hasData) {...}
}
problem is here
return UserModel.fromJson(responsedata);```
it should be userModelFromJson(responsedata);
Example Model:
import 'dart:convert';
DefaultModel defaultModelFromJson(String str) =>
DefaultModel.fromJson(json.decode(str));
String defaultModelToJson(DefaultModel data) => json.encode(data.toJson());
class DefaultModel {
DefaultModel({
this.response,
this.data,
});
String? response;
String? data;
factory DefaultModel.fromJson(Map<String, dynamic> json) => DefaultModel(
response: json["response"],
data: json["data"],
);
Map<String, dynamic> toJson() => {
"response": response,
"data": data,
};
}

how to retrive value from a firestore flutter where query

I started flutter recently, and I try to retrieve the data from a query I made using 'where' , but the only thing I got back is "Instance of '_JsonQueryDocumentSnapshot'".
I tried different thing , but nothing work or i do it badly
this is my code :
CollectionReference users =
FirebaseFirestore.instance.collection('users');
final documents =
await users.where("username", isEqualTo: "username").get();
documents.docs.forEach((element) {
print(element);
});
I have also tried to use Future but without success :
class finduser extends StatelessWidget {
final String username;
finduser(this.username);
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder(
future: users.where('username', isEqualTo: '${username}').get(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError) {
print("wrong");
return Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {
print("doesnt exist");
return Text("User does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data = snapshot.data! as Map<String, dynamic>;
print(snapshot.data!);
return Text("${data}");
}
return Text("loading");
},
);
}
}
for the moment, all usernames are just "username"
Thank you for the help
When you get your documents like this :
CollectionReference users =
FirebaseFirestore.instance.collection('users');
final documents =
await users.where("username", isEqualTo: "username").get();
documents.docs.forEach((element) {
print(element);
});
You are trying to print an instance of a QueryDocumentSnapshot
This QueryDocumentSnapshot has a method .data() which returns a Map<String,dynamic> aka JSON.
So in order to print the content of your Document, do this :
documents.docs.forEach((element) {
print(MyClass.fromJson(element.data()));
});
This data by itself will not be very useful so I recommend creating a factory method for your class :
class MyClass {
final String username;
const MyClass({required this.username});
factory MyClass.fromJson(Map<String, dynamic> json) =>
MyClass(username: json['username'] as String);
}
Now you can call MyClass.fromJson(element.data()); and get a new instance of your class this way.
I have searched a lot but i see you have written code right.
The only thing that came to my mind is that you didn't initialize your firebase to your flutter project (you should do it in any flutter project to be able to use flutter).
link of the document:
https://firebase.flutter.dev/docs/overview#initializing-flutterfire
In your first code snippet you are printing element, which are instances of the QueryDocumentSnapshot class. Since you're not accessing specific data of the document snapshot, you get its default toString implementation, which apparently just shows the class name.
A bit more meaningful be to print the document id:
documents.docs.forEach((doc) {
print(doc.id);
});
Or a field from the document, like the username:
documents.docs.forEach((doc) {
print(doc.get("username"));
});
Run this code, it will work.
I also faced this similar problem, so I used this work around.
Map<String, dynamic> data = {};
FirebaseFirestore.instance.collection('users').where("username", isEqualTo: "username").get().then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((value){
data = value.data()!;
print('printing uid ${data['uid']}');
print('printing username--${data['username']}');
print('printing all data--$data');
});
});

How to extract values from onCall firebase function and load them in future builder

i have a onCall cloud function which is returning
resp.status(200).send(JSON.stringify(entities));
In my flutter app, i have created this future to get values from it.
Future<void> dataDriven(String filename) async {
HttpsCallable callable =
FirebaseFunctions.instance.httpsCallable('fruitsType');
final results = await callable;
final datE = results.call(<String, dynamic>{
'filename': 'filename',
});
final dataF = await datE.then((value) => value.data);
print (dataF);
}
It is successfully printing the response which is as per expectation. but my snapshot is always returning null. It is not even reaching hasData stage. Please help.
Response;
[{"name":"banana","type":"fruit","count":0,"color":"yellow"},{{"name":"apple","type":"fruit","count":2,"color":"red"}]
FutureBuilder(
future: dataDriven('fruits.txt'),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: Text('An error has occurred!'),
);
} else {
final data = snapshot.data;
return Text(data.toString());
}
It looks like there are some issues that need to be fixed (See comments in code).
// Set the correct return type (not void because you are returning data)
Future<String> dataDriven(String filename) async {
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('fruitsType');
// You can just call the function here with await
final result = await callable.call({
// Remove the quotes on the filename value
'filename': filename,
});
// Don't forget to return the data
return result;
}
I suggest reading up on the documentation about calling cloud functions from a flutter app and basic dart syntax.

NoSuchMethodError: invalid member on null: '_get'

full error message:
The following JSNoSuchMethodError was thrown building FutureBuilder<DocumentSnapshot>(dirty, state: _FutureBuilderState<DocumentSnapshot>#dfc82):
NoSuchMethodError: invalid member on null: '_get'
it comes from this line : UserModel user = UserModel.fromDoc(snapshot.data); and it is in :
body: FutureBuilder(
future: usersRef.doc(widget.userId).get(),
builder: ( context, snapshot) {
List<Widget> children;
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
else if (snapshot.hasData) {
// print('user snapshot data is: ${snapshot.data}');
UserModel user = UserModel.fromDoc(snapshot.data);
model looks like :
factory UserModel.fromDoc(DocumentSnapshot doc) {
return UserModel(
id: doc.data()['id'],
name: doc.data()['name'],
username: doc.data()['username'],
password: doc.data()['password'],
profileImageUrl: doc.data()['profileImageUrl'],
email: doc.data()['email'] ,
userIds: doc.data()['userIds'] ?? '',
);
}
I tried downgrading the version of cloud_firestore but still don't work
As your error log tells, you are accessing some value on a null member.
It seems, the error lies in your factory method. In Flutter, to access all data from documentSnapshot in Map<String, dynamic> have to use doc.data.
Before doing that, we could check for the document existence within DocumentSnapshot using doc.exists. For further ref - https://firebase.flutter.dev/docs/firestore/usage/
I prefer to handle all connectionstate including error or else your screen would get stuck in CircularProgressIndicator and it's hard for user to know the reason.
if (snapshot.hasError) {
return Text("Something went wrong");
} else if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data = snapshot.data.data;
return Widget;
} else {
return Center(
child: CircularProgressIndicator());
}
Another case: If no document exists in firestore, the read (snapshot.data) will return null. However this null case internally handled by the futurebuilder connectionState. As per your debug result, since the snapshot.data has DocumentSnapshot, it didn't cause the error.