dio_curl_interceptor 3.3.3-alpha8 copy "dio_curl_interceptor: ^3.3.3-alpha8" to clipboard
dio_curl_interceptor: ^3.3.3-alpha8 copied to clipboard

A powerful Dio interceptor that converts HTTP requests to cURL commands, with modern UI for debugging, caching, filtering, and webhook integration.

dio_curl_interceptor #

pub package pub points popularity

A Flutter package with a Dio interceptor that logs HTTP requests as cURLβ€”ideal for debugging. Includes a modern UI to view, filter, and manage logs, plus webhook integration for team collaboration.

Features #

  • πŸ” Converts Dio HTTP requests to cURL commands for easy debugging and sharing.
  • πŸ“ Enhanced FormData handling with detailed file information in cURL commands.
  • πŸ’Ύ Caches cURL commands and responses with filtering and search options.
  • πŸ–₯️ Modern Flutter widget for viewing and managing cURL logs (search, filter by status/date, clear, copy, etc).
  • πŸ”” Webhook integration for remote logging and team collaboration (Discord & Telegram support, including bug and exception reporting).
  • πŸ“ Utility methods for custom interceptors and direct use.

For detailed screenshots of the interceptor's behavior, including simultaneous and chronological logging, please refer to the Screenshots section at the bottom of this README.

Migration Guide #

For detailed migration instructions, breaking changes, and code examples, please see our comprehensive MIGRATION.md guide.

Terminal Compatibility #

Below is a compatibility table for different terminals and their support for printing and ANSI colors:

-- currently being tested

Terminal/Console print/debugPrint log (dart:developer) ANSI Colors Support
VS Code Debug Console βœ… βœ… βœ…
Android Studio Logcat -- -- --
Android Studio Debug Tab -- -- --
IntelliJ IDEA Console -- -- --
Flutter DevTools Console -- -- --
Terminal/CMD -- -- --
PowerShell -- -- --
Xcode Console -- -- --

Usage #

Option 1: Using the CurlInterceptor #

Simple add the interceptor to your Dio instance, all done for you:

final dio = Dio();
dio.interceptors.add(CurlInterceptor()); // Simple usage with default options
// or
dio.interceptors.add(CurlInterceptor.allEnabled()); // Enable all options

You can customize the interceptor with CurlOptions and CacheOptions:

dio.interceptors.add(CurlInterceptor(
  curlOptions: CurlOptions(
    status: true, // Show status codes + name in logs
    responseTime: true, // Show response timing
    behavior: CurlBehavior.chronological,
    onRequest: RequestDetails(
      visible: true,
      ansi: Ansi.yellow, // ANSI color for request
    ),
    onResponse: ResponseDetails(
      visible: true,
      requestHeaders: true, // Show request headers
      requestBody: true, // Show request body
      responseBody: true, // Show response body
      responseHeaders: true, // Show response headers
      limitResponseBody: null, // Limit response body length (characters), default is null (no limit)
      ansi: Ansi.green, // ANSI color for response
    ),
    onError: ErrorDetails(
      visible: true,
      requestHeaders: true,
      requestBody: true,
      responseBody: true,
      responseHeaders: true,
      limitResponseBody: null,
      ansi: Ansi.red, // ANSI color for errors
    ),
    // Configure pretty printing options
    prettyConfig: PrettyConfig(
      blockEnabled: true, // Enable pretty printing
      colorEnabled: true, // Force enable/disable colored
      emojiEnabled: true, // Enable/disable emoji
      lineLength: 100, // Set the length of separator lines
    ),
    // Custom printer function to override default logging behavior
    printer: (String text) {
      // do whatever you want with the text
      // ...
      // Your custom logging implementation
      print('Custom log: $text'); // remember to print the text
    },
  ),
  webhookInspectors: [
    DiscordInspector(
      webhookUrls: ['https://discord.com/api/webhooks/your-webhook-url'],
      inspectionStatus: [ResponseStatus.clientError, ResponseStatus.serverError],
      includeUrls: const ['/api/v1/users', 'https://example.com/data'],
      excludeUrls: const ['/api/v1/auth/login', 'https://example.com/sensitive'],
    ),
    TelegramInspector(
      botToken: 'YOUR_BOT_TOKEN', // Get from @BotFather
      chatIds: [-1003019608685], // Get from getUpdates API
      inspectionStatus: [ResponseStatus.clientError, ResponseStatus.serverError],
      includeUrls: const ['/api/v1/users', 'https://example.com/data'],
      excludeUrls: const ['/api/v1/auth/login', 'https://example.com/sensitive'],
    ),
  ],
))

Option 2: Using CurlUtils directly in your own interceptor #

If you prefer to use the utility methods in your own custom interceptor, you can use CurlUtils directly:

