How can i use the result from the first API call as input for the second API call? - flutter

I have to make multiple API calls in order to get the actual data. I have written the below code to make the first API call. It works but I have to use the return value (let'say it returns access token) from the first call, and use this access token as part of the header on the second API call. How can I achieve that?
class Service {
final String url;
Map<String, String> header = new Map();
Map<String, String> body = new Map();
Service(this.url, this.header, this.body);
Future<Data> postCall() async {
final response = await http.post(url, headers: header, body: body);
return Data.fromJson(json.decode(response.body));
}
}
class MyApp extends StatelessWidget {
Service service;
Service serviceTwo;
....
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: FutureBuilder<Data>(
future: service.postCall,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.accessToken);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
),
);}}

There are many ways of achieving that, the simplest one is just using await on your method to append the future calls.
So your method postCall() would be something like this:
Future<Data> postCall() async {
// The first call, suppose you'll get the token
final responseToken = await http.post(url, headers: header, body: body);
// Decode it as you wish
final token = json.decode(responseToken.body);
// The second call to get data with the token
final response = await http.get(
url,
headers: {authorization: "Bearer $token"},
);
// Decode your data and return
return Data.fromJson(json.decode(response.body));
}
If it is a token you'll use many times, I recommend you to store it in flutter_secure_storage and use it as you wish.

Related

call parameter function to get data in flutter

I'm learning and trying to add parameters when calling parameters in functions when getting data from the API, but I'm a bit confused about how I call them in widgets.
static Future<Map<String, DataKuliahModel>> getDataKuliah(String smt) async {
String url = Constant.baseURL;
String token = await UtilSharedPreferences.getToken();
await Future.delayed(const Duration(milliseconds: 1000));
// String responseJson = await rootBundle.loadString('assets/1.json');
Map<String, DataKuliahModel> finalResult = {};
final response = await http.get(
Uri.parse(
'$url/auth/mhs_siakad/perwalian/get_paket',
),
headers: {
'Authorization': 'Bearer $token',
},
);
final result = jsonDecode(response.body)['data'] as Map<String, dynamic>;
result.forEach((key, value) {
DataKuliahModel dataKuliah = DataKuliahModel.fromMap(value);
finalResult.addAll({
key: dataKuliah,
});
});
return finalResult;
}
and I want to call him here
When you declare a function with positional parameters you need to provide those parameters when you call that function.
import 'package:flutter/material.dart';
class Services {
static Future<String> greeting(String name) async {
/// this function doesn't need to be Future
/// but when you call API to get some data it should be a Future
return 'Hello $name';
}
}
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
#override
Widget build(BuildContext context) {
return FutureBuilder(
/// pass positional parameter to [greeting] here
future: Services.greeting('Dash'),
builder: (context, AsyncSnapshot<String> snapshot) {
return Center(
child: Text(snapshot.data ?? 'default'),
);
},
);
}
}
Result: Hello Dash
In your case smt seems to be an int not a String
and you have to pass it as query parameter to http request as follows
static Future<Map<String, DataKuliahModel>> getDataKuliah(int smt) async {
String url = Constant.baseURL;
String token = await UtilSharedPreferences.getToken();
await Future.delayed(const Duration(milliseconds: 1000));
// String responseJson = await rootBundle.loadString('assets/1.json');
Map<String, DataKuliahModel> finalResult = {};
final response = await http.get(
// Uri.parse(
// '$url/auth/mhs_siakad/perwalian/get_paket',
// ),
Uri.http(url, '/auth/mhs_siakad/perwalian/get_paket',
{'smt':smt}),
headers: {
'Authorization': 'Bearer $token',
},
);
final result = jsonDecode(response.body)['data'] as Map<String, dynamic>;
result.forEach((key, value) {
DataKuliahModel dataKuliah = DataKuliahModel.fromMap(value);
finalResult.addAll({
key: dataKuliah,
});
});
return finalResult;
}
Have you looked at the Uri replace method?
You can do the following:
Uri.parse('$url/auth/mhs_siakad/perwalian/get_paket').replace(queryParameters:{ "smt":"$smt"});
Update on FutureBuilder:
// Put this outside your build function
Future<Map<String, DataKuliahModel>> DK ;
// Put this in your initState if you want the future to run on page load or use it for events like onTap
DK = Service.getDataKuliah(<PARAM>);
// This is in your build method
FutureBuilder(
future:DK,
builder: (context, snapshot) {
// add wigets to display results here
}
)

