I'm getting the data from an api as a string and converting it to a list. Then I want to create a model and convert the list to be a list of objects. But every time it returns an empty list, what could be the reason? json api is like this =
[
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
{
"userId": 1,
"id": 2,
"title": "qui est esse",
"body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
}
]
PostModel=
class Post {
int? userId;
int? id;
String? title;
String? body;
Post({
this.userId,
this.id,
this.title,
this.body,
});
factory Post.fromJson(List json, int index) {
return Post(
userId: json[index]['userId'],
id: json[index]['id'],
title: json[index]['title'],
body: json[index]['body'],
);
}
}
PostService=
import 'dart:convert';
import '../models/post_model.dart';
import 'package:http/http.dart' as http;
class PostService {
final url = Uri.parse('https://jsonplaceholder.typicode.com/posts');
Future<List<Post?>?> callPost() async {
var response = await http.get(url);
if (response.statusCode == 200) {
List myList = jsonDecode(response.body);
List<Post> _allPosts =
List.generate(myList.length, (index) => Post.fromJson(myList, index));
return _allPosts;
} else {
print('error code: ${response.statusCode}');
}
}
}
class HomeScreen extends StatefulWidget {
HomeScreen({Key? key}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
PostService _postService = PostService();
List<Post?> myPostList = [];
@override
void initState() {
_personelService.callPost().then((value) {
if (value != null) {
setState(() {
myPostList = value;
});
} else {
setState(() {});
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child: Column(
children: [
Text('${myPostList[0]!.body}'),
],
),
),
),
);
}
}
What I expect to return here is a list of objects, but the list returns empty. Can anyone explain why?
CodePudding user response:
I copy pasted your code and there is nothing wrong with the API fetching.
Your code in the widget is broken tho': You're calling [0] on a list which is initialized empty, I would suggest the following approach:
Mapping the list into another list to have cleaner code:
class PostService {
final url = Uri.parse('https://jsonplaceholder.typicode.com/posts');
Future<List<Post?>?> callPost() async {
var response = await http.get(url);
if (response.statusCode == 200) {
List myList = jsonDecode(response.body);
return myList.map((e) => Post.fromJson(e)).toList();
} else {
print('error code: ${response.statusCode}');
}
}
}
Using a future builder to control the state:
class HomeScreen extends StatefulWidget {
HomeScreen({Key? key}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final _postService = PostService();
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: FutureBuilder<List<Post?>?>(
future: _postService.callPost(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.data!.isEmpty) {
return const Center(
child: Text('No posts'),
);
}
return SingleChildScrollView(
child: Column(
children: snapshot.data!.map((e) => Text(e?.title ?? 'No title')).toList(),
),
);
}),
),
);
}
}
Mapping from a json map instead of passing a list index:
class Post {
int? userId;
int? id;
String? title;
String? body;
Post({
this.userId,
this.id,
this.title,
this.body,
});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
userId: json['userId'],
id: json['id'],
title: json['title'],
body: json['body'],
);
}
}
CodePudding user response:
Instead of call async in initState, use FutureBuilder, like this:
class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: FutureBuilder<List<Post?>?>(
future: PostService().callPost(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List<Post?> data = snapshot.data ?? [];
return ListView.builder(
itemBuilder: (context, index) {
return Column(children: [
Text(data[index]?.title ?? ''),
]);
},
itemCount: data.length,
);
}
}
}),
));
}
}