The http
package in Flutter is a powerful tool that allows developers to make HTTP requests and handle responses seamlessly. Whether you need to fetch data from an API, send data to a server, or handle authentication, the http
package has you covered. This article provides an in-depth look at the http
package, teaching every feature included, and providing numerous examples to help you master HTTP in Flutter.
To get started, add the http
package to your pubspec.yaml
file:
dependencies: flutter: sdk: flutter http: ^0.13.3
Then, import it into your Dart file:
import 'package:http/http.dart' as http;
A GET request is used to retrieve data from a server. Here’s a basic example:
// Function to fetch data from an API using a GET request FuturefetchData() async { // Perform a GET request to the given URL final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1')); // Check if the request was successful if (response.statusCode == 200) { // If successful, print the response body print('Response data: ${response.body}'); } else { // If not successful, print an error message print('Failed to load data'); } }
Explanation:
http.get
: This method sends a GET request to the specified URL.Uri.parse
: Converts a string URL into a Uri
object.response.statusCode
: Checks the status code of the response. A status code of 200 indicates success.response.body
: Contains the body of the response, typically in JSON format.A POST request is used to send data to a server. Here’s an example:
// Function to send data to an API using a POST request FuturesendData() async { // Perform a POST request to the given URL final response = await http.post( Uri.parse('https://jsonplaceholder.typicode.com/posts'), headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: jsonEncode( { 'title': 'Flutter POST Request', 'body': 'This is a POST request example', 'userId': '1', }), ); // Check if the request was successful if (response.statusCode == 201) { // If successful, print the response body print('Response data: ${response.body}'); } else { // If not successful, print an error message print('Failed to send data'); } }
Explanation:
http.post
: This method sends a POST request to the specified URL.headers
: A map of headers to send with the request, specifying that the content type is JSON.body
: The data to send with the request, encoded as a JSON string.jsonEncode
: Converts a Dart object to a JSON string.A PUT request is used to update data on a server. Here’s an example:
// Function to update data on an API using a PUT request FutureupdateData() async { // Perform a PUT request to the given URL final response = await http.put( Uri.parse('https://jsonplaceholder.typicode.com/posts/1'), headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: jsonEncode( { 'title': 'Updated Title', 'body': 'This is the updated body', 'userId': '1', }), ); // Check if the request was successful if (response.statusCode == 200) { // If successful, print the response body print('Response data: ${response.body}'); } else { // If not successful, print an error message print('Failed to update data'); } }
Explanation:
http.put
: This method sends a PUT request to the specified URL.headers
: A map of headers to send with the request, specifying that the content type is JSON.body
: The data to update, encoded as a JSON string.jsonEncode
: Converts a Dart object to a JSON string.A DELETE request is used to delete data from a server. Here’s an example:
// Function to delete data from an API using a DELETE request FuturedeleteData() async { // Perform a DELETE request to the given URL final response = await http.delete( Uri.parse('https://jsonplaceholder.typicode.com/posts/1'), ); // Check if the request was successful if (response.statusCode == 200) { // If successful, print a success message print('Data deleted successfully'); } else { // If not successful, print an error message print('Failed to delete data'); } }
Explanation:
http.delete
: This method sends a DELETE request to the specified URL.response.statusCode
: Checks the status code of the response. A status code of 200 indicates success.Headers can be added to HTTP requests to provide additional information to the server.
// Function to fetch data with custom headers FuturefetchDataWithHeaders() async { // Perform a GET request with custom headers final response = await http.get( Uri.parse('https://jsonplaceholder.typicode.com/posts/1'), headers: { 'Authorization': 'Bearer YOUR_TOKEN', }, ); // Check if the request was successful if (response.statusCode == 200) { // If successful, print the response body print('Response data: ${response.body}'); } else { // If not successful, print an error message print('Failed to load data'); } }
Explanation:
headers
: A map of headers to send with the request. In this example, an Authorization header is added.URL parameters can be added to a GET request for filtering or sorting data.
// Function to fetch data with URL parameters FuturefetchDataWithParams() async { // Perform a GET request with URL parameters final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts?userId=1')); // Check if the request was successful if (response.statusCode == 200) { // If successful, print the response body print('Response data: ${response.body}'); } else { // If not successful, print an error message print('Failed to load data'); } }
Explanation:
?
). In this example, userId=1
is added to filter the posts by user ID.A session manager can help manage the state of the user's session across multiple requests.
// Class to manage session state class SessionManager { Map_headers = {}; // Method to set the authorization token void setToken(String token) { _headers['Authorization'] = 'Bearer $token'; } // Method to perform a GET request with the session headers Future get(String url) async { return await http.get(Uri.parse(url), headers: _headers); } // Method to perform a POST request with the session headers Future post(String url, {Map ? headers, dynamic body}) async { return await http.post(Uri.parse(url), headers: {..._headers, ...?headers}, body: body); } }
Explanation:
_headers
: A private map to store session headers.setToken
: A method to set the authorization token.get
: A method to perform a GET request with the session headers.post
: A method to perform a POST request with the session headers.Token management involves storing, retrieving, and refreshing tokens for authenticated requests.
// Class to manage authentication tokens class TokenManager { String? _token; // Method to set the token void setToken(String token) { _token = token; } // Method to get the token String? getToken() { return _token; } // Method to clear the token void clearToken() { _token = null; } }
Explanation:
_token
: A private variable to store the token.setToken
: A method to set the token.getToken
: A method to retrieve the token.clearToken
: A method to clear the token.Basic authentication requires encoding the username and password in the request header.
// Function to fetch data with Basic Authentication FuturefetchDataWithBasicAuth() async { final username = 'user'; final password = 'password'; final credentials = base64Encode(utf8.encode('$username:$password')); final response = await http.get( Uri.parse('https://example.com/api'), headers: { 'Authorization': 'Basic $credentials', }, ); if (response.statusCode == 200) { print('Response data: ${response.body}'); } else { print('Failed to load data'); } }
Explanation:
base64Encode
: Encodes the username and password in base64 format for Basic Authentication.Authorization
: A header to include the encoded credentials.OAuth 2.0 is a more secure authentication method often used for API requests.
// Function to authenticate using OAuth 2.0 and fetch data FuturefetchDataWithOAuth() async { // Step 1: Obtain an access token final response = await http.post( Uri.parse('https://example.com/oauth/token'), body: { 'grant_type': 'client_credentials', 'client_id': 'YOUR_CLIENT_ID', 'client_secret': 'YOUR_CLIENT_SECRET', }, ); if (response.statusCode == 200) { // Step 2: Extract the access token from the response final token = jsonDecode(response.body)['access_token']; // Step 3: Use the access token to make an authenticated API request final apiResponse = await http.get( Uri.parse('https://example.com/api'), headers: { 'Authorization': 'Bearer $token', }, ); if (apiResponse.statusCode == 200) { print('Response data: ${apiResponse.body}'); } else { print('Failed to load data'); } } else { print('Failed to authenticate'); } }
Explanation:
grant_type
, client_id
, client_secret
: Parameters required for OAuth 2.0 authentication.Bearer
: A header to include the access token.Interceptors can be used to log requests and responses, modify headers, or handle errors globally.
// Class to log HTTP requests and responses class LoggingInterceptor extends http.BaseClient { final http.Client _inner; LoggingInterceptor(this._inner); @override Futuresend(http.BaseRequest request) async { // Log the request method and URL print('Request: ${request.method} ${request.url}'); // Send the request and wait for the response final response = await _inner.send(request); // Log the response status code print('Response: ${response.statusCode}'); return response; } } void main() { // Create a new instance of the LoggingInterceptor final client = LoggingInterceptor(http.Client()); // Make a GET request using the LoggingInterceptor client.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1')).then((response) { print('Response body: ${response.body}'); }); }
Explanation:
LoggingInterceptor
: A custom client that logs the request method, URL, and response status code.send
: An overridden method to send the request and log the details.An advanced interceptor can modify requests and responses, add headers, and handle errors globally.
// Class to intercept and modify HTTP requests and responses class AdvancedInterceptor extends http.BaseClient { final http.Client _client; AdvancedInterceptor(this._client); @override Futuresend(http.BaseRequest request) async { // Add custom headers request.headers['Custom-Header'] = 'CustomValue'; // Log the request print('Sending request: ${request.method} ${request.url}'); // Send the request and get the response final response = await _client.send(request); // Log the response print('Response status: ${response.statusCode}'); // Modify response if necessary if (response.statusCode == 401) { // Handle unauthorized error print('Unauthorized request'); // Retry the request or refresh token logic here } return response; } } void main() { // Create a new instance of the AdvancedInterceptor final client = AdvancedInterceptor(http.Client()); // Make a GET request using the AdvancedInterceptor client.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1')).then((response) { print('Response body: ${response.body}'); }); }
Explanation:
AdvancedInterceptor
: A custom client that adds headers, logs requests and responses, and handles errors.send
: An overridden method to send the request, add headers, log details, and handle errors.// Class to manage API services class ApiService { final String baseUrl; Mapheaders = {}; ApiService({required this.baseUrl}); // Method to set the authorization token void setToken(String token) { headers['Authorization'] = 'Bearer $token'; } // Method to perform a GET request Future getData(String endpoint) async { final url = Uri.parse('$baseUrl/$endpoint'); return await http.get(url, headers: headers); } // Method to perform a POST request Future postData(String endpoint, dynamic data) async { final url = Uri.parse('$baseUrl/$endpoint'); return await http.post( url, headers: { ...headers, 'Content-Type': 'application/json', }, body: jsonEncode(data), ); } // Method to perform a PUT request (update) Future updateData(String endpoint, dynamic data) async { final url = Uri.parse('$baseUrl/$endpoint'); return await http.put( url, headers: { ...headers, 'Content-Type': 'application/json', }, body: jsonEncode(data), ); } // Method to perform a DELETE request Future deleteData(String endpoint) async { final url = Uri.parse('$baseUrl/$endpoint'); return await http.delete(url, headers: headers); } } void main() { // Create a new instance of the ApiService final apiService = ApiService(baseUrl: 'https://jsonplaceholder.typicode.com'); // Example Usage of the ApiService apiService.getData('posts/1').then((response) { print('GET Response: ${response.body}'); }); apiService.postData('posts', { 'title': 'New Post', 'body': 'This is the body of the new post', 'userId': '1', }).then((response) { print('POST Response: ${response.body}'); }); apiService.updateData('posts/1', { 'title': 'Updated Post', 'body': 'This is the updated body of the post', 'userId': '1', }).then((response) { print('PUT Response: ${response.body}'); }); apiService.deleteData('posts/1').then((response) { print('DELETE Response: ${response.statusCode}'); }); }
Explanation:
ApiService
: A class to manage API services, including methods for GET, POST, PUT, and DELETE requests.setToken
: A method to set the authorization token.getData
, postData
, updateData
, deleteData
: Methods to perform CRUD operations.Using the http
package with logging to debug network requests and responses:
// Class to log HTTP requests and responses class LoggingClient extends http.BaseClient { final http.Client _client; LoggingClient(this._client); @override Futuresend(http.BaseRequest request) async { // Log the request method and URL print('Request: ${request.method} ${request.url}'); // Send the request and wait for the response final response = await _client.send(request); // Log the response status code print('Response: ${response.statusCode}'); return response; } } void main() { // Create a new instance of the LoggingClient final client = LoggingClient(http.Client()); // Make a GET request using the LoggingClient client.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1')).then((response) { print('Response body: ${response.body}'); }); }
Explanation:
LoggingClient
: A custom client that logs the request method, URL, and response status code.send
: An overridden method to send the request and log the details.Interceptors are middleware that can be used to inspect, modify, or react to requests and responses.
// Class to intercept and modify HTTP requests and responses class InterceptedClient extends http.BaseClient { final http.Client _client; InterceptedClient(this._client); @override Futuresend(http.BaseRequest request) async { // Modify request headers request.headers['Custom-Header'] = 'CustomValue'; // Send the request and wait for the response final response = await _client.send(request); // Log the response status code and reason phrase print('Response: ${response.statusCode} ${response.reasonPhrase}'); return response; } } void main() { // Create a new instance of the InterceptedClient final client = InterceptedClient(http.Client()); // Make a GET request using the InterceptedClient client.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1')).then((response) { print('Response body: ${response.body}'); }); }
Explanation:
InterceptedClient
: A custom client that modifies the request headers and logs the response details.send
: An overridden method to send the request, modify headers, and log the response.SSL pinning is a security technique used to ensure that the app communicates only with a trusted server. Here's how you can implement SSL pinning in Flutter:
import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:http/io_client.dart'; import 'package:http/http.dart' as http; import 'package:flutter/services.dart' show rootBundle; // Function to create an HTTP client with SSL pinning FuturecreateHttpClient() async { // Load the trusted certificate final sslCert = await rootBundle.load('assets/certificates/your_certificate.pem'); SecurityContext context = SecurityContext(withTrustedRoots: false); context.setTrustedCertificatesBytes(sslCert.buffer.asInt8List()); // Create an HTTP client that uses the security context HttpClient httpClient = HttpClient(context: context); return IOClient(httpClient); } // Function to fetch data with SSL pinning Future fetchDataWithSslPinning() async { final client = await createHttpClient(); final response = await client.get(Uri.parse('https://your-secure-api .com/data')); if (response.statusCode == 200) { print('Response data: ${response.body}'); } else { print('Failed to load data'); } } void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: Text('SSL Pinning Example')), body: Center( child: ElevatedButton( onPressed: fetchDataWithSslPinning, child: Text('Fetch Data with SSL Pinning'), ), ), ), ); } }
Explanation:
createHttpClient
: A function that creates an HTTP client with SSL pinning.rootBundle.load
: Loads the trusted certificate from the assets folder.SecurityContext
: A class that holds the SSL configuration.setTrustedCertificatesBytes
: Sets the trusted certificates in the security context.IOClient
: A client that uses the security context.// Function to sign up a new user FuturesignUp(String email, String password) async { // Perform a POST request to the sign-up endpoint final response = await http.post( Uri.parse('https://example.com/signup'), headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: jsonEncode( { 'email': email, 'password': password, }), ); // Check if the request was successful if (response.statusCode == 201) { print('Sign Up Successful'); } else { print('Failed to sign up'); } }
Explanation:
signUp
: A function to sign up a new user by sending a POST request to the sign-up endpoint.headers
: Specifies that the content type is JSON.body
: Contains the email and password, encoded as a JSON string.// Function to log in a user FuturelogIn(String email, String password) async { // Perform a POST request to the login endpoint final response = await http.post( Uri.parse('https://example.com/login'), headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: jsonEncode( { 'email': email, 'password': password, }), ); // Check if the request was successful if (response.statusCode == 200) { // Extract the token from the response final token = jsonDecode(response.body)['token']; // Store the token print('Login Successful, token: $token'); } else { print('Failed to log in'); } }
Explanation:
logIn
: A function to log in a user by sending a POST request to the login endpoint.headers
: Specifies that the content type is JSON.body
: Contains the email and password, encoded as a JSON string.token
: Extracted from the response and printed.// Class to manage authentication tokens class TokenManager { String? _token; // Method to set the token void setToken(String token) { _token = token; } // Method to get the token String? getToken() { return _token; } // Method to clear the token void clearToken() { _token = null; } } final tokenManager = TokenManager(); // Usage example void main() { // Set the token tokenManager.setToken('your_token_here'); // Get and print the token final token = tokenManager.getToken(); print('Stored token: $token'); // Clear the token tokenManager.clearToken(); }
Explanation:
TokenManager
: A class to manage authentication tokens.setToken
: A method to set the token.getToken
: A method to retrieve the token.clearToken
: A method to clear the token.flutter create http_example
in your terminal.cd http_example
.http
package to your pubspec.yaml
file:dependencies: flutter: sdk: flutter http: ^0.13.3
http
package in your lib/main.dart
file:import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: Text('HTTP Example')), body: Center(child: FetchDataButton()), ), ); } } class FetchDataButton extends StatelessWidget { // Function to fetch data from an API using a GET request FuturefetchData() async { // Perform a GET request to the given URL final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1')); // Check if the request was successful if (response.statusCode == 200) { // If successful, print the response body print('Response data: ${response.body}'); } else { // If not successful, print an error message print('Failed to load data'); } } @override Widget build(BuildContext context) { return ElevatedButton( onPressed: fetchData, child: Text('Fetch Data'), ); } }
Explanation:
main
: The entry point of the Flutter application. It runs the MyApp
widget.MyApp
: A stateless widget that builds the main application UI. It consists of a Scaffold with an AppBar and a centered FetchDataButton
widget.FetchDataButton
: A stateless widget that contains a button to fetch data from an API. The fetchData
function performs a GET request to the specified URL and prints the response data or an error message.To expand this example, let's add the ability to handle POST requests, manage session tokens, and use network interceptors.
Update the FetchDataButton
widget to include a method for sending POST requests:
class FetchDataButton extends StatelessWidget { // Function to fetch data from an API using a GET request FuturefetchData() async { // Perform a GET request to the given URL final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1')); // Check if the request was successful if (response.status Code == 200) { // If successful, print the response body print('Response data: ${response.body}'); } else { // If not successful, print an error message print('Failed to load data'); } } // Function to send data to an API using a POST request Future sendData() async { // Perform a POST request to the given URL final response = await http.post( Uri.parse('https://jsonplaceholder.typicode.com/posts'), headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: jsonEncode( { 'title': 'Flutter POST Request', 'body': 'This is a POST request example', 'userId': '1', }), ); // Check if the request was successful if (response.statusCode == 201) { // If successful, print the response body print('Response data: ${response.body}'); } else { // If not successful, print an error message print('Failed to send data'); } } @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: fetchData, child: Text('Fetch Data'), ), ElevatedButton( onPressed: sendData, child: Text('Send Data'), ), ], ); } }
Explanation:
sendData
: A function to send data to an API using a POST request. It performs a POST request to the specified URL with headers indicating the content type is JSON and a body containing the data to send.Column
: A widget that arranges its children in a vertical sequence. It contains two buttons: one for fetching data and another for sending data.To manage session tokens, let's create a SessionManager
class and update our example to use it:
class SessionManager { Map_headers = {}; // Method to set the authorization token void setToken(String token) { _headers['Authorization'] = 'Bearer $token'; } // Method to perform a GET request with the session headers Future get(String url) async { return await http.get(Uri.parse(url), headers: _headers); } // Method to perform a POST request with the session headers Future post(String url, {Map ? headers, dynamic body}) async { return await http.post(Uri.parse(url), headers: {..._headers, ...?headers}, body: body); } } class FetchDataButton extends StatelessWidget { final SessionManager sessionManager = SessionManager(); FetchDataButton() { // Set a dummy token for demonstration purposes sessionManager.setToken('dummy_token'); } // Function to fetch data using the session manager Future fetchData() async { final response = await sessionManager.get('https://jsonplaceholder.typicode.com/posts/1'); if (response.statusCode == 200) { print('Response data: ${response.body}'); } else { print('Failed to load data'); } } // Function to send data using the session manager Future sendData() async { final response = await sessionManager.post( 'https://jsonplaceholder.typicode.com/posts', headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: jsonEncode( { 'title': 'Flutter POST Request', 'body': 'This is a POST request example', 'userId': '1', }), ); if (response.statusCode == 201) { print('Response data: ${response.body}'); } else { print('Failed to send data'); } } @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: fetchData, child: Text('Fetch Data'), ), ElevatedButton( onPressed: sendData, child: Text('Send Data'), ), ], ); } }
Explanation:
SessionManager
: A class to manage session tokens and perform HTTP requests with the session headers.setToken
: Sets the authorization token in the session headers.get
: Performs a GET request with the session headers.post
: Performs a POST request with the session headers.FetchDataButton
: A stateless widget that uses the SessionManager
to perform GET and POST requests.To add logging capabilities, let's create a LoggingInterceptor
class and integrate it with our example:
class LoggingInterceptor extends http.BaseClient { final http.Client _inner; LoggingInterceptor(this._inner); @override Futuresend(http.BaseRequest request) async { // Log the request method and URL print('Request: ${request.method} ${request.url}'); // Send the request and wait for the response final response = await _inner.send(request); // Log the response status code print('Response: ${response.statusCode}'); return response; } } class FetchDataButton extends StatelessWidget { final http.Client client = LoggingInterceptor(http.Client()); // Function to fetch data using the logging interceptor Future fetchData() async { final response = await client.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1')); if (response.statusCode == 200) { print('Response data: ${response.body}'); } else { print('Failed to load data'); } } // Function to send data using the logging interceptor Future sendData() async { final response = await client.post( Uri.parse('https://jsonplaceholder.typicode.com/posts'), headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: jsonEncode( { 'title': 'Flutter POST Request', 'body': 'This is a POST request example', 'userId': '1', }), ); if (response.statusCode == 201) { print('Response data: ${response.body}'); } else { print('Failed to send data'); } } @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: fetchData, child: Text('Fetch Data'), ), ElevatedButton( onPressed: sendData, child: Text('Send Data'), ), ], ); } }
Explanation:
LoggingInterceptor
: A custom client that logs the request method, URL, and response status code.send
: An overridden method to send the request and log the details.FetchDataButton
: A stateless widget that uses the LoggingInterceptor
to perform GET and POST requests.Multipart requests are useful for uploading files along with form data. Here's how you can handle multipart requests:
// Function to upload a file using a multipart request FutureuploadFile(File file) async { // Create a multipart request var request = http.MultipartRequest('POST', Uri.parse('https://example.com/upload')); // Add the file to the request request.files.add(await http.MultipartFile.fromPath('file', file.path)); // Send the request and get the response var response = await request.send(); // Check if the request was successful if (response.statusCode == 200) { print('File uploaded successfully'); } else { print('Failed to upload file'); } } void main() { // Create a sample file (for demonstration purposes) File sample File = File('path/to/sample.txt'); // Upload the file uploadFile(sampleFile); }
Explanation:
http.MultipartRequest
: Creates a new multipart request.request.files.add
: Adds a file to the request.request.send
: Sends the multipart request and gets the response.File
: Represents a file in Dart. You need to import the dart:io
package to use this class.Sometimes, you might want to retry failed requests. Here's how you can implement a simple retry mechanism:
// Function to perform a GET request with retries FuturefetchDataWithRetries({int retries = 3}) async { int attempt = 0; while (attempt < retries) { // Perform a GET request to the given URL final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1')); // Check if the request was successful if (response.statusCode == 200) { // If successful, print the response body print('Response data: ${response.body}'); return; } else { // If not successful, increment the attempt counter attempt++; print('Failed to load data. Attempt $attempt/$retries'); } // Wait for 2 seconds before retrying await Future.delayed(Duration(seconds: 2)); } } void main() { // Fetch data with retries fetchDataWithRetries(); }
Explanation:
fetchDataWithRetries
: A function to perform a GET request with retries.retries
: The number of retry attempts (default is 3).attempt
: The current attempt number.Future.delayed
: Waits for the specified duration before continuing.Handling timeouts is crucial to ensure your application remains responsive. Here's how you can handle timeouts:
// Function to perform a GET request with a timeout FuturefetchDataWithTimeout() async { try { // Perform a GET request to the given URL with a timeout final response = await http .get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1')) .timeout(Duration(seconds: 5)); // Check if the request was successful if (response.statusCode == 200) { // If successful, print the response body print('Response data: ${response.body}'); } else { // If not successful, print an error message print('Failed to load data'); } } on TimeoutException catch (e) { // Handle timeout print('Request timed out'); } } void main() { // Fetch data with a timeout fetchDataWithTimeout(); }
Explanation:
fetchDataWithTimeout
: A function to perform a GET request with a timeout.timeout
: Specifies the duration to wait before timing out the request.TimeoutException
: An exception that occurs when a timeout is reached.Caching responses can improve performance and reduce server load. Here's a basic example of handling caching:
import 'package:path_provider/path_provider.dart'; import 'dart:io'; // Class to manage caching class CacheManager { final String cacheKey; final Duration cacheDuration; CacheManager(this.cacheKey, this.cacheDuration); // Method to get the cache file Future_getCacheFile() async { final directory = await getTemporaryDirectory(); return File('${directory.path}/$cacheKey.json'); } // Method to get cached data Future getCachedData() async { final cacheFile = await _getCacheFile(); if (await cacheFile.exists()) { final cacheDate = await cacheFile.lastModified(); if (DateTime.now().difference(cacheDate) < cacheDuration) { return await cacheFile.readAsString(); } else { await cacheFile.delete(); } } return null; } // Method to cache data Future cacheData(String data) async { final cacheFile = await _getCacheFile(); await cacheFile.writeAsString(data); } } // Function to fetch data with caching Future fetchDataWithCaching() async { final cacheManager = CacheManager('posts', Duration(minutes: 10)); // Try to get cached data final cachedData = await cacheManager.getCachedData(); if (cachedData != null) { print('Using cached data: $cachedData'); return; } // Perform a GET request to the given URL final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1')); // Check if the request was successful if (response.statusCode == 200) { // If successful, cache the response data and print it await cacheManager.cacheData(response.body); print('Response data: ${response.body}'); } else { // If not successful, print an error message print('Failed to load data'); } } void main() { // Fetch data with caching fetchDataWithCaching(); }
Explanation:
CacheManager
: A class to manage caching of data._getCacheFile
: A method to get the cache file.getCachedData
: A method to get cached data if it exists and is still valid.cacheData
: A method to cache data.fetchDataWithCaching
: A function to fetch data with caching.getTemporaryDirectory
: A function to get the temporary directory for storing the cache file. You need to import the path_provider
package to use this function.To make your code more modular and reusable, you can implement a RESTful API client. Here's a basic implementation:
// Class to manage API services class ApiClient { final String baseUrl; final Mapheaders; ApiClient({required this.baseUrl, required this.headers}); // Method to perform a GET request Future get(String endpoint) async { final url = Uri.parse('$baseUrl/$endpoint'); return await http.get(url, headers: headers); } // Method to perform a POST request Future post(String endpoint, dynamic data) async { final url = Uri.parse('$baseUrl/$endpoint'); return await http.post( url, headers: { ...headers, 'Content-Type': 'application/json', }, body: jsonEncode(data), ); } // Method to perform a PUT request Future put(String endpoint, dynamic data) async { final url = Uri.parse('$baseUrl/$endpoint'); return await http.put( url, headers: { ...headers, 'Content-Type': 'application/json', }, body: jsonEncode(data), ); } // Method to perform a DELETE request Future delete(String endpoint) async { final url = Uri.parse('$baseUrl/$endpoint'); return await http.delete(url, headers: headers); } } void main() { // Create a new instance of the ApiClient final apiClient = ApiClient(baseUrl: 'https://jsonplaceholder.typicode.com', headers: {}); // Example usage of the ApiClient apiClient.get('posts/1').then((response) { print('GET Response: ${response.body}'); }); apiClient.post('posts', { 'title': 'New Post', 'body': 'This is the body of the new post', 'userId': '1', }).then((response) { print('POST Response: ${response.body }'); }); apiClient.put('posts/1', { 'title': 'Updated Post', 'body': 'This is the updated body of the post', 'userId': '1', }).then((response) { print('PUT Response: ${response.body}'); }); apiClient.delete('posts/1').then((response) { print('DELETE Response: ${response.statusCode}'); }); }
Explanation:
ApiClient
: A class to manage API services, including methods for GET, POST, PUT, and DELETE requests.get
: Performs a GET request to the specified endpoint.post
: Performs a POST request to the specified endpoint with the provided data.put
: Performs a PUT request to the specified endpoint with the provided data.delete
: Performs a DELETE request to the specified endpoint.The http
package in Flutter is an essential tool for making HTTP requests and handling responses. From basic GET and POST requests to advanced features like authentication, session management, and interceptors, the http
package provides all the functionality needed for effective network communication. By mastering these concepts and utilizing the provided examples, you can confidently integrate REST APIs and handle network operations in your Flutter applications.