Most apps will need to get some sort of data from the Internet. If you think about how much data there is, you would never want to update an app to store all of the data in your app. It would make your app both unnecessarily large and you would be updating it all the time.
Flutter, and Dart, provide a few underlying libraries to help you retrieve data over the Internet. We will be using those in this project.
However we first need to set up a default Flutter project, like we’ve done so many times before. This project I’m going to call internet_data.
Adding an External Package
We’ve added an external package before, when we added Animation to a project. We’re going to do something similar, but we’re adding the HTTP package.
Open the pubspec.yaml file, and add a dependency of http, like you see below.
dependencies:
flutter:
sdk: flutter
http: ^0.13.3
If you are in Visual Studio Code, this will save your file, and download the package into your project.
Now we will need to import
the library into our main.dart file.
import 'package:http/http.dart' as http;
Setting Permissions
There is something we have to do, that we didn’t have to do with the Animations library, and that is modify the project permissions. Some functions require us to ask for permission to do them on another person’s device, because they could be potentially misused. Accessing the Internet is a prime example of this.
I found that VSC added the following line for me under android > app > src > debug and android > app > src > profile in the AndroidManifest.xml file.
<!-- Required to fetch data from the internet. -->
<uses-permission android:name="android.permission.INTERNET" />
Making a Network Request
Making a request to get data from a server takes several steps and sets of information.
First, you will need to have a URL to request data from.
Second, we will have a Future
data object. Future
is built into Dart and designed for async ( asynchronous ) calls. Future data is a piece of data which may not be instantly available, but the data, or an error, will be available in the future.
Asynchronous means that your call, or request for data, is made and returned without the program waiting for it. The idea being we don’t want our application to “lock up” because of a slow server, therefore we will not wait on it, but let the program still be interactive. When the data is available, it will be returned and your app can handle it then.
For this example we are going to be using https://jsonplaceholder.typicode.com/. It is a free online resource testing applications. We’re going to use their posts dataset. They have some others you can use, such as photos, todos (for a todo list), and more.
A sample function is seen below. We’ll use something like this, but this function will allow us to call a remote set of data, and check what is returned.
Future<http.Response> fetchPost() {
return http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));
}
You can go directly to https://jsonplaceholder.typicode.com/posts/1 to see what the data returned will look like. It is listed below:
{
"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"
}
If you don’t put the /1
at the end, it will return a whole list of posts.
If this page format doesn’t look familiar to you, it’s because it isn’t for a regular webpage, but rather is JSON (JavaScript Object Notation) data which can easily be read and parsed by many languages including Dart.
Creating a Custom Dart Class
But before we even worry about a list of posts, we need to take the data we get, and put it into a custom Dart object. To do that, we first need to create a class.
In a large app, this is something I’d put into a separate file and import into our main.dart file, however, for a small demo, we can keep it contained. We’re going to need four pieces of data, one for each element that is listed in the JSON data.
class Post {
final int userId;
final int id;
final String title;
final String body;
Post ({
required this.userId,
required this.id,
required this.title,
required this.body,
});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
userId: json['userId'],
id: json['id'],
title: json['title'],
body: json['body'],
);
}
}
Notice how the fromJson takes data from the JSON file and parses it easily for us to return a Post object.
Apply Response Data to the Post Class
At this time we need to apply the http.Response into the Post data.
We’re going to go out to the server, and depending upon the response code, will determine what’s going to happen. If a success code of 200, that means we got the data and need to parse it, if not, we’ll throw an exception saying we couldn’t load the data.
Future<Post> fetchPost() async {
final response =
await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));
if (response.statusCode == 200) {
// If the server did return a 200 OK response, then parse the JSON.
return Post.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response, then throw an exception.
throw Exception('Failed to load post data');
}
}
Now if we look at this data, we will have an error on the jsonDecode method. We will need to import dart:convert at the top of the file, like you see here.
import 'dart:convert';
Now we have a function that can fetch the data, and a way to store the data, but we need to call it.
Fetching the Data
We need to call the fetchPost()
method in either the initState()
or didChangeDependencies()
methods.
The initState()
method is called exactly once and then never again. If you want to have the option of reloading the API in response to an InheritedWidget
changing, put the call into the didChangeDependencies()
method.
late Future<Post> futurePost;
@override
void initState() {
super.initState();
futurePost = fetchPost();
}
As you can see, for this example, we are using the initState() for simplicity.
If you run this, nothing happens because the data is not being displayed. I temporarily put a print statement in the fetchPost() function if there was a success just to make sure that it was going out to the server and loading the data.
// ...
if (response.statusCode == 200) {
print("success"); // just for testing purposes
// If the server did return a 200 OK response, then parse the JSON.
return Post.fromJson(jsonDecode(response.body));
}
// ....
Now we need to actually display our post data.
Displaying the Post Data
To display the data, we will need to use a built in Flutter Widget called FutureBuilder. It makes it easier to work with asynchronous data like we are currently doing.
The FutureBuilder takes two pieces of data, the Future object, which we have from fetchPost(), and a builder function which tells it how to build the data.
We are going to use the following code to build the widget to be displayed. This goes as the child of the Center object.
FutureBuilder<Post>(
future: futurePost,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: <Widget>[
Text(
snapshot.data!.title,
style: Theme.of(context).textTheme.headlineMedium,
),
Text(snapshot.data!.body),
],
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
} else {
// added because you cannot return a null value, this
// means we always get a returned value
return const Text('Unknown Error');
}
}),
Save and do a hot reload, and you should see the title appear, larger and the text of the body appear smaller and below the title.
Notice how depending upon if the result has data, it will return different results, but both will work as they are widgets to be attached to the child.
The .hasData is a boolean value which only returns true if there was no error, and the data is non-null data.
Making an Authenticated Request
This type of request has been anonymous. Most requests you need to have some sort of authentication. This can be to make sure you have rights to the data, track data usage, or even for billing purposes (to know how much data you access).
One way to add an authentication information is to put it in your header request like you see below.
final response = await http.get(
Uri.parse('https://jsonplaceholder.typicode.com/posts/1'),
// Send authorization headers to the backe nd.
headers: {
HttpHeaders.authorizationHeader: 'your_api_token',
},
);
Your api token would be given to you when you create an account where you are accessing the information.
For the HttpHeaders to work, you will need to import one more library within your project, dart:io
.
import 'dart:io';
Since the data we’re using is publicly facing and doesn’t require authorization, we’re not going to use it in these examples.
Getting Data from the Internet Using Flutter was originally found on Access 2 Learn
3 Comments
Comments are closed.