Flutter Chuck Inspection
A powerful HTTP inspector for Flutter — monitor, debug, and analyze all network requests and responses in real-time with a sleek dark UI and persistent notifications.
Features
- Real-time HTTP Monitoring — Track every request and response as it happens
- Export — Save logs as JSON or CSV for offline analysis
- Persistent Notifications — Live request statistics in your notification tray
- Beautiful Dark UI — Modern interface with smooth animations
- Detailed Request Inspector — View headers, body, response, and error details
- Multiple HTTP Clients:
httppackage viaChuckHttpClientdiopackage viaChuckDioInterceptor
- Smart Filtering — Filter by All, Success, or Error requests
- Share Logs — Export and share HTTP call reports instantly
- Zero Configuration — Simple setup, works out of the box
- Notification Permission Handling — Automatic permission management on Android 13+ and iOS
- Statistics Dashboard — Total, success, and error counts at a glance
Screenshots
Notification |
Inspector List |
Success Filter |
Overview Tab |
Request Tab |
Response Tab |
Error Tab |
Installation
Add to your pubspec.yaml:
dependencies:
flutter_chuck_inspection: ^1.0.7
Then run:
flutter pub get
Platform Setup
Android
Add the following permissions to android/app/src/main/AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:label="your_app_name"
android:icon="@mipmap/ic_launcher">
<receiver android:exported="false"
android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
<receiver android:exported="false"
android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
</intent-filter>
</receiver>
<activity android:name=".MainActivity">
<!-- your activity configuration -->
</activity>
</application>
</manifest>
Ensure your android/app/build.gradle targets SDK 33 or higher:
android {
compileSdkVersion 34
defaultConfig {
minSdkVersion 21
targetSdkVersion 34
}
}
iOS
No additional configuration required.
Usage
Basic Setup
Initialize ChuckInspector in your main() and pass the same NavigatorKey to your MaterialApp:
import 'package:flutter/material.dart';
import 'package:flutter_chuck_inspection/flutter_chuck_inspection.dart';
final _navigatorKey = GlobalKey<NavigatorState>();
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await ChuckInspector().init(
navKey: _navigatorKey,
enableNotifications: true,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: _navigatorKey,
title: 'My App',
home: const HomeScreen(),
);
}
}
Using with the http Package
import 'package:http/http.dart' as http;
import 'package:flutter_chuck_inspection/flutter_chuck_inspection.dart';
final client = ChuckHttpClient(http.Client());
final response = await client.get(
Uri.parse('https://jsonplaceholder.typicode.com/posts'),
);
// All requests are automatically tracked.
Using with Dio
import 'package:dio/dio.dart';
import 'package:flutter_chuck_inspection/flutter_chuck_inspection.dart';
final dio = Dio();
dio.interceptors.add(ChuckDioInterceptor());
final response = await dio.get('https://jsonplaceholder.typicode.com/posts');
// All requests are automatically tracked.
Opening the Inspector Screen
// Programmatically
ChuckInspector().showInspector();
// Via a FloatingActionButton
FloatingActionButton(
onPressed: () => ChuckInspector().showInspector(),
child: const Icon(Icons.bug_report),
)
// Tapping the persistent notification also opens the inspector automatically.
Managing Notifications
final inspector = ChuckInspector();
// Check current state
bool enabled = inspector.notificationsEnabled;
// Request permissions at runtime
bool granted = await inspector.requestNotificationPermissions();
// Check if permissions are granted
bool hasPermission = await inspector.checkNotificationPermissions();
// Toggle notifications
inspector.enableNotifications();
inspector.disableNotifications();
// Manually refresh the notification badge
await inspector.refreshNotification();
// Cancel the persistent notification
await inspector.cancelNotifications();
// Clear all recorded calls and cancel the notification
inspector.clear();
UI Overview
Inspector Screen
| Element | Description |
|---|---|
| Statistics bar | Total requests, success count, and error count |
| Filter chips | Filter by All, Success, or Errors |
| Request cards | Method badge, status code, duration, URL path, and host |
| Export button | Export logs as JSON or CSV, or share a text summary |
| Delete button | Clear all recorded calls |
Request Details Screen
Four tabs with full request/response information:
| Tab | Content |
|---|---|
| Overview | Method, URL, status code, duration, timestamp, response size |
| Request | Request headers and body (formatted JSON) |
| Response | Response headers and body (formatted JSON) |
| Error | Error message and exception details |
Both the Request and Response tabs include a copy-to-clipboard button.
API Reference
ChuckInspector
Singleton class — access it anywhere via ChuckInspector().
Properties
| Property | Type | Description |
|---|---|---|
calls |
List<HttpCall> |
Unmodifiable list of all tracked HTTP calls |
totalCalls |
int |
Total number of calls |
successCount |
int |
Number of successful calls (status < 400, no error) |
errorCount |
int |
Number of failed calls (status >= 400 or error set) |
notificationsEnabled |
bool |
Whether notifications are currently enabled |
navigationKey |
GlobalKey<NavigatorState>? |
Navigator key used to push the inspector screen |
Methods
| Method | Returns | Description |
|---|---|---|
init({navKey, enableNotifications, ...}) |
Future<void> |
Initialize the inspector and notification channel |
showInspector() |
void |
Push the inspector screen onto the navigator stack |
clear() |
void |
Clear all tracked calls and cancel the notification |
enableNotifications() |
void |
Re-enable notification updates |
disableNotifications() |
void |
Disable and cancel the notification |
requestNotificationPermissions() |
Future<bool> |
Prompt the user to grant notification permissions |
checkNotificationPermissions() |
Future<bool> |
Check whether notification permissions are granted |
refreshNotification() |
Future<void> |
Manually update the notification with current stats |
cancelNotifications() |
Future<void> |
Cancel the persistent notification |
cancelAllNotifications() |
Future<void> |
Cancel all notifications shown by the plugin |
HttpCall
Immutable model representing a single HTTP request/response cycle.
class HttpCall {
final String id; // UUID
final String method; // GET, POST, PUT, DELETE, PATCH, ...
final String url; // Full request URL
final DateTime createdAt; // Request start time
final Map<String, dynamic>? requestHeaders;
final dynamic requestBody;
final HttpResponse? response;
final String? error;
final Duration? duration;
bool get isError; // true if error != null || status >= 400
bool get isSuccess; // true if !isError && response != null
}
HttpResponse
class HttpResponse {
final int statusCode;
final Map<String, dynamic>? headers;
final dynamic body;
final int? contentLength;
}
ExportUtils
Utility class used internally by the inspector screen.
| Method | Description |
|---|---|
exportToJson(calls) |
Serializes calls to JSON and opens the system share sheet |
exportToCsv(calls) |
Serializes calls to CSV and opens the system share sheet |
Advanced Configuration
Custom Notification Channel
await ChuckInspector().init(
navKey: _navigatorKey,
enableNotifications: true,
notificationChannelId: 'my_api_monitor',
notificationChannelName: 'API Monitor',
notificationChannelDescription: 'Tracks all outgoing API calls',
);
Disable in Production
import 'package:flutter/foundation.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
if (kDebugMode) {
await ChuckInspector().init(
navKey: _navigatorKey,
enableNotifications: true,
);
}
runApp(const MyApp());
}
Settings Screen Example
class SettingsScreen extends StatefulWidget {
const SettingsScreen({super.key});
@override
State<SettingsScreen> createState() => _SettingsScreenState();
}
class _SettingsScreenState extends State<SettingsScreen> {
final _inspector = ChuckInspector();
bool _notificationsEnabled = true;
@override
void initState() {
super.initState();
_loadPermissionState();
}
Future<void> _loadPermissionState() async {
final enabled = await _inspector.checkNotificationPermissions();
setState(() => _notificationsEnabled = enabled);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Inspector Settings')),
body: ListView(
children: [
SwitchListTile(
title: const Text('Enable Notifications'),
subtitle: const Text('Show live HTTP request statistics'),
value: _notificationsEnabled,
onChanged: (value) async {
if (value) {
final granted =
await _inspector.requestNotificationPermissions();
if (granted) {
_inspector.enableNotifications();
setState(() => _notificationsEnabled = true);
}
} else {
_inspector.disableNotifications();
setState(() => _notificationsEnabled = false);
}
},
),
ListTile(
leading: const Icon(Icons.delete),
title: const Text('Clear All Data'),
onTap: () {
_inspector.clear();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('All HTTP calls cleared')),
);
},
),
ListTile(
leading: const Icon(Icons.bug_report),
title: const Text('Open Inspector'),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
onTap: () => _inspector.showInspector(),
),
],
),
);
}
}
Troubleshooting
Notifications Not Appearing
- Verify
POST_NOTIFICATIONSpermission is inAndroidManifest.xml - Ensure
compileSdkVersionis 33 or higher - Call
requestNotificationPermissions()explicitly before the first request - Check the device notification settings for your app
- Run
flutter run --verboseand look forChuckInspector:log lines
Inspector Screen Not Opening
- Confirm
navigationKeyis assigned both toChuckInspector().init()and toMaterialApp.navigatorKey - Ensure
init()is awaited beforerunApp()
HTTP Calls Not Being Tracked
- Verify you are using
ChuckHttpClient(not a plainhttp.Client) or thatChuckDioInterceptoris added to yourDioinstance - Make sure
init()completes before the first network request - When using Dio, ensure the interceptor is added before any requests are made
Example App
See the /example folder for a complete working demo covering:
- HTTP client integration
- Dio interceptor integration
- Notification permission handling
- Settings screen with toggle
- Programmatic inspector launch
Contributing
Contributions are welcome!
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature - Commit your changes:
git commit -m 'Add your feature' - Push the branch:
git push origin feature/your-feature - Open a Pull Request
Please follow the existing code style and add tests where applicable.
License
This project is licensed under the MIT License. See the LICENSE file for details.
Acknowledgments
- Inspired by Chuck for Android
- Built with love for the Flutter community
Support
- Report a bug or request a feature: GitHub Issues
- Email: sanjaysharmajw@gmail.com
If this package helps you, please give it a star on pub.dev and GitHub!