Http request does not complete - csv

I'm trying to get data from a csv file on the internet:
https://covid.ourworldindata.org/data/owid-covid-data.csv
If you clicked on it you might have noticed that it downloads automatically, which I think is the problem.
When I'm using the http library in dart to get this data, I only get back a small part of it at the beginning. The response.statusCode is still equal to 200 and everything else works as expected.
I think that the automatic download is part of the problem but I'm not sure how to fix that
Here is the code:
import 'package:http/http.dart' as http;
...
main(){
String url = 'https://covid.ourworldindata.org/data/owid-covid-data.csv';
http.Response response = await http.get(url);
print(response.body);
}

Here is an example with json format:
Future<Response> fetchData() async
{
return await http.get("https://covid.ourworldindata.org/data/owid-covid-data.json");
}
Then inside the build() method:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: FutureBuilder(
future: fetchData(),
builder: (context, AsyncSnapshot<Response> snapshot) {
if (snapshot.hasData) {
Map<String, dynamic> map =
Map.from(jsonDecode(snapshot.data.body));
return Text(map["LBN"]["continent"]);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
), // This trailing comma makes auto-formatting nicer for build methods.
));
}

Related

Flutter how to json decode with a string and a list

I've spent around an hour looking for this solution somewhere online. I'm new to flutter & dart languages but I'm very comfortable with C# and .net. Even tho dart/flutter use C# syntax a lot of the language feels much different than I thought it would.
I have a restful API in .net which returns a json object of String : String and String : [Array of Strings]. I have an object class within flutter where I can deserialize the response. I already done this with a normal response of just List and String without a problem but now I ran into massive problem. I don't have a clue how I can deserialise a Json that looks like this.
As requested
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
centerTitle: true,
backgroundColor: Colors.blueGrey,
foregroundColor: Colors.white,
),
drawer: const NavigationDrawer(),
body: Column(
children: [
Center(
child: Text(templateName),
),
Center(
child: FutureBuilder<TemplateContentAndArgumentsObject>(
future: templateContent,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Text(snapshot.data?.TemplateContent ?? "null");
} else {
return CircularProgressIndicator();
}
},
),
),
],
),
);
Api Call Code
Future<TemplateContentAndArgumentsObject> getTemplateContent(
String customerId, String templateName) async {
var url = Uri.parse(
'https://localhost:7167/api/v1/Templates/$customerId/$templateName');
var response = await http.get(url, headers: {
"Accept": "application/json",
"Access-Control-Allow-Origin": "*"
});
try {
print(response.body);
var sm = json.decode(response.body);
print(sm);
} catch (ex) {
print(ex);
}
if (response.statusCode == 200) {
TemplateContentAndArgumentsObject obj =
TemplateContentAndArgumentsObject.fromJson(json.decode(response.body));
print(obj.TemplateContent);
print(obj.TemplateArguments);
return obj;
} else {
print('Request failed with status: ${response.statusCode}');
}
return TemplateContentAndArgumentsObject(
TemplateContent: "", TemplateArguments: new List<String>.empty());
}
Class Object
import 'package:flutter/cupertino.dart';
class TemplateContentAndArgumentsObject {
String TemplateContent;
List<String> TemplateArguments;
TemplateContentAndArgumentsObject({
required this.TemplateContent,
required this.TemplateArguments,
});
factory TemplateContentAndArgumentsObject.fromJson(
Map<String, dynamic> json,
) =>
TemplateContentAndArgumentsObject(
TemplateContent: json["TemplateContent"] as String,
TemplateArguments: (json["TemplateArguments"] as List<String>),
);
}
Image of Json
Below is Sample Code for your problem. Please be aware that the code is just created based on your example where your list just contains String objects. In case your list contains more advanced objects, we should model them individually and put into a list. But for the strings you can do something like this:
class TemplateContentAndArgumentsObject {
String myStringContent;
List<String> myArrayContent;
TemplateContentAndArgumentsObject({
required this.myStringContent,
required this.myArrayContent,
});
factory TemplateContentAndArgumentsObject.fromJson(
Map<String, dynamic> json,
) =>
TemplateContentAndArgumentsObject(
myStringContent: json["myStringContent"] as String,
myArrayContent:
(json["myArrayContent"] as List<dynamic>).cast<String>(),
);
Map<String, Object> toJson() => {
"stringContnet": myStringContent,
"arrayCOntnet": myArrayContent,
};
}
I have changed the fromJson constructor into a factory constructor that just calls the class constructor. By doing so it removes the need for the class variables to be declared late.
Hey you can modify your build method, you need to check condition snapshot.hasData, for more detail see FutureBuilder
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
centerTitle: true,
backgroundColor: Colors.blueGrey,
foregroundColor: Colors.white,
),
drawer: const NavigationDrawer(),
body: Column(
children: [
Center(
child: Text(templateName),
),
Center(
child: FutureBuilder<TemplateContentAndArgumentsObject>(
future: templateContent,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data?.TemplateContent ?? "");
}else if (snapshot.hasError){
/// return error widget
return Container();
} else {
return CircularProgressIndicator();
}
},
),
),
],
),
);
//map to String
Map<String, dynamic> mapData = {
"username":name,
"email":email,
"phoneNumber":mobileNo,
"password":password ,
"refCode": inviteCode,
"countryCode":countryCode,
"country": "india"
};
json.encode(mapData);
// map to list
List values = List();
mapData.forEach((v) => values.add(v));
print(values);

