When building mobile or web applications with Dart—especially using the Flutter framework—making HTTP requests to REST APIs is a routine task. However, handling errors effectively during these operations is essential for creating reliable and user-friendly applications. In this guide, you’ll learn how to manage HTTP errors properly in Dart using the http
package, covering timeout handling, client/server-side error responses, and exception types.
Why Error Handling Matters
Without proper error handling, your app might crash or behave unexpectedly when the API is down, the user has no internet connection, or the server returns a 500 error. Proper error handling ensures your application fails gracefully and provides helpful feedback to the user.
Installing the HTTP Package
Make sure you have the http
package added in your pubspec.yaml
:
dependencies:
http: ^0.13.6
dependencies:
http: ^0.13.6
Making HTTP Requests with Error Handling
Here is a complete example that includes:
- HTTP GET request
- Timeout exception
- Socket exception
- HTTP status code validation
File: api_service.dart
import 'package:http/http.dart' as http;
final String baseUrl = 'https://jsonplaceholder.typicode.com';
Future<dynamic> fetchData(String endpoint) async {
final url = Uri.parse('$baseUrl/$endpoint');
final response = await http.get(url).timeout(const Duration(seconds: 10));
if (response.statusCode >= 200 && response.statusCode < 300) {
return jsonDecode(response.body);
throw HttpException('Server responded with status: ${response.statusCode}');
throw Exception('No Internet connection.');
} on HttpException catch (e) {
throw Exception('HTTP error: ${e.message}');
throw Exception('Bad response format.');
throw Exception('The connection has timed out.');
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
class ApiService {
final String baseUrl = 'https://jsonplaceholder.typicode.com';
Future<dynamic> fetchData(String endpoint) async {
final url = Uri.parse('$baseUrl/$endpoint');
try {
final response = await http.get(url).timeout(const Duration(seconds: 10));
if (response.statusCode >= 200 && response.statusCode < 300) {
return jsonDecode(response.body);
} else {
throw HttpException('Server responded with status: ${response.statusCode}');
}
} on SocketException {
throw Exception('No Internet connection.');
} on HttpException catch (e) {
throw Exception('HTTP error: ${e.message}');
} on FormatException {
throw Exception('Bad response format.');
} on TimeoutException {
throw Exception('The connection has timed out.');
}
}
}
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
class ApiService {
final String baseUrl = 'https://jsonplaceholder.typicode.com';
Future<dynamic> fetchData(String endpoint) async {
final url = Uri.parse('$baseUrl/$endpoint');
try {
final response = await http.get(url).timeout(const Duration(seconds: 10));
if (response.statusCode >= 200 && response.statusCode < 300) {
return jsonDecode(response.body);
} else {
throw HttpException('Server responded with status: ${response.statusCode}');
}
} on SocketException {
throw Exception('No Internet connection.');
} on HttpException catch (e) {
throw Exception('HTTP error: ${e.message}');
} on FormatException {
throw Exception('Bad response format.');
} on TimeoutException {
throw Exception('The connection has timed out.');
}
}
}
File: main.dart
import 'package:flutter/material.dart';
import 'api_service.dart';
class MyApp extends <a href="https://aliendro.id/create-ui-with-statelesswidget-in-dart/">StatelessWidget</a> {
const MyApp({super.key});
Widget build(BuildContext context) {
title: 'HTTP Error Handling Demo',
appBar: AppBar(title: const Text('API Error Handling')),
body: const Center(child: DataDisplay()),
class DataDisplay extends <a href="https://aliendro.id/statefulwidget-vs-statelesswidget-in-dart-guide/">StatefulWidget</a> {
const DataDisplay({super.key});
State<DataDisplay> createState() => _DataDisplayState();
class _DataDisplayState extends State<DataDisplay> {
final ApiService _apiService = ApiService();
String _result = 'Fetching data...';
final data = await _apiService.fetchData('posts/1');
_result = data.toString();
Widget build(BuildContext context) {
padding: const EdgeInsets.all(16.0),
import 'package:flutter/material.dart';
import 'api_service.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends <a href="https://aliendro.id/create-ui-with-statelesswidget-in-dart/">StatelessWidget</a> {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'HTTP Error Handling Demo',
home: Scaffold(
appBar: AppBar(title: const Text('API Error Handling')),
body: const Center(child: DataDisplay()),
),
);
}
}
class DataDisplay extends <a href="https://aliendro.id/statefulwidget-vs-statelesswidget-in-dart-guide/">StatefulWidget</a> {
const DataDisplay({super.key});
@override
State<DataDisplay> createState() => _DataDisplayState();
}
class _DataDisplayState extends State<DataDisplay> {
final ApiService _apiService = ApiService();
String _result = 'Fetching data...';
@override
void initState() {
super.initState();
_loadData();
}
void _loadData() async {
try {
final data = await _apiService.fetchData('posts/1');
setState(() {
_result = data.toString();
});
} catch (e) {
setState(() {
_result = 'Error: $e';
});
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Text(_result),
);
}
}
import 'package:flutter/material.dart';
import 'api_service.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'HTTP Error Handling Demo',
home: Scaffold(
appBar: AppBar(title: const Text('API Error Handling')),
body: const Center(child: DataDisplay()),
),
);
}
}
class DataDisplay extends StatefulWidget {
const DataDisplay({super.key});
@override
State<DataDisplay> createState() => _DataDisplayState();
}
class _DataDisplayState extends State<DataDisplay> {
final ApiService _apiService = ApiService();
String _result = 'Fetching data...';
@override
void initState() {
super.initState();
_loadData();
}
void _loadData() async {
try {
final data = await _apiService.fetchData('posts/1');
setState(() {
_result = data.toString();
});
} catch (e) {
setState(() {
_result = 'Error: $e';
});
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Text(_result),
);
}
}
This code provides a robust and reusable pattern for HTTP requests in Dart, catching various error scenarios cleanly.
To deepen your understanding of HTTP error codes and best practices in API response management, check out this MDN Web Docs guide.
Post Views: 30