Flutter HTTP APIs and JSON

Real apps pull data from the internet — weather info, product listings, news articles. They do this through APIs (Application Programming Interfaces) using HTTP requests. This topic covers how to fetch data, convert JSON to Dart objects, and display it in the UI.

What Is an API

An API is a service that gives you data when you ask for it. You send a request to a web address (URL), and it returns data in JSON format.

  Your Flutter App           Server (API)
  ─────────────              ────────────
  "Give me user #5"   ──►   Looks up user #5
                      ◄──   Returns JSON data

What Is JSON

JSON (JavaScript Object Notation) is a text format for sending data. It looks like a Dart Map.

{
  "id": 5,
  "name": "Ravi Kumar",
  "email": "ravi@mail.com",
  "city": "Delhi"
}

Step 1 — Add the http Package

  In pubspec.yaml:
  ─────────────────
  dependencies:
    flutter:
      sdk: flutter
    http: ^1.2.0

  Run: flutter pub get

Step 2 — Add Internet Permission (Android)

  In android/app/src/main/AndroidManifest.xml,
  add this line inside <manifest>:
  ────────────────────────────────────────────
  <uses-permission android:name="android.permission.INTERNET" />

Step 3 — Make an HTTP GET Request

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

Future<Map<String, dynamic>> fetchUser(int id) async {
  final url = Uri.parse('https://jsonplaceholder.typicode.com/users/$id');
  final response = await http.get(url);

  if (response.statusCode == 200) {
    // Decode JSON string into a Dart Map
    return jsonDecode(response.body);
  } else {
    throw Exception('Failed to load user. Status: ${response.statusCode}');
  }
}

HTTP Status Codes

  200 → OK — request succeeded
  201 → Created — new resource created
  400 → Bad Request — you sent something wrong
  401 → Unauthorized — login required
  404 → Not Found — resource does not exist
  500 → Server Error — problem on the server side

Step 4 — Create a Dart Model Class

Convert the raw Map into a typed Dart class. This makes the data easier and safer to use.

class User {
  final int id;
  final String name;
  final String email;

  User({required this.id, required this.name, required this.email});

  // Create a User from JSON map
  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'],
      name: json['name'],
      email: json['email'],
    );
  }

  // Convert User back to JSON
  Map<String, dynamic> toJson() {
    return {'id': id, 'name': name, 'email': email};
  }
}

JSON → Model Diagram

  API Response (raw JSON):
  ─────────────────────────────────────────
  {"id": 1, "name": "Leanne", "email": "a@b.com"}
             ↓
         jsonDecode()
             ↓
  Dart Map: {'id': 1, 'name': 'Leanne', ...}
             ↓
       User.fromJson()
             ↓
  Dart Object: User(id:1, name:'Leanne', email:'a@b.com')

Step 5 — Fetch a List of Items

Future<List<User>> fetchAllUsers() async {
  final url = Uri.parse('https://jsonplaceholder.typicode.com/users');
  final response = await http.get(url);

  if (response.statusCode == 200) {
    List<dynamic> jsonList = jsonDecode(response.body);
    return jsonList.map((json) => User.fromJson(json)).toList();
  } else {
    throw Exception('Failed to fetch users');
  }
}

Step 6 — Display in Flutter UI

class UserListScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Users')),
      body: FutureBuilder<List<User>>(
        future: fetchAllUsers(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return Center(child: CircularProgressIndicator());
          }
          if (snapshot.hasError) {
            return Center(child: Text('Error: ${snapshot.error}'));
          }
          final users = snapshot.data!;
          return ListView.builder(
            itemCount: users.length,
            itemBuilder: (context, index) {
              final user = users[index];
              return ListTile(
                leading: CircleAvatar(child: Text('${user.id}')),
                title: Text(user.name),
                subtitle: Text(user.email),
              );
            },
          );
        },
      ),
    );
  }
}

POST Request — Sending Data to API

Future<void> createUser(User user) async {
  final url = Uri.parse('https://jsonplaceholder.typicode.com/users');
  final response = await http.post(
    url,
    headers: {'Content-Type': 'application/json'},
    body: jsonEncode(user.toJson()),
  );

  if (response.statusCode == 201) {
    print('User created successfully');
  } else {
    throw Exception('Failed to create user');
  }
}

HTTP Methods Summary

MethodPurposeExample Use
GETFetch dataLoad product list
POSTSend new dataSubmit a form, create account
PUTReplace existing dataUpdate full profile
PATCHUpdate part of dataChange just the email
DELETERemove dataDelete a post

Best Practices

  • Always check response.statusCode before using the body.
  • Always use a model class instead of raw Maps in your UI code.
  • Show a loading indicator while the request runs.
  • Show a useful error message when the request fails.
  • Store the base API URL in a constant so you only change it in one place.

Leave a Comment

Your email address will not be published. Required fields are marked *