Flutter Networking: Fetching Data from REST API with http | (Ch. 22)

Flutter Course Chapter 22 cover image illustrating Networking. The title reads: "The http package and fetching data from a REST API". A diagram shows a smartphone exchanging packets labeled "JSON" and "DATA" with a cloud server labeled "REST API SERVER" via arrows.

Welcome to the most exciting chapter in your Flutter journey so far! Until now, your apps have been "islands." They could only talk to themselves. They didn't know what time it was in London, what the stock price of Google was, or what your friends were posting on social media.

In this chapter, we build a bridge. We are going to learn Networking. By the end of this post, your app will be able to reach out across the internet, talk to a server, and pull back live data.

1. What is a REST API?

Imagine you go to a restaurant. You are the Client (The App). You want food, but you can't go into the kitchen yourself. The Kitchen is the Server (Database).

To get your food, you talk to the Waiter. The waiter takes your request, tells the kitchen, and brings back your food. In the world of apps, the Waiter is the API (Application Programming Interface).

REST (Representational State Transfer) is simply a set of rules for how the waiter and the client should talk. It usually involves these "verbs":

  • GET: "Give me data" (Reading a post).
  • POST: "Here is new data" (Creating a post).
  • PUT: "Update this data" (Editing a post).
  • DELETE: "Remove this data" (Deleting a post).

2. The JSON Language

Servers don't send back Flutter Widgets. They send back JSON (JavaScript Object Notation). It's a lightweight text format that looks almost exactly like a Dart Map.


{
  "id": 1,
  "title": "Learn Flutter Networking",
  "author": "SRF Developer",
  "isPublished": true
}

Our job is to take that text string and turn it into a Dart Object we can use in our UI.

3. Setup: The `http` Package

Flutter doesn't include networking by default to keep the app size small. We need the official http package from the Dart team.

1. Add Dependency

Open pubspec.yaml and add:


dependencies:
  http: ^1.1.0

2. Internet Permissions

Android: Open android/app/src/main/AndroidManifest.xml and add this line above the <application> tag:


<uses-permission android:name="android.permission.INTERNET" />

iOS: Usually works by default, but for macOS or specific setups, you may need to enable "App Sandbox" in Xcode.

4. Step-by-Step: Fetching Data

Let's use a free testing API called JSONPlaceholder. We will fetch a list of "Posts."

The Basic GET Request


import 'package:http/http.dart' as http;
import 'dart:convert'; // Required for json.decode()

Future<void> fetchPosts() async {
  // 1. Create the URL
  final url = Uri.parse('https://jsonplaceholder.typicode.com/posts/1');

  // 2. Make the request
  final response = await http.get(url);

  // 3. Check if it worked (Status 200 means OK)
  if (response.statusCode == 200) {
    // 4. Convert the String body into a Map
    final Map<String, dynamic> data = json.decode(response.body);
    print('Post Title: ${data['title']}');
  } else {
    print('Failed to load data');
  }
}

5. Making it Professional: Data Models

In the code above, we accessed data like this: data['title']. This is dangerous. If you misspell "title" as "tite", the app won't crash until you run it, and you'll get a null error.

Professional developers create Model Classes. This turns the Map into a real Dart object with properties.


class Post {
  final int id;
  final String title;
  final String body;

  Post({required this.id, required this.title, required this.body});

  // A "factory" constructor to create a Post from JSON
  factory Post.fromJson(Map<String, dynamic> json) {
    return Post(
      id: json['id'],
      title: json['title'],
      body: json['body'],
    );
  }
}
Pro Tip: Automatic Models

Don't write models manually for large APIs! Use a tool like QuickType.io. Paste your JSON, select "Dart," and it will generate the classes for you.

6. Handling Status Codes & Errors

The internet is unreliable. You must handle different response codes:

Code Meaning What to do?
200 Success Show the data.
201 Created Success (usually for POST).
404 Not Found Tell user the resource is gone.
500 Server Error Tell user the server is down.

7. Displaying API Data in the UI

Now, let's put it all together into a real app using FutureBuilder.


import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

// --- MODEL ---
class Post {
  final String title;
  final String body;
  Post({required this.title, required this.body});

  factory Post.fromJson(Map<String, dynamic> json) {
    return Post(title: json['title'], body: json['body']);
  }
}

// --- APP ---
void main() => runApp(MaterialApp(home: PostScreen()));

class PostScreen extends StatelessWidget {
  
  // The Networking Function
  Future<Post> getSinglePost() async {
    final response = await http.get(
      Uri.parse('https://jsonplaceholder.typicode.com/posts/1')
    );

    if (response.statusCode == 200) {
      return Post.fromJson(json.decode(response.body));
    } else {
      throw Exception('Failed to load post');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("API Fetch Demo")),
      body: Center(
        child: FutureBuilder<Post>(
          future: getSinglePost(),
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return CircularProgressIndicator();
            } else if (snapshot.hasError) {
              return Text("Error: ${snapshot.error}");
            } else if (snapshot.hasData) {
              return Padding(
                padding: const EdgeInsets.all(20.0),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text(snapshot.data!.title, 
                      style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
                    SizedBox(height: 10),
                    Text(snapshot.data!.body),
                  ],
                ),
              );
            }
            return Text("No data found");
          },
        ),
      ),
    );
  }
}

Networking FAQ

Q: Should I use 'http' or 'dio'?
http is lightweight and perfect for beginners. dio is a 3rd party package that adds features like Global Interceptors, File Downloading progress, and automatic retries. For most apps, http is enough.
Q: Why do I get "XMLHttpRequest error" on Web?
This is a CORS issue. It happens when the server doesn't allow requests from a web browser. It's a security feature of browsers, not a Flutter bug. You can fix it by configuring the server or using a proxy during development.
Q: How do I send data (POST)?
Use http.post. You must pass a body and usually a header to tell the server you are sending JSON.
headers: {"Content-Type": "application/json"}

Conclusion

You have just turned your Flutter app into a global communicator. You can now build News apps, Weather apps, and Social Media clones.

In the next chapter, we will build something even more powerful. We will learn how to save data offline so that even when the user has no internet, their data stays safe on the device. Get ready for Shared Preferences!

Comments