Flutter HTTP: How to Fetch Data from the Internet
•7 min read
<div style="text-align: center;">
<img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjIwMCIgdmlld0JveD0iMCAwIDMwMCAyMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPCEtLSBIVFRQIFJlcXVlc3QgRXhhbXBsZSAtLT4KICA8cmVjdCB3aWR0aD0iMzAwIiBoZWlnaHQ9IjIwMCIgZmlsbD0iI0ZGRiIgc3Ryb2tlPSIjMDAwIi8+CiAgPHRleHQgeD0iMTUwIiB5PSIxMDAiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxMiIgZmlsbD0iIzIxMjEyMSIgdGV4dC1hbmNob3I9Im1pZGRsZSI+SFRUUCBSZXF1ZXN0PC90ZXh0Pgo8L3N2Zz4=" alt="HTTP Request Example" width="300" />
</div>
This comprehensive guide will walk you through fetching and handling data from the internet in Flutter applications. Learn how to make HTTP requests, parse responses, and implement best practices for data handling.
Getting Started
1. Add Dependencies
dependencies: http: ^0.13.4 dio: ^4.0.0
2. Basic HTTP Request
import 'package:http/http.dart' as http; Future<void> fetchData() async { final response = await http.get(Uri.parse('https://api.example.com/data')); if (response.statusCode == 200) { // Parse the response print(response.body); } else { throw Exception('Failed to load data'); } }
HTTP Methods
1. GET Request
Future<Map<String, dynamic>> getData() async { final response = await http.get( Uri.parse('https://api.example.com/data'), headers: {'Authorization': 'Bearer token'}, ); if (response.statusCode == 200) { return json.decode(response.body); } throw Exception('Failed to load data'); }
2. POST Request
Future<void> postData(Map<String, dynamic> data) async { final response = await http.post( Uri.parse('https://api.example.com/data'), headers: {'Content-Type': 'application/json'}, body: json.encode(data), ); if (response.statusCode == 201) { print('Data posted successfully'); } else { throw Exception('Failed to post data'); } }
3. PUT Request
Future<void> updateData(String id, Map<String, dynamic> data) async { final response = await http.put( Uri.parse('https://api.example.com/data/$id'), headers: {'Content-Type': 'application/json'}, body: json.encode(data), ); if (response.statusCode == 200) { print('Data updated successfully'); } else { throw Exception('Failed to update data'); } }
4. DELETE Request
Future<void> deleteData(String id) async { final response = await http.delete( Uri.parse('https://api.example.com/data/$id'), ); if (response.statusCode == 204) { print('Data deleted successfully'); } else { throw Exception('Failed to delete data'); } }
Data Parsing
1. JSON Parsing
class User { final int id; final String name; final String email; User({required this.id, required this.name, required this.email}); factory User.fromJson(Map<String, dynamic> json) { return User( id: json['id'], name: json['name'], email: json['email'], ); } Map<String, dynamic> toJson() => { 'id': id, 'name': name, 'email': email, }; }
2. List Parsing
List<User> parseUsers(String responseBody) { final parsed = json.decode(responseBody).cast<Map<String, dynamic>>(); return parsed.map<User>((json) => User.fromJson(json)).toList(); }
Error Handling
1. Basic Error Handling
Future<void> fetchData() async { try { final response = await http.get(Uri.parse('https://api.example.com/data')); if (response.statusCode == 200) { // Handle success } else { throw HttpException('Failed to load data: ${response.statusCode}'); } } catch (e) { // Handle error print('Error: $e'); } }
2. Custom Exception
class ApiException implements Exception { final String message; final int statusCode; ApiException(this.message, this.statusCode); @override String toString() => 'ApiException: $message (Status: $statusCode)'; }
State Management
1. Using Provider
class DataProvider extends ChangeNotifier { List<User> _users = []; bool _isLoading = false; String _error = ''; List<User> get users => _users; bool get isLoading => _isLoading; String get error => _error; Future<void> fetchUsers() async { _isLoading = true; notifyListeners(); try { final response = await http.get(Uri.parse('https://api.example.com/users')); if (response.statusCode == 200) { _users = parseUsers(response.body); _error = ''; } else { throw ApiException('Failed to load users', response.statusCode); } } catch (e) { _error = e.toString(); } finally { _isLoading = false; notifyListeners(); } } }
2. Using Bloc
class DataBloc extends Bloc<DataEvent, DataState> { DataBloc() : super(DataInitial()) { on<FetchData>(_onFetchData); } Future<void> _onFetchData(FetchData event, Emitter<DataState> emit) async { emit(DataLoading()); try { final response = await http.get(Uri.parse('https://api.example.com/data')); if (response.statusCode == 200) { emit(DataLoaded(parseData(response.body))); } else { emit(DataError('Failed to load data')); } } catch (e) { emit(DataError(e.toString())); } } }
Best Practices
1. API Client
class ApiClient { final http.Client _client; final String _baseUrl; ApiClient({http.Client? client, required String baseUrl}) : _client = client ?? http.Client(), _baseUrl = baseUrl; Future<Map<String, dynamic>> get(String endpoint) async { final response = await _client.get(Uri.parse('$_baseUrl/$endpoint')); return _handleResponse(response); } Map<String, dynamic> _handleResponse(http.Response response) { if (response.statusCode == 200) { return json.decode(response.body); } throw ApiException('Request failed', response.statusCode); } }
2. Caching
class CachedApiClient { final ApiClient _apiClient; final Map<String, dynamic> _cache = {}; CachedApiClient(this._apiClient); Future<Map<String, dynamic>> get(String endpoint) async { if (_cache.containsKey(endpoint)) { return _cache[endpoint]; } final data = await _apiClient.get(endpoint); _cache[endpoint] = data; return data; } }
3. Retry Logic
Future<T> retry<T>( Future<T> Function() function, { int maxAttempts = 3, Duration delay = const Duration(seconds: 1), }) async { for (var i = 0; i < maxAttempts; i++) { try { return await function(); } catch (e) { if (i == maxAttempts - 1) rethrow; await Future.delayed(delay); } } throw Exception('Max attempts reached'); }
Common Issues and Solutions
1. SSL Certificate
class CustomHttpClient extends http.BaseClient { final http.Client _client = http.Client(); @override Future<http.StreamedResponse> send(http.BaseRequest request) async { // Handle SSL certificate issues return _client.send(request); } }
2. Timeout Handling
Future<void> fetchData() async { try { final response = await http.get( Uri.parse('https://api.example.com/data'), ).timeout( const Duration(seconds: 10), onTimeout: () { throw TimeoutException('Request timed out'); }, ); // Handle response } catch (e) { // Handle error } }
3. Network Connectivity
Future<bool> checkConnectivity() async { try { final result = await InternetAddress.lookup('example.com'); return result.isNotEmpty && result[0].rawAddress.isNotEmpty; } on SocketException catch (_) { return false; } }
Conclusion
Fetching data from the internet in Flutter requires proper implementation of HTTP requests, error handling, and state management. Remember to:
- Use appropriate HTTP methods
- Handle errors gracefully
- Implement proper state management
- Follow best practices
- Test thoroughly
Happy coding!