I want to fetch data from api and showing on app screen but there nothing on screen but a circular progress indicator. if i try to print response.body on consol it works just fine and showing the body of data in consol but it not showing anything to app screen. weher is the problem of my code???
I try to change itemcount: snapshot.data?.length, but didnt do anything. if i want to print api data on consol it works fine. but not showing to screen.
class _MyHomePageState extends State<MyHomePage> {
List<ApiModel> apiModel = [];
final url = "https://newsapi.org/v2/top-headlines?country=us&category=sports&apiKey=bb66e3f0405944a58cb6853e55995bec";
Future<List<ApiModel>> getData() async {
final response = await http.get(Uri.parse(url));
var data = jsonDecode(response.body.toString());
if (response.statusCode == 200) {
for (Map<String, dynamic> index in data) {
apiModel.add(ApiModel.fromJson(index));
}
return apiModel;
} else {
return apiModel;
}
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: apiModel.length,
itemBuilder: (context, index){
return Container(
height: 200,
child: Column(
children: [
Text.rich(TextSpan(children: [
TextSpan(text: 'data'),
TextSpan(text: 'data'),
]))
],
),
);
});
}else{
return Center(child: CircularProgressIndicator(),);
}
});
}
}
Your data is not correctly mapped in you ApiModel because you mapping in wrong way so update your getData() with below code.
Future<ApiModel> getData() async {
try {
final response = await http.get(Uri.parse(url));
var data = jsonDecode(response.body.toString());
if (response.statusCode == 200) {
apiResposne = ApiModel.fromJson(data);
return apiResposne;
} else {
return apiResposne;
}
} catch (ex) {
return apiResposne;
}
}
Also you need to update FutureBuilder UI widget part to update data so replace below code.
FutureBuilder(
future: getData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: apiResposne.articles?.length ?? 0,
itemBuilder: (context, index) {
return SizedBox(
height: 200,
child: Column(
children: [
Text.rich(TextSpan(children: [
TextSpan(
text:
(apiResposne.articles?[index].author ?? "")),
TextSpan(
text: (apiResposne.articles?[index].publishedAt ??
"")),
]))
],
),
);
});
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
})
Related
I have been trying with last an hour but not getting solution and failing completely to understand why its showing an error...
I have created a function for fetching data,
I have placed print statement for seeing what does it returns...here it is printing data but while inside feature builder it showing an error...
when I run app its showing output with
list<dynamic> is not a subtype of type FutureOr<List<Map<String,dynamic>>
it means its executes snapshot.haserror part
here is my code
class _HomeScreenState extends State<HomeScreen> {
Future<List<Map<String,dynamic>>> fetchdata() async {
var resp =
await http.get(Uri.parse("https://jsonplaceholder.typicode.com/photos"));
print("fetchdata function showing"+json.decode(resp.body).toString());
return json.decode(resp.body);
}
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: MyBody(),
),
);
}
MyBody() {
return FutureBuilder<List<Map<String,dynamic>>>(
future: fetchdata(),
builder: (context, snapshot) {
print("Futurebuilder showing:"+snapshot.toString());
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
print('againt'+snapshot.toString());
List<Map<String,dynamic>> data = snapshot.data ?? [];
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(8.0),
child: Text(data[index]['title']));
});
}
}
},
);
}}
Future<List<Map<String, dynamic>>> fetchdata() async {
var resp = await http
.get(Uri.parse("https://jsonplaceholder.typicode.com/photos"));
print("fetchdata function showing" + json.decode(resp.body).toString());
List<dynamic> result = jsonDecode(resp.body);
return result.map((e) => e as Map<String, dynamic>).toList();
}
just change your function like this
Your API Call:
Future<List<dynamic>> getJobsData() async {
String url = 'https://jsonplaceholder.typicode.com/photos';
var response = await http.get(Uri.parse(url), headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
});
return json.decode(response.body);
}
Your Widget:
Center(
child: FutureBuilder<List<dynamic>>(
future: getJobsData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
var title = snapshot.data![index]['title'];
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
child: ListTile(
title: Text(title),
),
);
},
),
);
}
return CircularProgressIndicator();
},
),
),
Result->
I have been working on solving an exception error(FormatException: Unexpected character (at character 1)) that is present at the json.decode(response.body) section of the code. I have no idea on how I can solve that. Any response from you guys is highly appreciated. Below is the code:
Future<List<Garage>> garagesFuture = getGarages();
static Future<List<Garage>> getGarages() async {
const url =
'https://console.firebase.google.com/project/atta-web-app-a5135/database/atta-web-app-a5135-default-rtdb/data/~2FGarages';
final response = await http.get(Uri.parse(url));
final body = json.decode(response.body);
return body.map<Garage>(Garage.fromJson).toList();
}
#override
Widget build(BuildContext context) {
final ref = dref.ref().child('Garages');
return Scaffold(
body: Center(
child: FutureBuilder<List<Garage>>(
future: garagesFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasData) {
final garages = snapshot.data!;
return buildGarage(garages);
} else {
return const Text('No Garages Available');
}
},
),
),
);
}
buildGarage(List<Garage> garages) {
ListView.builder(
itemCount: garages.length,
itemBuilder: (context, index) {
final garage = garages[index];
return Card(
child: ListTile(
title: Text(garage.garageName),
subtitle: Text(garage.officeNumber),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: ((context) => const MessageCenter())));
},
),
Seems your problem with decoder.
Try using
json.decode(utf8.decode(response.bodyBytes))
to convert the encoding to utf8
I have a method that fetches a PatientLog from SQLite.However, This PatientLog table mapped to an object with a class named PatientLog. Inside this PatientLog class, several other objects such as Speciality, AttendingPhysician, Course, etc. I need to map these PatienLog records to a local object. However, I have to use nested Futures. I need to retrieve the data from this nested Future. Think of Future of Future.
This is my fetch method
Future<List<Future<PatientLog>>> getForms() async {
Database db = await instance.getDatabase;
List<Map<String, dynamic>> forms =
await db.query(_tablePatientLog, orderBy: 'id DESC');
Institute? institute;
AttendingPhysician? attendingPhysician;
Speciality? speciality;
Course? course;
List<Future<PatientLog>> list = forms.map((myMap) async {
int? courseId = myMap['course_id'] as int?;
int? specialityId = myMap['speciality_id'] as int?;
int? attendingId = myMap['attending_id'] as int?;
int? instituteId = myMap['institute_id'] as int?;
if (courseId != null) {
await getCourse(courseId).then((value) => course=value);
}
if (attendingId != null) {
await getAttending(attendingId).then((value) => attendingPhysician=value);
}
if (specialityId != null) {
await getSpeciality(specialityId).then((value) => speciality=value);
}
if (instituteId != null) {
await getInstitute(instituteId).then((value) => institute=value);
}
return PatientLog.fromMap(
myMap, institute, course, attendingPhysician, speciality);
}).toList();
return list;
}
I need to display that information on a screen. I get an error type 'List<Future<PatientLog>>' is not a subtype of type 'Future<Object?>?'
class _DraftsState extends State<Drafts> {
final SQFLiteHelper _helper = SQFLiteHelper.instance;
#override
void initState() {
super.initState();
_refresh();
}
late List<Future<PatientLog>> fromDatabase;
Future<dynamic> _refresh() async {
await _helper.getForms().then((value) async{
setState(() {
fromDatabase = value;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: _helper.getForms(),
builder: (BuildContext context,
AsyncSnapshot snapshot) {
if (snapshot.hasData && snapshot.data!.isEmpty) {
return Center(
child: Text(
"Henüz kaydedilmiş taslak bulunmamaktadır.",
textAlign: TextAlign.center,
style: TEXT_STYLE,
));
}
if (snapshot.hasError) {
return Center(
child: Text(
'Sanırım bir şeyler ters gitti.',
style: TEXT_STYLE,
));
}
if (snapshot.connectionState == ConnectionState.done) {
return RefreshIndicator(
backgroundColor: Colors.grey[700],
color: LIGHT_BUTTON_COLOR,
onRefresh: _refresh,
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: ListView.builder(
shrinkWrap: true,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics(),
),
itemCount: snapshot.data!.length,
itemBuilder: (BuildContext context, int index) {
return FutureBuilder(
future: snapshot.data,
builder: (context,innerSnap) {
return Text(innerSnap.toString());/*CustomListTile(
formData: innerSnap.data[index],
index: index,
routeTo: 1,
isDeletable: true,
);*/
}
);
},
),
),
);
}
return const Center(
child: Text("Nothing")//spinkit,
);
}),
);
}
}
In my main.dart I have an async function to get data from an URL.
getShopLength() async {
final queryParameters = {
'api_key': '123',
'user_id': '123',
'lat': '123',
'long': '123',
'km': '123',
};
var response = await http.get(Uri.https('google.de','getSth', queryParameters));
var jsonData = jsonDecode(response.body);
List<Shops> shops = [];
for(var x in jsonData) {
Shops shop = Shops(x['name'], x['slogan']);
shops.add(shop);
}
return shops.length;
}
In my home.dart I want to get the value from getShopLength() but I always get the error: type 'Future<dynamic> is not a subtype of type 'Future<String>?'
I try to save the return value into valueShop and pass it to buildRestaurantRow('Top Angebote', context, valueShop)
home.dart
#override
Widget build(BuildContext context) {
var valueShop = "0";
FutureBuilder<String>(
future: getShopLength(),
builder: (context, snapshot) {
if (snapshot.hasData) {
valueShop = snapshot.data;
}
return CircularProgressIndicator();
}
);
return Scaffold(
appBar: buildSearchBar(context),
body: Padding(
padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0),
child: ListView(
children: <Widget>[
SizedBox(height: 20.0),
buildRestaurantRow('Top Angebote', context, valueShop),
SizedBox(height: 10.0),
buildRestaurantList(context),
SizedBox(height: 10.0),
buildCategoryRow('Nach Kategorie', context),
SizedBox(height: 10.0),
buildCategoryList(context),
SizedBox(height: 20.0),
buildCategoryRow('Deine Favoriten', context),
SizedBox(height: 10.0),
buildFriendsList(),
SizedBox(height: 30.0),
],
),
),
);
}
What am I missing?
So the problem lies here:
FutureBuilder<String>(
future: getShopLength(),
Your future builder has a type of string, which means that the future should be of type Future<String>, but when you declared the function getShopLength, you did this:
getShopLength() async {
You did not give it a return type, because of that, the default return type is Future<dynamic>.
The obvious solution is giving the function a return type, but you have another problem:
The futurebuilder expects a string value, but the function returns a number, so which is it?
If you want to return a string of the length, you can just do this:
Future<String> getShopLength() async {
...
return shops.length.toString();
}
Or you can also change the futurebuilder's value to be int:
Future<int> getShopLength() async {
...
return shops.length;
}
...
int valueShop = 0;
FutureBuilder<int>(
future: getShopLength(),
builder: (context, snapshot) {
if (snapshot.hasData) {
valueShop = snapshot.data;
}
return CircularProgressIndicator();
},
);
Side note:
Ok, I have a couple of things to mention about your code:
First of all, on your getShopsLength function, you have two lists, jsonData and shops, you don't actually need both, you can just use one:
var jsonData = jsonDecode(response.body);
return jsonData.length // no need for the shops list.
Second of all, what's up with your builder code?? You first declare a FutureBuilder, but then completely ignore it and move on to a Scaffold widget? I believe the scaffold code should be inside the future builder, as it stands, you will never see the circular progress indicator:
From:
var valueShop = '0';
FutureBuilder<String>(
future: getShopLength(),
builder: (context, snapshot) {
if (snapshot.hasData) {
valueShop = snapshot.data;
}
return CircularProgressIndicator();
}
);
return Scaffold(...);
To:
return FutureBuilder<String>(
future: getShopLength(),
builder: (context, snapshot) {
if (snapshot.hasData) {
var valueShop = snapshot.data;
return Scaffold(...);
}
return CircularProgressIndicator();
}
);
I've been looking for a solution to sort a list (ascending and descending) On Button Press inside of a FutureBuilder, that is a Future<List>, but can't seem to understand how to define it as a List and then sort it on a button press. So I call the API, the API returns some dummy value, it's gets built in the Future Builder and in a ListView.builder, now I want sort the list by id (or by any type for that matter) but the method is not working because the list is null. The code:
API Call for the dummy data:
Future<List<Post>> fetchPosts() async {
List<Post> posts = [];
final response = await http.get('https://jsonplaceholder.typicode.com/posts');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
var postsJson = jsonDecode(response.body);
for (int i = 0; i < postsJson.length; i++) {
posts.add(Post.fromJson(jsonDecode(response.body)[i]));
}
return posts;
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load posts');
}
}
The Future Builder:
List<Post> posts = []; /// if a define it like this, the value is always null
Future<List<Post>> futurePosts;
#override
void initState() {
super.initState();
futurePosts = fetchPosts();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
MaterialButton(color: Colors.grey, onPressed: (){
// here I am setting set to compare the values of all IDs so it can be sorted ascending and descending by number of ID every time I press the button
setState(() {
posts.sort((a, b) => a.id.compareTo(b.id));
});
},),
Container(
height: 1000,
child: FutureBuilder<List<Post>>(
future: futurePosts,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Text('${snapshot.data[index].id}')
},
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return Container();
},
),
But it seems my understanding and code is not working for me at this point. Any help is appreciated, thanks in advance!
You can move your posts.sort((a, b) => a.id.compareTo(b.id)); inside your Future function, before returning posts. And change the setState, to change the state of a boolean, which sorts or not.
You can change like this:
//define a boolen
bool _isSorted =false;
Future<List<Post>> fetchPosts(bool sortORnot) async {
List<Post> posts = [];
final response = await http.get('https://jsonplaceholder.typicode.com/posts');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
var postsJson = jsonDecode(response.body);
for (int i = 0; i < postsJson.length; i++) {
posts.add(Post.fromJson(jsonDecode(response.body)[i]));
}
if (sortORnot) {posts.sort((a, b) => a.id.compareTo(b.id));}// this will sort only if you wanted your list sorted.
return posts;
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load posts');
}
}
Change your FutureBuilder to this:
FutureBuilder<List<Post>>(
future:_isSorted? fetchPosts(true):fetchPosts(false),
builder: (context, snapshot) {
and setState to this:
setState(() {
_isSorted = !_isSorted; //this flips the value whenever you press it.
});
Now, in your future builder, you should get the posts sorted, can you try this?
Something like this, I think, should work:
List<Post> posts;
#override
void initState() {
super.initState();
fetchPosts().then((items) {
setState(() {
posts = items;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(children: [
MaterialButton(
color: Colors.grey,
onPressed: () {
setState(() {
if (posts != null) {
posts = posts.toList();
posts.sort((a, b) => a.id.compareTo(b.id));
}
});
},
),
Container(
height: 1000,
child: (posts != null)
? ListView.builder(
shrinkWrap: true,
itemCount: posts.length,
itemBuilder: (context, index) {
return Text('${posts[index].id}');
},
)
: Container(),
)
]),
),
),
);
}
Your posts field is always empty because you never assign data to that field. And this is the main problem. Try it out.