class YourInterceptor extends Interceptor {
  // Initialize webhook inspectors
  final webhookInspectors = [
    DiscordInspector(
      webhookUrls: ['https://discord.com/api/webhooks/your-webhook-url'],
      inspectionStatus: [ResponseStatus.clientError, ResponseStatus.serverError],
    ),
    TelegramInspector(
      botToken: 'YOUR_BOT_TOKEN', // Get from @BotFather
      chatIds: [-1003019608685], // Get from getUpdates API
      inspectionStatus: [ResponseStatus.clientError, ResponseStatus.serverError],
    ),
  ];

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    // ... your request handling logic (like adding headers, modifying options, etc.)

    // for measure request time, it will add `X-Client-Time` header, then consume on response (error)
    CurlUtils.addXClientTime(options);

    CurlUtils.handleOnRequest(options);
    handler.next(options);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    // ... your response handling logic
    CurlUtils.handleOnResponse(response, webhookInspectors: webhookInspectors);
    handler.next(response);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    // ... your error handling logic
    CurlUtils.handleOnError(err, webhookInspectors: webhookInspectors);
    handler.next(err);
  }
}

Using Multiple Webhook Inspectors

You can configure multiple webhook inspectors to send notifications to different services simultaneously. Each inspector operates independently with its own filters and configuration:

// Example of using multiple webhook inspectors
final webhookInspectors = [
  DiscordInspector(
    webhookUrls: ['https://discord.com/api/webhooks/your-discord-webhook'],
    inspectionStatus: [ResponseStatus.clientError, ResponseStatus.serverError],
    includeUrls: ['api.example.com'],
  ),
  TelegramInspector(
    botToken: 'YOUR_BOT_TOKEN', // Get from @BotFather
    chatIds: [-1003019608685], // Get from getUpdates API
    inspectionStatus: [ResponseStatus.serverError], // Only server errors to Telegram
    includeUrls: ['api.example.com'],
  ),
];

// Use with CurlInterceptor
dio.interceptors.add(CurlInterceptor(
  webhookInspectors: webhookInspectors,
  // ... other options
));

Option 3: Using webhook integration #

You can use webhook integration to send cURL logs to Discord channels or Telegram chats for remote logging and team collaboration:

Setting up Telegram Webhooks

