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®ion=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);
Related
I am learning to use api and all data in my interface is showing as null
in this section to understand where the problem lies i change ! with ?
How can I check if there is a problem while pulling the data?
I deleted the irrelevant parts to shorten the code.
WeatherApiClient client = WeatherApiClient();
Hava? data;
Future<void> getData()async{
data = await client.getCurrentWeather("London");
}
#override
Widget build(BuildContext context) {
return MaterialApp(
body: FutureBuilder(
future: getData(),
builder: (context, snapshot){
if(snapshot.connectionState==ConnectionState.done){
return Column(
children: [
GuncelVeri(Icons.wb_sunny_rounded, "${data?.derece}", "${data?.sehir}"),
bilgiler("${data?.humidity}","${data?.feels_like}", "${data?.pressure}","${data?.description}"),],
);
}
else if(snapshot.connectionState == ConnectionState.waiting){
return Center(
child: CircularProgressIndicator(),
);
}
return Container();
},
)
),
);
}
}
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:weatherapp/model.dart';
class WeatherApiClient{
Future<Hava>? getCurrentWeather(String? location)async{
var endpoint = Uri.parse(
"https://api.openweathermap.org/data/2.5/weather?q=$location&appid=9b6ece44d1233c111b86cacb5e3617f1&units=metric&lang=tr"
);
var response = await http.get(endpoint);
var body = jsonDecode(response.body);
print(Hava.fromJson(body));
return Hava.fromJson(body);
}
}
Your are getting Null because you are trying to access the Hava class which have not been initilized yet.
You are printing print(Hava.fromJson(body));
before return Hava.fromJson(body);
First you want to check if what responce is getting from the http call.
for that you can just print the http responce like this:-
print(responce.body)
Then you can see what's the responce is.
They you can do return Hava.fromJson(body);
The right way
Check the status code of the responce, if the responce is 200 the cast the json responce to the model class or show an error
Refer code
Future<Profileclass> fetchprofile() async {
String tokenFetch = await getStringValuesSF();
return await Dio()
.get(urlprofiledio,
options: Options(
headers: {
'Authorization': 'Bearer $tokenFetch',
},
))
.then((response) {
if (response.statusCode == 200) {
return Profileclass.fromJson(jsonDecode(response.toString()));
} else {
throw Exception('Failed to load profile');
}
});
}
Thank you
I am using Riverpod's FutureProvider with family. The FutureProvider keeps on running again and again. It shows the loading dialog only. Also the hot reload stops working. FutureProvider is working fine without family. Please help in finding what's wrong.
final ephemerisProvider =
Provider((ref) => ApiService("https://localhost"));
final ephemerisFutureProvider = FutureProvider.family
.autoDispose<EpheModel, Map<String, dynamic>>((ref, data) async {
var response = await ref.read(ephemerisProvider).getData(data);
print(EpheModel.fromJSON(response));
return EpheModel.fromJSON(response);
});
class Kundlis extends ConsumerWidget {
static const routeName = "/kundlis";
#override
Widget build(BuildContext context, ScopedReader watch) {
final AsyncValue<EpheModel> kundlis = watch(ephemerisFutureProvider({}));
return Scaffold(
appBar: AppBar(
title: Text("Kundlis"),
),
drawer: AppDrawer(),
body: kundlis.when(
data: (kundli) => Center(child: Text(kundli.toString())),
loading: () => ProgressDialog(message: "Fetching Details..."),
error: (message, st) =>
CustomSnackBar.buildErrorSnackbar(context, '$message')));
}
}
class ApiService {
final String url;
ApiService(this.url);
Future<Map<String, dynamic>> getData(Map<String, dynamic> data) async {
try {
http.Response response = await http.post(url + "/ephe",
headers: <String, String>{'Content-Type': 'application/json'},
body: jsonEncode(data));
if (response.statusCode == 200) {
return data;
} else {
throw Exception("Error Fetching Details");
}
} on SocketException {
throw Exception("No Internet Connection");
} on HttpException {
throw Exception("Error Fetching Details");
}
}
}
{} != {}. Because of .family, you are creating a completely new provider every time you call watch(ephemerisFutureProvider({})). To select a previously-built provider via family, you must pass an identical value. And {} is never identical to {}, guaranteed. :)
I am working with a FutureBuider and I don't know how I can load a Future that receives an argument. I get an error, the future is in an external file 'provider.dart', I attach an image of the error:
1 positional argument(s) expected, but 0 found.
Try adding the missing arguments.dart(not_enough_positional_arguments)
To explain myself better I have to mention the structure of the application:
HomePage: in it you select an image that will be sent to a 'provider.dart' file.
PhotoPage: On this page, a value obtained from the Provider.dart file is obtained through a FutureBuilder, as shown in the image above.
children: [
FutureBuilder(
future: fotoProvider.uploadImageCloudinary(),
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasError) {
return Center(
child: Text('ERROR: ${snapshot.error.toString()}'),
);
}
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return Center(
// I WOULD LIKE TO PRESENT THE RESULTS OF THE SECOND FUTURE HERE
child: Text(snapshot.data),
);
},
),
],
),
Provider.dart: There are two Future's:
1 Future: It receives the image from the home and processes it sending it to the Cloudinary API, I get the url generated by Cloudinary to be sent to the second Future.
Future<String> uploadImageCloudinary(File imagen) async {
final url = Uri.parse('https://api.cloudinary.com/...');
final mimeType = mime(imagen.path).split('/'); // Image/jpeg
final imageUploadRequest = http.MultipartRequest('POST', url);
final file = await http.MultipartFile.fromPath(
'file',
imagen.path,
contentType: MediaType(mimeType[0], mimeType[1]),
);
imageUploadRequest.files.add(file);
// execute request
final streamResponse = await imageUploadRequest.send();
final resp = await http.Response.fromStream(streamResponse);
if (resp.statusCode != 200 && resp.statusCode != 201) {
print('Something went wrong');
print(resp.body); // Error
return null;
}
dynamic respData = json.decode(resp.body);
print(respData);
urlGenCloudinary = respData['secure_url'];
// Shipment to the second future
return await requestSecondAPI(urlGenCloudinary);
}
2 Future: It receives the url and sends it to another API, this one processes it and generates a String that is the data that I want to be sent to FutureBuilder in FotoPage.
Future requestSecondAPI(String url) async {
final String apiUrl =
"https://api...";
final res = await http.post(
Uri.parse(apiUrl),
headers: {
"Content-type": "application/json",
"Authorization": "Key "
},
body: jsonEncode({
"inputs": [
{
"data": {
"image": {'url': url}
}
}
],
"model": {
"output_info": {
"output_config": {"max_concepts": 40, "min_value": 0.8}
}
}
}),
);
final resReqRes = reqResRespuestaFromJson(res.body);
String resultado = resReqRes.outputs[0].data.concepts[0].id;
return resultado;
}
Thank you in advance.
I tried to place the second Future in the FutureBuilder and I get the following errors:
In FutureBuilder:
"Couldn't infer type parameter 'T'. "
In the future within the FutureBuilder:
"1 positional argument(s) expected, but 0 found.
Try adding the missing arguments."
In the builder:
"The argument type 'Center Function(BuildContext, AsyncSnapshot)' can't be assigned to the parameter type 'Widget Function(BuildContext, AsyncSnapshot)' "
I am getting a null when calling my Future builder.
I have my api setup like this:
Future getDriverInfo() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var _token = prefs.getString('token');
var dProfile;
var url =
'http://buddies-8269.herokuapp.com/api/driver/current_user/?access=$_token';
await http.post(url, headers: {"Content-Type": "application/json"}).then(
(http.Response response) {
switch (response.statusCode) {
case (200):
var responseData = json.decode(response.body);
DriverProfile driverProfile = DriverProfile.fromJson(responseData);
print('Driver Info API: Got Data ${driverProfile.status.user.email}');
dProfile = driverProfile.status;
break;
case (500):
print('500 Error ${response.body}');
break;
}
return dProfile;
});
}
For the future builder I wrote:
_getInfo = getDriverInfo();
Widget _buildDataWidget() {
return Container(
height: 10,
child: FutureBuilder(
future: getDriverInfo(),
builder: (context, snapshot) {
if (!snapshot.hasData == null) {
return Center(child: CircularProgressIndicator());
} else {
var x = snapshot.data;
print('The Drivers data is $x');
return Container(
child:Text(x)
);
}
}));
}
The console returns "The Drivers data is null" but, when I print out the data directly from the api function, I get data. Could you let me know what I've done wrong here.
Using the await keyword together with .then might be causing some unexpected outcomes. Rewrite the function to just use await.
http.Response response = await http.post(url, headers: {"Content-Type": "application/json"})
switch (response.statusCode) {
case (200):
var responseData = json.decode(response.body);
DriverProfile driverProfile = DriverProfile.fromJson(responseData);
print('Driver Info API: Got Data ${driverProfile.status.user.email}');
dProfile = driverProfile.status;
break;
case (500):
print('500 Error ${response.body}');
break;
}
return dProfile;
You might be getting status code other than 200 or 500 from post request. You've not handled default case in switch statement in your code snippet. Try adding a default case and check if there's some other error.
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.