When building Flutter apps, you often need to manage network requests to fetch or send data through APIs. Debugging these network requests is crucial to identify issues like incorrect endpoints, missing headers, or unexpected responses. Developers commonly use HTTP interceptors to inspect and log network traffic directly in their code.
This article explains how to use Flutter HTTP interceptors to debug network requests. It covers setup, implementation, and the challenges.
What Are HTTP Interceptors in Flutter?
In Flutter, an HTTP interceptor is a component that allows you to inspect, modify, or log network requests and responses. You can use it to add headers, track requests, retry failed calls, or block specific requests.
The core http package doesn’t have built‑in interceptor support, so you need to implement this behavior by extending http.BaseClient or a plugin like http_interceptor. If you use Dio, interceptors are built in, making it easy to add this functionality to your app.
Setting Up HTTP Interceptor in Flutter
Before using a Flutter HTTP interceptor to debug network requests, you must set up the right package and configure it correctly in your app.
Prerequisites
- A Flutter app that makes HTTP or API requests
- Familiarity with the HTTP package or the Dio package for making API requests.
1. Add Dependencies
You need to add the required packages to your pubspec.yaml file. For example, if you are using the http_interceptor package:
dependencies: http: ^0.14.0 http_interceptor: ^1.0.1 Run flutter pub get to install the packages. If you are using Dio, the setup is similar: dependencies: dio: ^5.3.1
2. Create an Interceptor
You must define a class that implements the interceptor logic. This is where you will add logging and inspection code.
For http_interceptor:
import 'package:http_interceptor/http_interceptor.dart'; class LoggingInterceptor implements InterceptorContract { @override Future<RequestData> interceptRequest({required RequestData data}) async { print("Request to: ${data.url}"); print("Headers: ${data.headers}"); print("Body: ${data.body}"); return data; } @override Future<ResponseData> interceptResponse({required ResponseData data}) async { print("Response status: ${data.statusCode}"); print("Response body: ${data.body}"); return data; } }
For Dio:
import 'package:dio/dio.dart'; class LoggingInterceptor extends Interceptor { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) { print("Request to: ${options.uri}"); print("Headers: ${options.headers}"); print("Body: ${options.data}"); handler.next(options); } @override void onResponse(Response response, ResponseInterceptorHandler handler) { print("Response status: ${response.statusCode}"); print("Response body: ${response.data}"); handler.next(response); } @override void onError(DioError err, ErrorInterceptorHandler handler) { print("Error: ${err.message}"); handler.next(err); } }
Also Read: Essential Guide to Flutter Test Automation
3. Set Up the HTTP Client with Interceptors
You need to create a client that uses your interceptor.
For http_interceptor:
import 'package:http/http.dart'; import 'package:http_interceptor/http_interceptor.dart'; final client = InterceptedClient.build(interceptors: [LoggingInterceptor()]);
For Dio:
final dio = Dio(); dio.interceptors.add(LoggingInterceptor());
4. Use the Intercepted HTTP Client
You can now use this client to make requests, and the interceptor will handle logging and inspection automatically.
Example for http_interceptor:
final response = await client.get(Uri.parse("https://jsonplaceholder.typicode.com/posts/1")); print("Fetched data: ${response.body}");
Example for Dio:
final response = await dio.get("https://jsonplaceholder.typicode.com/posts/1"); print("Fetched data: ${response.data}");
Also Read: Essential Guide to Flutter Test Automation
5. Run and Test
Run your app and trigger the API calls. You will see the request and response details printed in the console. This helps you verify the data sent to and received from the API.
How to Debug Network Requests in Flutter Using HTTP Interceptor
To debug network requests using a Flutter HTTP interceptor, you need to capture and log all the essential details of each API call your app makes.
The goal is to check if the request is built correctly and sent to the correct URL, and if the response from the server is what you expect. This helps you catch issues like wrong endpoints, missing headers, or bad data before they affect your app’s behavior.
Also Read: Flutter Debugging: Top Tools and Tips
You should log:
- The full request URL to see the exact API endpoint and query parameters being called
- The request headers to verify that required headers like authentication tokens or content types are included
- The request body to check the data being sent, especially for POST or PUT requests
- The response status code to confirm if the API call succeeded or failed
- The response headers and body to inspect the returned data and ensure it matches what your app expects
- Any errors, including their messages, to see what went wrong during the network call
Below is how you can implement this using http_interceptor:
import 'package:http_interceptor/http_interceptor.dart'; class FlutterHttpLoggingInterceptor implements InterceptorContract { @override Future<RequestData> interceptRequest({required RequestData data}) async { print("Request URL: ${data.url}"); print("Request Headers: ${data.headers}"); print("Request Body: ${data.body}"); return data; } @override Future<ResponseData> interceptResponse({required ResponseData data}) async { print("Response Status: ${data.statusCode}"); print("Response Headers: ${data.headers}"); print("Response Body: ${data.body}"); return data; } }
If you are using dio, the setup looks like this:
import 'package:dio/dio.dart'; class FlutterHttpLoggingInterceptor extends Interceptor { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) { print("Request URL: ${options.uri}"); print("Request Headers: ${options.headers}"); print("Request Body: ${options.data}"); handler.next(options); } @override void onResponse(Response response, ResponseInterceptorHandler handler) { print("Response Status: ${response.statusCode}"); print("Response Headers: ${response.headers}"); print("Response Body: ${response.data}"); handler.next(response); } @override void onError(DioError err, ErrorInterceptorHandler handler) { print("Request Error: ${err.message}"); handler.next(err); } }
You can add timing logic if you want to measure how long each API call takes. This helps identify slow requests that could affect app performance. Here is an example for dio:
class TimingInterceptor extends Interceptor { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) { options.extra["startTime"] = DateTime.now(); handler.next(options); } @override void onResponse(Response response, ResponseInterceptorHandler handler) { final startTime = response.requestOptions.extra["startTime"] as DateTime; final duration = DateTime.now().difference(startTime); print("Request to ${response.requestOptions.uri} took ${duration.inMilliseconds} ms"); handler.next(response); } @override void onError(DioError err, ErrorInterceptorHandler handler) { final startTime = err.requestOptions.extra["startTime"] as DateTime; final duration = DateTime.now().difference(startTime); print("Failed request to ${err.requestOptions.uri} took ${duration.inMilliseconds} ms"); print("Error: ${err.message}"); handler.next(err); } }
Limitations of Flutter HTTP Interceptors for Debugging
Flutter HTTP interceptors help log and inspect network requests, but they have several limitations that can slow down or complicate debugging. These limits become more noticeable as your app grows or you must test more complex scenarios.
- Need for code changes: You must add or update interceptor code in your app to modify what gets logged or inspected. This means rebuilding the app each time you want to change the logging logic or inspect new parts of a request or response.
- No UI to view network activity: The interceptor logs appear in the console. There is no built-in way to filter, search, or organize these logs, so finding the exact request or response you want to inspect becomes harder, especially in apps with many API calls.
- Cannot modify or mock requests on the fly: Interceptors let you log and modify requests through code, but you cannot change request URLs, headers, or bodies at runtime without writing and deploying code changes. This makes it hard to test alternate API responses or simulate edge cases.
- Hard to share debug setup: The interceptor logic lives inside your app code. If you want other developers or testers to use the same setup, they need to copy or merge the code. There is no easy way to share and apply logging rules across teams without duplicating effort.
- No separation from app logic: The interceptor code runs as part of your app. This means any changes to logging or debugging logic can increase the risk of introducing bugs or making the app more challenging to maintain.
Requestly: A Simpler Alternative for Debugging Network Requests
Requestly allows you to intercept network traffic outside of your app. It lets you inspect, modify, and mock HTTP requests and responses without writing or changing any code. You set up rules in a visual interface that apply to network calls made by your app.
Requestly can help debug network requests in several ways:
- Inspect network traffic without code changes: Requestly shows all network requests in a UI where you can filter, search, and view full details. You need not modify or redeploy your app to see this data.
- Modify requests: Requestly rules allow you to change request URLs, headers, or bodies. This lets you test different API conditions without touching your code.
- Mock API responses: Requestly lets you simulate different API responses, including errors or specific payloads. This helps you test edge cases or build features even if the backend is not ready.
- Share rules with your team: You can export and share Requestly rules, so other developers or testers can use the same debugging setup without duplicating effort.
Conclusion
Flutter HTTP interceptors allow you to log and inspect network requests inside your app. They help track request URLs, headers, bodies, and responses so you can identify issues during development. However, they require code changes for each adjustment and offer limited control over how you view or manage network data.
Requestly offers a simpler alternative by letting you modify, mock, and inspect HTTP requests without changing your app code. You can create rules to log requests, change headers or bodies, simulate responses, or block requests in real time.