0

After looking at resources including this SO post, I'm still unsure why my data's not loading on initState:

class _MyAppState extends State<MyApp> {
  List<NewsItem> itemList = [];

  void _getNews() async {
    final news = await http.get(Uri.parse(
        'https://newsapi.org/...'));
    if (news.statusCode == 200) {
      itemList.clear();
      var articles = jsonDecode(news.body)['articles'];

      for (int i = 0; i < articles.length; i++) {
        var newsItem = articles[i];
        var item = NewsItem.fromJson(newsItem);
        itemList.add(item);
      }
    } else {
      throw Exception('Failed to retrieve news.');
    }
  }

  Future<void> _getData() async {
    setState(() {
      _getNews();
    });
  }

  @override
  void initState() {
    super.initState();
    _getNews();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'NewsAPI.org Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(title: const Text("News App")),
        body: RefreshIndicator(
          onRefresh: _getData,
          child: ListView.builder(
            itemCount: itemList.length,
            itemBuilder: (context, i) {
              return GestureDetector(
                onTap: () {
                  if (itemList[i].url != null) {
                    var _url = itemList[i].url!;
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                          builder: (context) => DisplayWebPage(_url)),
                    );
                  }
                },
                child: Card(
                  elevation: 10,
                  shadowColor: Colors.black,
                  child: Padding(
                    padding: const EdgeInsets.all(10),
                    child: Column(
                      children: [
                        Image.network(itemList[i].urlToImage ?? ''),
                        Text(itemList[i].title ?? '',
                            style: const TextStyle(
                                fontWeight: FontWeight.bold, fontSize: 14)),
                        Text(itemList[i].description ?? ''),
                        const Divider(
                          color: Colors.black,
                          height: 25,
                          thickness: 2,
                        ),
                      ],
                    ),
                  ),
                ),
              );
            },
          ),
        ),
      ),
    );
  }
}

When the app loads, the screen is blank. If I pull to refresh, i.e., call _getData(), then the expected web data loads. I didn't think calling setState() during initialization would matter, but I tried calling _getNews() at startup, too. (Didn't matter.)

What should the code be so that the web data is loaded when the app starts?

Al C
  • 5,175
  • 6
  • 44
  • 74
  • 1
    Your code doesn't work because there's nothing that updates your widget tree when your asynchronous method completes. The proper thing to do is to use a `FutureBuilder` so that your widget tree is rebuilt when your asynchronous method completes. You also will need to make `_getNews` return a proper `Future` so that callers can be notified of its completion. Also see: [What is a Future and how do I use it?](https://stackoverflow.com/q/63017280/) – jamesdlin Oct 05 '21 at 22:11
  • Because it's async and contains other async calls you can't guarantee _getData() will complete before build() is called. You need a mechanism to update your your UI once it completes. FutureBuilder like the other commentor mentions is one way. If you're going to be calling _getData() from your UI (like you mention pull down), use a setState() at the end of _getData() – Pat9RB Oct 05 '21 at 23:45
  • Although I have been learning about `FutureBuilder` after @jamesdlin responded, but (for the sake of learning) wasn't I calling setState() at the end of _getData() when I tried _getNews()? – Al C Oct 05 '21 at 23:59
  • 1
    You made `setState` call `getNews`, but the point is that you must wait for `getNews` to *run to completion*. Your call to `setState` instead calls `getNews` and immediately returns. – jamesdlin Oct 06 '21 at 00:48

0 Answers0