Flutter API Integration Explained: HTTP Requests, FutureBuilder & Error Handling Guide
Learn Flutter API integration step by step. Understand HTTP requests, JSON parsing, FutureBuilder, loading states, error handling, and real-world best practices.
Introduction
Modern mobile applications rely heavily on APIs. Whether it is login authentication, product listing, user profiles, or dashboard data — everything usually comes from a backend server.
In Flutter, API integration is simple but requires understanding asynchronous programming, HTTP requests, and proper error handling.
In this complete guide, we will cover:
- How HTTP works in Flutter
- Making GET and POST requests
- Parsing JSON data
- Using FutureBuilder
- Handling loading and error states
- Best practices for production apps
Adding HTTP Package
To make API calls, we need the http package. Add it inside pubspec.yaml.
dependencies: http: ^0.13.6
Then run flutter pub get.
Making a GET Request
import 'package:http/http.dart' as http; import 'dart:convert'; Future> fetchPosts() async { final response = await http.get( Uri.parse("https://jsonplaceholder.typicode.com/posts"), ); if (response.statusCode == 200) { return jsonDecode(response.body); } else { throw Exception("Failed to load posts"); } }
This function:
- Sends a GET request
- Checks status code
- Parses JSON
- Returns data
Using FutureBuilder
FutureBuilder is used to build UI based on asynchronous data.
FutureBuilder(
future: fetchPosts(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text("Error: ${snapshot.error}");
}
final posts = snapshot.data as List;
return ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(posts[index]['title']),
);
},
);
},
)
Understanding ConnectionState
- waiting: API is loading
- done: Data received
- active: Stream active
- none: No connection
Making a POST Request
FutureloginUser(String email, String password) async { final response = await http.post( Uri.parse("https://example.com/login"), body: { "email": email, "password": password, }, ); if (response.statusCode == 200) { print("Login success"); } else { throw Exception("Login failed"); } }
Creating a Model Class
Instead of using dynamic JSON, create model classes.
class Post {
final int id;
final String title;
Post({required this.id, required this.title});
factory Post.fromJson(Map json) {
return Post(
id: json['id'],
title: json['title'],
);
}
}
Parsing JSON to Model
Future> fetchPosts() async { final response = await http.get( Uri.parse("https://jsonplaceholder.typicode.com/posts"), ); if (response.statusCode == 200) { List data = jsonDecode(response.body); return data.map((e) => Post.fromJson(e)).toList(); } else { throw Exception("Error fetching posts"); } }
Handling Loading State Properly
Always show a loading indicator while fetching data. Never leave the screen blank.
Handling Errors Gracefully
Instead of crashing the app:
- Show user-friendly messages
- Provide retry option
- Log errors for debugging
Common Beginner Mistakes
- Calling API inside build()
- Not handling exceptions
- Ignoring status codes
- Using dynamic everywhere instead of models
Best Practices
- Keep API logic inside separate service files
- Use model classes
- Handle network errors properly
- Use proper state management for large apps
Real-World Architecture Tip
In production apps:
- Create API service class
- Separate repository layer
- Use state management (Provider, Bloc, etc.)
- Avoid calling API directly inside UI widgets
Conclusion
API integration is a core skill for Flutter developers. Once you understand HTTP requests, JSON parsing, FutureBuilder, and proper error handling, you can build real-world applications.
Master this concept, and you are ready to connect your Flutter apps to real backend systems.
Share
What's Your Reaction?
Like
0
Dislike
0
Love
0
Funny
0
Angry
0
Sad
0
Wow
0