Mysql query result in Flutter Widgets

Trying to display in Flutter a result I am receiving from my Node.JS server via MySQL query:
[{"NAME":"Matematicas"},
{"NAME":"Naturales"},
{"NAME":"Ciencias Sociales"},
{"NAME":"Lenguaje"},
{"NAME":"Religion"}]
This is the class I am using in Flutter to handle it:
class Subject {
final String name;
Subject({
required this.name,
});
factory Subject.fromJson(Map<String, dynamic> json) {
return Subject(name: json['NAME']);
}
}
This is the method from which I obtain the data:
Future<Subject> fetchSubject() async {
var prefs = await SharedPreferences.getInstance();
var token = prefs.getString('token');
var response = await http.get(Uri.parse('http://localhost:8000/subjects'),
headers: {'x-access-token': token!});
print(response.body);
return Subject.fromJson(jsonDecode(response.body));
}
This is my initState
void initState() {
super.initState();
futureSubject = fetchSubject();
}
This is my Widget build piece:
Widget build(BuildContext context) {
return FutureBuilder<Subject>(
future: fetchSubject(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('Error'),
);
} else if (snapshot.hasData) {
return Scaffold(
appBar: AppBar(
title: Text('Materias'),
backgroundColor: Colors.green[300],
actions: [
Padding(
padding: EdgeInsets.only(right: 3.0),
child: IconButton(
icon: Icon(Icons.logout),
//TODO llamar funcion logout
onPressed: () {},
iconSize: 26,
),
)
],
),
body: Text(snapshot.data!.name));
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
});
}
This is what I get:
Uncaught (in promise) Error: Expected a value of type 'Map<String, dynamic>', but got one of type 'List<dynamic>'
I just want to display the information I am receiving as a List or table like fashion. Any ideas on what and how to refactor this?
Its happened because your return data is an array. Try this
final data = json.decode(response.body);
return List<Subject>.from(data.map((value) => Subject.fromJson(value)));
It looks like the fetchSubject method needs to be modified and the widget itself. The data you displayed is a List of objects, thus the error that you are trying to see type Map<String, dynamic> from jsonDecode(response.body) but it returns a List<dynamic> instead. Thus, you need to modify fetchSubject and get a List<Subject from your API not just an object. Or, you need to update an API. Just as an example (haven't tested it but should work):
Future<List<Subject>> fetchSubject() async {
var prefs = await SharedPreferences.getInstance();
var token = prefs.getString('token');
var response = await http.get(Uri.parse('http://localhost:8000/subjects'),
headers: {'x-access-token': token!});
print(response.body);
return jsonDecode(response.body).map((item) => Subject.fromJson(item));
}
and change all logic to handle a List of Subject and not just Subject. The JSON your API returns is a list (array) of objects, not just an object.

Futubuilder snapshot.hasData returning false

I wrote code to fetch data and put them to GridView Buider.
But condition snapshot.hasData not working, its always False and return CircularProgressIndicator().
But If I change condition to !snapshot.hasData(true) my code is working, and fetch data shows on the screen correctly.
Fetch API
List<String> pcBusy = [];
Future fetchDataStandart() async {
final urlAuth =
Uri.parse('http://xxx.xx.xxx.xxx/api/usersessions/activeinfo');
final response = await http
.get(urlAuth, headers: <String, String>{'authorization': basicAuth});
if (response.statusCode == 200) {
List listPc = List.from(json.decode(response.body)['result']);
pcBusy.clear();
for (int i = 0; i < listPc.length; i++) {
pcBusy.add(listPc[i]['hostName']);
}
print(pcBusy);
} else {
throw Exception('Error');
}
Builder code
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(
'ЕВРОКО Стандарт',
),
),
body: FutureBuilder(
future: fetchDataStandart(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ComputerGrid();
} else {
return Center(
child: CircularProgressIndicator(),
);
}
Your fetchDataStandart() function does not have any return statement. Therefore calling it will not give you any data. You need to add a return statement. I do not know if you have just not shared the complete function, because one closing bracket is missing at the end.

JSON Array to JSON Object

I'm using a HTTP request that gets a JSON array and pushes this into a JSON object which is read by a list view. I'm having difficulty forcing the JSON array into a JSON object so I'm currently calling each object once via json.decode(response.body)[0]. How can I cast the JSON Array to a JSON Object and have the list view read this entire JSON object?
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Post> fetchPost() async {
final url = <my_url>;
final response =
await http.get(url);
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON.
print(json.decode(response.body));
// TODO: Identify a way to convert JSON Array to JSON Object
return Post.fromJson(json.decode(response.body)[0]);
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
class Post {
final String title;
Post({this.title});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
title: json['title']
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<Post> post;
#override
void initState() {
super.initState();
post = fetchPost();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Post>(
future: post,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.title);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
),
);
}
}
try this,
Future<List<Post>> fetchPost() async {
final url = <my_url>;
final response =
await http.get(url);
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON.
print(json.decode(response.body));
List<dynamic> responseList = json.decode(response.body);
// TODO: Identify a way to convert JSON Array to JSON Object
List<Post> tempList = [];
responseList.forEach((f) {
tempList.add(Post.fromJson(f));
});
return tempList;
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
class Post {
final int id;
final String title;
Post({this.id, this.title});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(id: json['id'], title: json['title']);
}
}
class _Frag_CommitteeState extends State<Frag_Committee> {
Future<List<Post>> post;
#override
void initState() {
super.initState();
post = fetchPost();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<List<Post>>(
future: post,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Text(snapshot.data[index].title);
});
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
);
}
}

