Building a simple news aggregator app with Flutter

What the final app will look like.

Powered by Dart, Flutter is:

Google’s mobile UI framework for crafting high-quality native interfaces on iOS and Android in record time.

I am going to walk us through the steps to building a news aggregator app with Flutter framework.

Prerequisites

I am assuming you have already installed Flutter on your development machine and built a test Flutter project. However, if you haven’t done so, please follow any of these guilds for Linux, Mac, or Windows machines.

Now create a fresh Flutter Project and name it news_aggreator.

For the purpose of this tutorial, we’ll be using the New York Times Top Stories API. Please get your API key here (select “Top Stories” from the API drop-down).

Let’s start.

Setting up Dart

Delete all the code from lib/main.dart and replace it with the following:

The lines import ‘package:news_aggreator/home_page.dart’ and new MyHomePage(title: “News Aggregator”) will show errors. That’s because we haven't created ahome_page.dart file and MyHomePage class. We’ll do that next.

Create a new dart file by navigating to File > New > Dart File. Name it home_page.

In this new file, we’ll create a MyHomePage class that extends StatefulWidget.

A Stateful widget supports state changes and user interactions…,they maintain state that might change during the lifetime of the widget. Stateless widgets are immutable, meaning that their properties can’t change — all values are final.

— Official Docs

Import package:flutter/material.dart and override createState() and return a State Widget. The final code should look like this:

Now we’ll create a _MyHomePageState class in the same home_page file. Note the “_” (underscore) before the class name? That’s Dart’s way of making an identifier private in its library. _MyHomePageState should extend State<MyHomePage> and override Widget build(BuildContext context) {}. Make these changes to it:

That’s OK for now. We’ll return to it later.

Making API calls with Flutter

Now, we’re gonna make a network call to the New York Times API to get the top stories. The response looks like this:

{
...
"results": [
{
...
"title": "This is the article Title is This",
"abstract": "A summary of what this article is talking about",
"url": "https://www.THE_LINK_TO_THE_ARTICLE.html",
...
"created_date": "2018-03-30T05:00:08-04:00",
...
"multimedia": [
{...},
{...},
{...},
{
"url": "https://www.IMAGE_URL.png",
...
},
{...}
],
...
},
{...}
{...}
{...}
{...}
]
}

Add the following import statements to the top of home_page,

import 'package:http/http.dart' as http; // For network calls
import 'dart:convert'; // Amongst other functions, for decoding and encoding JSON

So, we’ll make the API calls, decode, and parse the response with this:

void _sendRequest() async {
String url = "https://api.nytimes.com/svc/topstories/v2/technology"
".json?api-key=API_KEY_HERE";
http.Response response = await http.get(url);
Map decode = json.decode(response.body);
List results = decode["results"];
for (var jsonObject in results) {
print(jsonObject);
}
}

I will explain the above lines below.

Dart is single-threaded, and so long-lasting functions such as network calls will make your app freeze. This is where the Future API comes in. A future is a way of saying:

“I want to get data from the NY Times API, but I know it might take some time, but please don’t block the thread of execution. And while you are at it, you can return an uncompleted object. Emmm… Can you also let me know when the response is ready?”

So how do we know when the response is ready? We either use callbacks or await for the request to return. We won’t be using callbacks in this tutorial but rather the latter — aync and await keywords.

http.Response response = await http.get(url); makes the actual network call.

On the surface, it looks like synchronous code but it is actually asynchronous. Read more about futures here.

Map decode = json.decode(response.body); decodes the response body and assigns it to key-value pairs.

List results = decode[‘results’]; gets the JSON array with key “results” while

for (var jsonObject in results) {
print(jsonObject);
}

loops through the JSON array and prints each of the JSON objects to the console.

Parsing JSON responses in Flutter

The jsonObjectholds a reference to each of the JSON objects in the results JSON array, and it looks like this:

{
...
"title": "The Article Title is This",
"abstract": "A summary of what this article is talking about",
"url": "https://www.THE_LINK_TO_THE_ARTICLE.html",
...
"created_date": "2018-03-30T05:00:08-04:00",
...
"multimedia": [
{...},
{...},
{...},
{
"url": "https://www.IMAGE_URL.png",
...
},
{...}
],
...
},

So we’re gonna parse the response and create a Post object with it. For example, to get the title above, we’re gonna do jsonObject[‘title’]. Yes, it’s that simple.

Create a Post class directly under _MyHomePageState class. The whole Post class should look like this:

Now, create a List<Post> postList = [] field before the build method of _MyHomePageState class. Also, create a _isRequestSent boolean field (it should default to false. ). This will hold the value of the request status.

Now, we have this:

for (var jsonObject in results) {
var post = Post.getPostFrmJSONPost(jsonObject);
postList.add(post);
print(post);

}
setState(() => _isRequestSent = true);

The setState is a way of telling Flutter to rebuild the Widgets and redraw the UI. So you should arrive at this:

The only new line here is the new ListView.builder. It is synonymous with Android’s RecyclerView or ListView. It calls itemBuilder: (BuildContext context, int index)as many times as the value assigned to itemCount. Note that a Widget must be returned in itemBuilder .

Next, we’ll return a proper Widget in _getPostWidgets.

Now, we’re almost done. The remaining part is the details page. There’s nothing special going on here, so I won’t dwell much on it. Create a dart file, name it post_details, and write the following:

Note the usage of the intl package for date parsing. The installation instruction can be found here. Another line worthy of mention here is:

final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();

Generally, Global Keys are used to uniquely identify widgets. You can dive into it in-depth here: GlobalKey.

Now update openDetailsUIwith:

openDetailsUI(Post post) {
Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) => new PostDetails(post)));
}

Remember to add this import statement import ‘package:news_aggreator/post_details.dart’;to the imports section of home_page.dart.

I guess that’s that!

In order to reduce the length of this article, I have skipped some stuff like error handling for network calls, network image caching and placeholders, and so on.

You can check the complete and working GitHub repo here:

Please feel very free to point out errors or make any corrections and suggestions.

--

--