Great applications should provide useful information to their users. Therefore, displaying data from other source (server) is necessary for most application. Fortunately, Flutter gives us convenient tools out of the box to do such thing.

In Dart language, we can use a package called http. It’s a package that contains useful functions to send and retrieve data from HTTP server.

To install http package, add the following package to your pubspec.yaml file on your Flutter project.

pubspec.yaml
# ...

dependencies:
  http: <latest_version> # add this

In this tutorial, we will fetch some data from JSONPlaceholder. It's an open-source REST API that contains useful fake resources like blog posts, comments, photos, users, etc.

First, we can create a function that returns the response of our HTTP request. This function is asynchronous, so it should return a Future. Then, we need to check wether the response is successful or not by checking the HTTP status code. Finally, we decode our response body to with jsonDecode function and return it.

We also want to use the http package which we installed earlier.

import 'package:http/http.dart' as http;

Future<List<dynamic>> fetchPost() async {
  final response = await http.get('https://jsonplaceholder.typicode.com/posts/1');

  if (response.statusCode == 200) {
    List<dynamic> post = jsonDecode(response.body);
    return post;
  } else {
    throw Exception('Failed to load post');
  }
}

Now, look back to your Flutter widget where you want to make a request to the server. If your widget is now stateless, change it to stateful. Because, we want to store the response data to display it in our widget.

import 'package:flutter/material.dart';

class PostDetail extends StatefulWidget {
  
  _PostDetailState createState() => _PostDetailState();
}

class _PostDetailState extends State<PostDetail> {
  
  Widget build(BuildContext context) {
    // Your widgets
  }
}

Create an initState method in your widget. This method will be invoked whenever your flutter widget is ready to be displayed on the screen. From there, we will fetch our data and save the response to the widget state.

// ...

class _PostDetailState extends State<PostDetail> {
  Future<List<dynamic>> _post;

  
  void initState() {
    super.initState();

    _post = fetchPost();
  }

  
  Widget build(BuildContext context) {
    // Your widgets
  }
}

From there, we invoke our fetchPost function and store the response to the post widget state.

Remember, this data is asynchronous. So we can't just put the post state inside our build method directly.

One way to display asynchronous data is by using a widget called FutureBuilder. It's a widget provided by Flutter inside the Flutter Material Library. This widget makes us very easy to work with asynchronous data. It let us determine what to display when the data is loaded.

When using FutureBuilder widget, we need to define our Future, obviously, and a function that returns a widget.

// ...

class _PostDetailState extends State<PostDetail> {
  Future<List<dynamic>> _post;

  
  void initState() {
    super.initState();

    _post = fetchPost();
  }

  
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: _post,
      builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
        if (snapshot.hasData) {
          // Data fetched successfully, display your data here
          return Column(
            children: <Widget>[
              Text(snapshot.data['title']),
              Text(snapshot.data['content'])
            ],
          );
        } else if (snapshot.hasError) {
          // If something went wrong
          return Text('Something went wrong...');
        }

        // While fetching, show a loading spinner.
        return CircularProgressIndicator();
      }
    )
  }
}

The builder function gets two parameters. The BuildContext, and an AsyncSnapshot. The AsyncSnaphot object, which we defined as snapshot, has the hasData and hasError properties, which very useful to check if there is an error. Finally, while the request is still going on, we can display a loading spinner.