Fetch Api Data Automatically with Interval in Flutter

On my flutter application I am trying to show updating data. I am successful in getting data from weather api manually. But I need to constantly grab data every 5 seconds. So it should be updated automatically. Here is my code in Flutter :
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sakarya Hava',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Sakarya Hava'),
),
body: Center(
child: FutureBuilder<SakaryaAir>(
future: getSakaryaAir(), //sets the getSakaryaAir method as the expected Future
builder: (context, snapshot) {
if (snapshot.hasData) { //checks if the response returns valid data
return Center(
child: Column(
children: <Widget>[
Text("${snapshot.data.temp}"), //displays the temperature
SizedBox(
height: 10.0,
),
Text(" - ${snapshot.data.humidity}"), //displays the humidity
],
),
);
} else if (snapshot.hasError) { //checks if the response throws an error
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
),
),
),
);
}
Future<SakaryaAir> getSakaryaAir() async {
String url = 'http://api.openweathermap.org/data/2.5/weather?id=740352&APPID=6ccf09034c9f8b587c47133a646f0e8a';
final response =
await http.get(url, headers: {"Accept": "application/json"});
if (response.statusCode == 200) {
return SakaryaAir.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to load post');
}
}
}
I found such a snippet to benefit from :
// runs every 5 second
Timer.periodic(new Duration(seconds: 5), (timer) {
debugPrint(timer.tick);
});
Probably I need to wrap and call FutureBuilder with this snippet but I was not able to understand how to do it.
Futures can have 2 states: completed or uncompleted. Futures cannot "progress", but Streams can, so for your use case Streams make more sense.
You can use them like this:
Stream.periodic(Duration(seconds: 5)).asyncMap((i) => getSakaryaAir())
periodic emits empty events every 5 seconds and we use asyncMap to map that event into another stream, which get us the data.
Here is working example:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class ExamplePage extends StatelessWidget {
Future<String> getSakaryaAir() async {
String url =
'https://www.random.org/integers/?num=1&min=1&max=6&col=1&base=10&format=plain&rnd=new';
final response =
await http.get(url, headers: {"Accept": "application/json"});
return response.body;
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: Stream.periodic(Duration(seconds: 5))
.asyncMap((i) => getSakaryaAir()), // i is null here (check periodic docs)
builder: (context, snapshot) => Text(snapshot.data.toString()), // builder should also handle the case when data is not fetched yet
);
}
}
You can refactor your FutureBuilder to use a Future variable instead of calling the method in the FutureBuilder. This would require you to use a StatefulWidget and you can set up the future in your initState and update it by calling setState.
So you have a future variable field like:
Future< SakaryaAir> _future;
So your initState would look like this :
#override
void initState() {
super.initState();
setUpTimedFetch();
}
where setUpTimedFetch is defined as
setUpTimedFetch() {
Timer.periodic(Duration(milliseconds: 5000), (timer) {
setState(() {
_future = getSakaryaAir();
});
});
}
Finally, your FutureBuilder will be changed to:
FutureBuilder<SakaryaAir>(
future: _future,
builder: (context, snapshot) {
//Rest of your code
}),
Here is a DartPad demo: https://dartpad.dev/2f937d27a9fffd8f59ccf08221b82be3