Running Multiple Async Requests at Once in Dart. Proper way

I am trying to work with concurrency in Flutter, so I have three get-request from server and I need to get values from them at the same time. Each request was parsed into a model. Now I'm trying to figure out how I can combine all the models into one list with three get-request and run this the final list in ListView.builder. Also my task is quite hard for such noobie as I am, beacuse besides parsed concurrently all three lists I need to filter them out, because as said in API all requests are nested and depends on id. How can i resolve this?
This is my models:
ScheduleVariants{
final int mrId;
final int mvId;
ScheduleVariants({this.mrId, this.mvId});
}
FlightCard{
final int mvId;
final int stId;
FlightCard({this.mrId, this.stId});
}
Stop{
final int stId;
Stop({this.stId})
}
I need to get final values from Stop models. As you can see all models have nested stucture and I can't avoid this.
Now I am trying to make concurrent call like this:
class Dire extends StatefulWidget {
final int mrId;
final int mvId;
final int stId;
const Dire({Key key, this.mrId, this.mvId, this.stId}) : super(key: key);
#override
_DireState createState() => _DireState();
}
class _DireState extends State<Dire> {
#override
void initState() {
fetchData();
super.initState();
stops.where((element) => element.stId == widget.stId).toList();
card.where((element) => element.mvId == widget.mvId).toList();
sheduler.where((element) => element.mrId == widget.mrId).toList();
}
List<ScheduleVariants> sheduler;
List<FlightCard> card;
List<Stop> stops;
Future fetchData() async {
String username = '';
String password = '';
String basicAuth =
'Basic ' + base64Encode(utf8.encode('$username:$password'));
print(basicAuth);
final result = await Future.wait([
http.get(
Uri.parse(
"http://mysecurelink/getMarshVariants.php?fmt=json"),
headers: <String, String>{'authorization': basicAuth}),
http.get(
Uri.parse(
"http://mysecurelink/getFlightCard.php?fmt=json&mv_id"),
headers: <String, String>{'authorization': basicAuth}),
http.get(
Uri.parse(
"http://mysecurelink/getStops.php?fmt=json"),
headers: <String, String>{'authorization': basicAuth}),
]);
setState(() {
sheduler = json.decode(result[0].body) as List;
card = json.decode(result[1].body) as List;
stops = json.decode(result[2].body) as List;
});
}
#override
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: FutureBuilder(
future: fetchData(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: stops.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(stops[index].stTitle),
);
});
} else {
return CircularProgressIndicator();
}
}));
}
}
At the end of the main task is to run three parallel request filtered by ID and get data from the Stops model. How can you do it right?
I am noob and don't get how properly to do it and I will really glad if someone help me to figure this task out.
I am going to make some assumptions, because there is not enough information:
Dire is a combination of the three classes ScheduleVariants, FlightCard and Stop where ScheduleVariants.mvId == FlightCard.mvId and FlightCard.stId == Stop.stId.
All three APIs will return a list as their response.
All ScheduleVariants have unique mvId, all FlightCards have unique mvId and stId, and all Stops have unique stId.
There is nothing wrong with the way you execute multiple asynchronous requests. Future.wait in this case takes in a list of futures and returns a list of responses. The problem you are facing is just that you do not know how to merge the responses from the three API requests.
You seem to also be mixing up the use of state with the use of futures. At least in the code snippet you provided, it does not seem like you ever need to change the state after you initialize it, which means you do not need to use state at all.
Dire should just be a model class.
class Dire {
final ScheduleVariants scheduleVariant;
final FlightCard flightCard;
final Stop stop;
Dire(this.scheduleVariant, this.flightCard, this.stop);
}
In your widget where you want to get the Dires from the APIs, you can use this function in the FutureBuilder:
Future<List<Dire>> fetchData() async {
String username = '';
String password = '';
String basicAuth =
'Basic ' + base64Encode(utf8.encode('$username:$password'));
print(basicAuth);
final result = await Future.wait([
http.get(
Uri.parse(
"http://mysecurelink/getMarshVariants.php?fmt=json"),
headers: <String, String>{'authorization': basicAuth}),
http.get(
Uri.parse(
"http://mysecurelink/getFlightCard.php?fmt=json&mv_id"),
headers: <String, String>{'authorization': basicAuth}),
http.get(
Uri.parse(
"http://mysecurelink/getStops.php?fmt=json"),
headers: <String, String>{'authorization': basicAuth}),
]);
flightCardMap = HashMap.fromIterable(json.decode(result[1].body), (fc) => fc["mvId"], (fc) => FlightCard(fc));
stopMap = HashMap.fromIterable(json.decode(result[2].body), (s) => s["stId"], (s) => Stop(s));
return json.decode(result[0].body).map((sv) => {
flightCard = flightCardMap[sv["mvId"]];
return Dire(ScheduleVariants(sv), flightCard, stopMap[flightCard["stId"]]);
}).toList();
}
A disclaimer: I did not check this code snippet for syntax errors, so there might be some but the general idea is there.