For Telegram integration, you need to:

  1. Create a Telegram Bot:

    • Message @BotFather on Telegram
    • Use /newbot command and follow the instructions
    • Save your bot token
  2. Get your Chat ID:

    • Start a conversation with your bot
    • Send any message to the bot
    • Visit https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates
    • Find your chat ID in the response (it's a number, can be negative for groups)
  3. Configure the TelegramInspector:

    • Use botToken and chatIds parameters directly
    • Example: TelegramInspector(botToken: '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11', chatIds: [123456789])
// Using manual webhook configuration
dio.interceptors.add(CurlInterceptor(
  webhookInspectors: [
    DiscordInspector(
      webhookUrls: ['https://discord.com/api/webhooks/your-webhook-url'],
      includeUrls: ['api.example.com', '/users/'],
      excludeUrls: ['/healthz'],
      inspectionStatus: [ResponseStatus.clientError, ResponseStatus.serverError],
    ),
    TelegramInspector(
      botToken: 'YOUR_BOT_TOKEN', // Get from @BotFather
      chatIds: [-1003019608685], // Get from getUpdates API
      includeUrls: ['api.example.com'],
      inspectionStatus: [ResponseStatus.serverError], // Only server errors
    ),
  ],
));

// Manual webhook sending
final discordInspector = DiscordInspector(
  webhookUrls: ['https://discord.com/api/webhooks/your-webhook-url'],
);

final telegramInspector = TelegramInspector(
  botToken: 'YOUR_BOT_TOKEN', // Get from @BotFather
  chatIds: [-1003019608685], // Get from getUpdates API
);

// Send messages
await discordInspector.sendMessage(content: 'Hello from Discord!');
await telegramInspector.sendMessage(content: 'Hello from Telegram!');

// Send bug reports
await discordInspector.sendBugReport(
  error: 'Example Error',
  message: 'An example bug report.',
  extraInfo: {'userId': 'testUser', 'appVersion': '1.0.0'},
);

await telegramInspector.sendBugReport(
  error: 'Example Error',
  message: 'An example bug report.',
  extraInfo: {'userId': 'testUser', 'appVersion': '1.0.0'},
);

Option 4: Using utility functions directly #

If you don't want to add a full interceptor, you can use the utility functions directly in your code:

// Generate a curl command from request options
final dio = Dio();
final response = await dio.get('https://example.com');

// Generate and log a curl command
CurlUtils.logCurl(response.requestOptions);

// Log response details
CurlUtils.handleOnResponse(response);

// Cache a successful response
CurlUtils.cacheResponse(response);

// Log error details
try {
  await dio.get('https://invalid-url.com');
} on DioException catch (e) {
  CurlUtils.handleOnError(e);

  // Cache an error response
  CurlUtils.cacheError(e);
}

## Dio Cache Storage

### Public Flutter Widget: cURL Log Viewer

Show pre-built popup cURL log viewer widget with `showCurlViewer(context)`:

```dart
ElevatedButton(
  onPressed: () => showCurlViewer(context),
  child: const Text('View cURL Logs'),
);

The log viewer supports:

  • Search and filter by status code, date range, or text
  • Copy cURL command
  • Clear all logs
  • Enhanced sharing functionality with improved system integration
  • Better error handling and UI responsiveness

Floating Bubble Overlay #

For a non-intrusive debugging experience, use the floating bubble overlay that wraps your main app content:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: CurlBubble(
          // Wrap your main app content
          body: YourMainContent(),
          initialPosition: const Offset(50, 200),
          snapToEdges: false, // Stays where you drag it
          onExpanded: () => debugPrint('Bubble expanded'),
          onMinimized: () => debugPrint('Bubble minimized'),
        ),
      ),
    );
  }
}

Bubble Features

  • Draggable: Drag the bubble around the screen
  • Free Positioning: Stays where you drag it (no auto-snapping by default)
  • Expandable: Tap to expand and view cURL logs
  • Non-intrusive: Stays on top without blocking your app
  • Controller-based: Full programmatic control via BubbleOverlayController

Custom Bubble Widgets

CurlBubble(
  body: YourMainContent(),
  customMinimizedChild: Container(
    width: 60,
    height: 60,
    decoration: BoxDecoration(
      color: Colors.blue,
      shape: BoxShape.circle,
    ),
    child: const Icon(Icons.bug_report, color: Colors.white),
  ),
  customExpandedChild: Container(
    child: const CurlViewer(displayType: CurlViewerDisplayType.bubble),
  ),
  maxExpandedWidth: 400,
  maxExpandedHeight: 500,
)

Programmatic Control

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late BubbleOverlayController _controller;

  @override
  void initState() {
    super.initState();
    _controller = BubbleOverlayController();
    _controller.configure(
      snapToEdges: false, // Stays where you drag it
      enableLogging: true,
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: BubbleOverlay(
          controller: _controller,
          body: YourMainContent(),
          minimizedChild: Container(
            width: 60,
            height: 60,
            decoration: BoxDecoration(
              color: Colors.green,
              shape: BoxShape.circle,
            ),
            child: const Icon(Icons.terminal, color: Colors.white),
          ),
          expandedChild: const CurlViewer(displayType: CurlViewerDisplayType.bubble),
        ),
        floatingActionButton: Column(
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            FloatingActionButton(
              onPressed: () => _controller.show(),
              child: Icon(Icons.visibility),
            ),
            SizedBox(height: 10),
            FloatingActionButton(
              onPressed: () => _controller.hide(),
              child: Icon(Icons.visibility_off),
            ),
          ],
        ),
      ),
    );
  }
}

Generic Bubble Overlay

For non-cURL use cases, use the generic BubbleOverlay:

BubbleOverlay(
  body: YourMainContent(),
  minimizedChild: CircleAvatar(
    radius: 30,
    backgroundColor: Colors.blue,
    child: Icon(Icons.chat, color: Colors.white),
  ),
  expandedChild: Container(
    width: 200,
    height: 150,
    padding: const EdgeInsets.all(12),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(16),
      boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 8)],
    ),
    child: const Column(
      children: [
        Text("Chat Bubble", style: TextStyle(fontWeight: FontWeight.bold)),
        Text("This is expanded content"),
      ],
    ),
  ),
  onExpanded: () => debugPrint("Bubble expanded"),
  onMinimized: () => debugPrint("Bubble minimized"),
)

Note: File export functionality has been removed in v3.3.3-alpha. For legacy export features, see the legacy documentation.

Cache Storage Initialization #

Before using caching or the log viewer, initialize storage in your main():

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await CachedCurlService.init();
  runApp(const MyApp());
}

Screenshots #

Simultaneous (log the curl and response (error) together) #

Simultaneous Screenshot

Chronological (log the curl immediately after the request is made) #

Chronological Screenshot

Cached Viewer #

Cached Viewer Screenshot

Inspect Bug Discord #

Inspect Bug Discord Screenshot

Inspect cURL Discord #

Inspect cURL Discord Screenshot

License #

This project is licensed under the MIT License - see the LICENSE file for details.

  • Repository: GitHub
  • Bug Reports: Please file issues on the GitHub repository
  • Feature Requests: Feel free to suggest new features through GitHub issues

"Buy Me A Coffee"

Contributions are welcome! Please feel free to submit a Pull Request.

2
likes
0
points
1.24k
downloads

Publisher

verified publishervenhdev.me

Weekly Downloads

A powerful Dio interceptor that converts HTTP requests to cURL commands, with modern UI for debugging, caching, filtering, and webhook integration.

Repository (GitHub)
View/report issues

Topics

#curl #dio #logging #console #monitor

License

unknown (license)

Dependencies

colored_logger, dio, flutter, flutter_secure_storage, hive, hive_flutter, path_provider, share_plus, type_caster

More

Packages that depend on dio_curl_interceptor