Home > other >  flutter- Get Api and convert to list of objects problem
flutter- Get Api and convert to list of objects problem

Time:08-31

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,
                      );
                    }
                }
              }),
        ));
  }
}
  • Related