Connect to Yahoo Finance API with Flutter

I am trying to connect to Yahoo Finance API within my Flutter application, and I canĀ“t connect.
I've seen guidance to connect to the API in different languages but there is no official guidance for Dart language.
I have the following method to connect to the API:
Future<double> getStockPrice(String symbol) async {
//var client = http.Client();
var url = 'https://apidojo-yahoo-finance-v1.p.rapidapi.com';
var apiStr = "/stock/v2/get-summary";
var headers = {'x-rapidapi-key': "809135c7c7mshf35d1107bf50919p15c9fajsn37166d6b1bfc",
'x-rapidapi-host': "apidojo-yahoo-finance-v1.p.rapidapi.com"};
var params = {'symbol': 'AMRN', 'region': 'US'};
var response = await http.get(Uri.https(url, apiStr,params),headers: headers);
//https://apidojo-yahoo-finance-v1.p.rapidapi.com/stock/v2/get-summary?symbol=AMRN&region=US
//var response = await http.get(Uri.https(url, apiStr, params));
print('RESPONSE STATUS code: ${response.statusCode}');
if (response.statusCode == 200) {
print('RESPONSE STATUS code: ${response.statusCode}');
var json = jsonDecode(response.body);
String value = json['price']['regularMarketOpen']['raw'];
return double.parse(value);
} else {
return 0.0;
}
}
And I get null from this method. I guess it can be a matter of getting value variable, maybe I am not traversing the JSON tree correctly. But I don't see why.
Then I retrieve the data from the API in the following screen:
import 'package:flutter/material.dart';
import 'package:stock_analyzer/api_method/webService.dart';
class TestScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
Future<String> test = YahooFinanceService().getWebSite('symbol');
return Scaffold(
appBar: AppBar(
title: Text('test'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FutureBuilder<String>(
future: test,
builder: (context, AsyncSnapshot<String> snapshot){
return Text('Result: ${snapshot.data}');
})
],
),
);
}
}
In this screen I get null. So, snapshot.data = null
Thanks
The 401 error shows that you are not authorized to make a post request, I think the error is that you are doing post instead of get.
Try changing:
var response = await client.post(url, body: headers);
To:
var response = await client.get(url, headers: headers);

Flutter http request from Rapid Api not loading

I am tying retrieve data from an api on Rapid Api using Dart's http package and displaying it using Flutter however the content never loads and the api doesn't return an error.
class APIService {
// API key
static const _api_key = <MYAPIKEY>;
// Base API url
static const String _baseUrl = "covid-19-data.p.rapidapi.com";
// Base headers for Response url
static const Map<String, String> _headers = {
"content-type": "application/json",
"x-rapidapi-host": "covid-19-data.p.rapidapi.com",
"x-rapidapi-key": _api_key,
};
Future<CovidNumbers> fetchData(
{#required String endpoint, #required Map<String, String> query}) async {
Uri uri = Uri.https(_baseUrl, endpoint, query);
final response = await http.get(uri, headers: _headers);
if (response.statusCode == 200) {
return CovidNumbers.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to load Data');
}
}
}
The method is then called onInit
Future<CovidNumbers> data;
APIService apiService = APIService();
#override
void initState() {
super.initState();
data = apiService.fetchData(
endpoint: "/country", query: {"format": "json", "name": "malta"});
}
And finally I display it in a FutureBuilder
FutureBuilder<CovidNumbers>(
//future: futureCovidNumbers,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(
"Confirmed Cases: ${snapshot.data.confirmed.toString()}");
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
));
The app remains stuck on the CircularProgressIndicator and does not display an error.
you future is empty, for that reason always is returning a CircularProgressIndicator, place your "data" variable inside the future and try again

Building widget in Flutter when response statusCode on API call is >400

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.