I been reading multiple threads about passing data between futures, but for some reason I can get this to work.:(
I have two futures, where the first future is doing post request to retrieve a token
Future<AuthStart> fetchAuthStart() async {
final response = await http.post(
Uri.parse('http://ipsum.com/auth/start'),
body: jsonEncode(<String, String>{
'IdNumber': '198805172387',
}));
if (response.statusCode == 200) {
final responseJson = jsonDecode(response.body);
return AuthStart.fromJson(responseJson);
} else {
throw Exception("Failed");
}
}
this request will generate an GUID that I need in my next future
Future<AuthStatus> fetchAuthStatus(String token) async {
final response = await http.post(Uri.parse('http://ipsum/auth/status'),
body: jsonEncode(<String, String>{
'Token':token,
}),
);
if (response.statusCode == 200) {
return AuthStatus.fromJson(jsonDecode(response.body));
} else {
throw Exception("Failed");
}
}
I have tried multiple ways, one that worked was to pass the variable in my FutureBuilder like this
FutureBuilder(
future: futureAuthStart,
builder: (context, snapshot) {
if (snapshot.hasData) {
fetchAuthStatus(snapshot.data!.Token);
return Text(snapshot.data!.Token);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
),
But this does not make sense?
I would rather pass it in my InitState something like this.
void initState() {
super.initState();
futureAuthStart = fetchAuthStart();
futureAuthStatus = fetchAuthStatus(token);
}
Am I totally on the wrong path? Any tutorials out there that can give better info :)?
Thanks in advance.
Create a void function and pass it the functions and one at a time it will be executed with async/await. You should take a look at Asynchronous programming: futures, async, await.
void initState() {
super.initState();
_onInit();
}
void _onInit() async {
try {
var futureAuthStart = await fetchAuthStart();
var futureAuthStatus = await fetchAuthStatus(futureAuthStart.data.token);
}catch(e, stackTrace){
//handler error
print(e);
print(stackTrace);
}
}
Related
Hi guys I'm new to Flutter
I'm trying to fetch the data from http.post method using FutureBuilder and snapshot.data but it keeps returning CircularProgressIndicator which is means snapshot has no data.
Future postDataTransaksi() async {
try {
http.Response response = await http.post(
Uri.parse('https://ignis.rumahzakat.org/donol/listTransaksi'),
body: {
'---': '---',
'---': '---',
},
headers: {
'access-token': '---'
});
if (response.statusCode == 200) {
var data = jsonDecode(response.body);
} else {}
} catch (e) {
print(e.toString());
}
}
This is my post method code.
FutureBuilder(
future: postDataTransaksi(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return Container(child: Text(snapshot.data[0]['id']));
} else {
return const Center(child: CircularProgressIndicator());
}
})
And this is how I try to fetch the data
Change your builder to this:
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
return Container(child: Text(snapshot.data[0]['id']));
} else {
return const Center(child: CircularProgressIndicator());
}
and also change your future to this"
Future<List> postDataTransaksi() async {
try {
http.Response response = await http.post(
Uri.parse('https://ignis.rumahzakat.org/donol/listTransaksi'),
body: {
'---': '---',
'---': '---',
},
headers: {
'access-token': '---'
});
if (response.statusCode == 200) {
var data = jsonDecode(response.body);
return data;// <--- add this
}
return [];// <--- add this
} catch (e) {
print(e.toString());
return [];// <--- add this
}
}
Referring to this article
https://medium.com/solidmvp-africa/making-your-api-calls-in-flutter-the-right-way-f0a03e35b4b1
I was trying to call API from a flutter app. But to make it the right way, I was looking for a complete example and came here. My question is why do I need to create an ApiBaseHelper class then RepositoryClass then all other formalities to call an API. Why can't I use FutureBuilder and a simple async function associated with the API like this:
class Networking {
static const BASE_URL = 'https://example.com';
static Future<dynamic> getProductById({
required String? token,
required String? productId,
}) async {
final url = Uri.parse('$BASE_URL/products/$productId');
final accessToken = 'Bearer $token';
Map<String, String> requestHeaders = {
'Authorization': accessToken,
'Content-Type': 'application/json'
};
try {
final response = await http.get(
url,
headers: requestHeaders,
);
if (response.statusCode != 200) {
throw Exception('Error fetching data.');
}
final responseJSON = json.decode(response.body);
if (responseJSON['error'] != null) {
return throw Exception(responseJSON['error']);
}
final product = Product.fromJson(responseJSON);
return product;
} catch (e) {
throw Exception(e.toString());
}
}
}
And then calling it from a FutureBuilder like this:
FutureBuilder(
future: Networking.getProductById(token, id),
builder: (context, snapshot) {
// rest of the code
}
)
Can anyone tell me what is the most convenient and widely used way to call an API?
In a Future Builder im trying to use two methods with different types, both of them fetch data from the api,
the main problem that im having is that both of the function have different types, so im having problem on putting
the two methods because of their types. I tried using
Future.wait(Future[]) aswell but i was getting many errors, there errors where mostly on List,
im still trying to learn how Future Builders work, i worked with FutureBuilders before but didnt have to use two functions inside the FutureBuilder. So if anyone could implement their solution on my code, that would really help and maybe add some comments on why did you make the change so i learn for the future. As a bonus im getting the List is not a subtype of type Map<String, dynamic> error aswell so if anyone could help with that too it would be very helpful. Tried looking into stack over flow answers for that but i couldnt figure it out since i was getting an error on this part
buildSwipeButton() {
return MenuPage(
sendData: fetchLoginData()// i was getting error here,
);
}
buildSwipeButton() {
return MenuPage( // other class name from a different file
sendData: fetchLoginData(),
);
}
buildSwipeButton2() {
return MenuPage( // other class name from a different file
sendData2: fetchWorkingLocationData(),
);
}
Future<LoginData>? fetchLoginData() async {
var url = 'https://dev.api.wurk.skyver.co/api/employees';
String basicAuth = 'Basic ' +
base64Encode(
utf8.encode('${emailController.text}:${passwordController.text}'),
);
var response = await http.get(
Uri.parse(url),
headers: <String, String>{'authorization': basicAuth},
);
print(response.body);
if (response.statusCode == 200) {
print(response.statusCode);
return LoginData.fromJson(
jsonDecode(response.body),
);
} else {
throw Exception('Failed to load LoginData');
}
}
Future<WorkingLocationData>? fetchWorkingLocationData() async {
var url = 'https://dev.api.wurk.skyver.co/api/locations';
String basicAuth = 'Basic ' +
base64Encode(
utf8.encode('${emailController.text}:${passwordController.text}'),
);
var response2 = await http.get(
Uri.parse(url),
headers: <String, String>{'authorization': basicAuth},
);
print(response2.body);
if (response2.statusCode == 200) {
print(response2.statusCode);
return WorkingLocationData.fromJson(
jsonDecode(response2.body),
);
} else {
throw Exception('Failed to load Working Location Data');
}
}
// other file where im trying to use Future Builder
late LoginData data;
Future<LoginData>? sendData;
Future<WorkingLocationData>? sendData2;
body: FutureBuilder<LoginData>(
future: sendData, // trying to use sendData and sendData2
builder: (context, snapshot) {
if (snapshot.hasData) {
LoginData? data1 = snapshot.data;
data = data1!;
print(data.loginPhoneNumber);
return afterLoginBody();
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return Center(child: const CircularProgressIndicator());
},
),
),
Try below code hope its help to you.
late LoginData data;
Future<LoginData>? sendData;
Future<WorkingLocationData>? sendData2;
body: FutureBuilder<LoginData>(
Future.wait([sendData, sendData2]),
builder: (context, AsyncSnapshot<List<dynamic>> snapshot{
snapshot.data[0]; //sendData
snapshot.data[1]; //sendData2
},
),
),
You can try this:
Future? _future;
Future<dynamic> sendData() async {
final data1 = await sendData1();
final data2 = await sendData2();
return [data1, data2];
}
#override
void initState() {
_future = sendData()();
super.initState();
}
///
FutureBuilder(
future: _future,
builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CupertinoActivityIndicator();
}
if (snapshot.hasError) {
return SomethingWentWrong();
}
final data1= snapshot.data[0] as YourDataModel;
final data2 = snapshot.data[1] as YourDataModel;
});
I want to get List values as ints one by one.
where movieId requires an int value.
help me please
thank you
Future<MovieDetailModel> getMovieDetail(int? movieId) async {
try {
http.Response res = await http
.get(Uri.parse('$mainUrl/movie/$**movieId**?$apiKey&language=ko-KR'))
.timeout(Duration(seconds: 8),
onTimeout: () async => new http.Response('{}', 404));
if (res.statusCode == 404) {
return MovieDetailModel.fromJson({});
}
Map<String, dynamic> result = jsonDecode(res.body);
MovieDetailModel movies = MovieDetailModel.fromJson(result);
return movies;
} catch (e) {
print('MovieDetail $e');
}
return MovieDetailModel.fromJson({});
}
where user.userMovieId value is List .
Widget userMovie(UserModel user) {
return FutureBuilder(
future: this._movieDetailProvider.movies(**user.userMovieId!**),
builder:
(BuildContext context, AsyncSnapshot<MovieDetailModel> snapshot) {
if (snapshot.hasData) {}
Provider.
Future<MovieDetailModel> movies(int movieId) async {
MovieDetailModel movieList = await _movieRepo.getMovieDetail(movieId);
notifyListeners();
return movieList;
}
So I'm trying to call the REST API for the login here. This is in my api_services.dart where I am calling all the APIs for the application.
api_services.dart
Future<User> loginUser(String email, String password)
async {
final response = await http.post(serverOauthUrl+'/token',
headers: {
HttpHeaders.AUTHORIZATION: "xxxx"
},
body: {
"email":"$email",
"password":"$password",
}
);
print(response.statusCode);
final responseJson = json.decode(response.body);
return new User.fromJson(responseJson);
}
And there are two ways I can call this loginUser() method in my UI files and get the response. One that uses the then() method and the other uses FutureBuilder. However, in none of the method, can I get the status code. My use case is that when the status code is >400, I will build a widget that shows the error message.
login_screen.dart
then() method code:
_callLoginAPI(String email, String password){
loginUser(userName, password, "password").then((response) {
response.data.token;
// want my status code here as well along with response data
}
else
{
//todo show something on error
}
}, onError: (error) {
debugPrint(error.toString());
});
}
Or using FutureBuilder :
return new FutureBuilder<User>(
future: loginUser(email, password),
builder: (context, snapshot) {
if (snapshot.hasData) {
print(snapshot.data.token);
} else if (snapshot.hasError) {
print(snapshot.error);
return new Text("${snapshot.error}");
}
return new CircularProgressIndicator();
},
);
What I want to do is something like this
if(response.statusCode > 400)
return new Text("Error"):</code>
Thanks to #Thomas, this issue is resolved. Was an easy solution actually.
Adding the changes in the code for other beginners to follow :
api_services.dart
Future<http.Response> loginUser(String email, String password) async {
final response = await http.post(serverOauthUrl+
'/token',
headers: {
HttpHeaders.AUTHORIZATION: "Basic xxx"
},
body: {
"email":"$email",
"password":"$password",
}
);
return response;
}
So instead of the User, I'm returning the http.Response object and now I can retrieve all the required info from the UI files.
Like this:
final responseJson = json.decode(response.body);
User user = User.fromJson(responseJson);
print(user.userName);
Hope it helps somebody
Why aren't you return an Api Result object instead of a user that contains the error code and the user?
Then you can build different widgets on your FutureBuilder depending on the status code.