Dio

repository·main·Indexed 11 days ago

https://github.com/cfug/dio

A highly extensible HTTP client for Dart and Flutter applications. Dio provides a robust foundation for networking, featuring support for interceptors, global and per-request configuration via BaseOptions and Options, FormData for file uploads, and customizable Transformers for request and response data. It includes specialized tools like LogInterceptor for debugging and QueuedInterceptor for sequential request processing.

Tokens
29.6K
Snippets
100
Records
137
Agent score
95%

What's inside Dio

  1. Overview of the dio project

    main
    dio is a powerful HTTP client for Dart. The project is organized into a core package (dio) and several specialized plugins for different environments and functionalities. When publishing your own dio-related packages, it is recommended to add the dio topic to your pubspec.yaml to improve discoverability.
  2. What is a QueuedInterceptor and when to use it

    main
    Standard Interceptors execute concurrently, meaning multiple requests can enter the interceptor at the same time. If you need requests to be processed sequentially (step-by-step)—for example, when you need to perform an asynchronous task like fetching a csrfToken before allowing any subsequent network requests to proceed—use a QueuedInterceptor.
  3. How the dio_compatibility_layer works

    main
    The dio_compatibility_layer provides a bridge between the Dio API and other HTTP client implementations. It uses the ConversionLayerAdapter class to wrap an existing client (such as an http.Client) and presents it to Dio as a valid HttpClientAdapter. This enables developers to leverage the feature-rich Dio interface while using specialized underlying HTTP implementations like cronet_http or cupertino_http.
  4. Use HttpClientAdapter to bridge Dio and HttpClient

    main

    The HttpClientAdapter acts as a bridge between Dio's high-level API and the actual HttpClient making the requests. This allows you to use different HTTP clients (like dart:io or Web-based clients).

    • Default: IOHttpClientAdapter on native, BrowserHttpClientAdapter on Web.
    • Explicitly setting platform adapters:
      • Web: BrowserHttpClientAdapter from package:dio/browser.dart.
      • Native: IOHttpClientAdapter from package:dio/io.dart.
    import 'package:dio/io.dart';
    // ...
    dio.httpClientAdapter = IOHttpClientAdapter();
  5. How file downloading works on Web

    main

    When using Dio.download on the Web, the adapter fetches the response bytes and triggers a browser download using a Blob URL.

    Key behaviors and limitations:

    • savePath: This argument is treated as a suggested filename, not a local filesystem path. The browser determines the actual save location.
    • Download Process: A returned Response indicates Dio fetched the response and dispatched the browser download click; it does not guarantee the file was written to disk or that the browser didn't prompt the user.
    • Memory: The entire response is loaded into memory before the download starts.
    • CORS: Requests are still subject to CORS because they are fetched via Dio.
    • Protocol: Network requests are handled via XHR, so HTTP versioning (HTTP/1.1, HTTP/2, etc.) is controlled by the browser.
    • Unsupported Features:
      • FileAccessMode.append is not supported.
      • deleteOnError has no effect as there is no local file to delete.
      • Custom lengthHeader values are ignored; progress totals rely on browser response progress events.
    • Requirements: Relies on browser support for Blob, URL.createObjectURL, and HTMLAnchorElement.download.
  6. Best practice: Reuse FormData and MultipartFiles

    main

    Do not reuse the same FormData or MultipartFile instance across multiple requests. Doing so can cause Cannot finalize exceptions. Always create a new instance for every request.

    Future<void> _repeatedlyRequest() async {
      Future<FormData> createFormData() async {
        return FormData.fromMap({
          'name': 'dio',
          'date': DateTime.now().toIso8601String(),
          'file': await MultipartFile.fromFile('./text.txt',filename: 'upload.txt'),
        });
      }
      
      await dio.post('some-url', data: await createFormData());
    }
  7. Use Interceptors to preprocess requests and responses

    main

    Interceptors are added to a Dio instance in a queue (FIFO). They allow you to perform unified operations before a request is sent, after a response is received, or when an error occurs.

    You can use InterceptorsWrapper to implement these hooks. Within an interceptor, you can:

    • Resolve: Return a custom Response to bypass the actual network call.
    • Reject: Return a DioException to trigger an error in the caller's catchError block.
    • Next: Pass the request/response to the next interceptor in the queue.
    dio.interceptors.add(
      InterceptorsWrapper(
        onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
          // To complete request with custom data:
          // return handler.resolve(Response(requestOptions: options, data: 'fake data'));
          return handler.next(options);
        },
        onResponse: (Response response, ResponseInterceptorHandler handler) {
          // To terminate and trigger error:
          // return handler.reject(DioException(...));
          return handler.next(response);
        },
        onError: (DioException error, ErrorInterceptorHandler handler) {
          return handler.next(error);
        },
      ),
    );
  8. Reuse FormData and MultipartFile correctly

    main

    When making repeated requests, always create a new instance of FormData or MultipartFile for every request. Do not assign a FormData object to a shared variable and reuse it, as this can lead to serialization errors.

    Future<void> _repeatedlyRequest() async {
      // Correct: Create a new instance inside a factory function for every call
      Future<FormData> createFormData() async {
        return FormData.fromMap({
          'name': 'dio',
          'date': DateTime.now().toIso8601String(),
          'file': await MultipartFile.fromFile('./text.txt', filename: 'upload.txt'),
        });
      }
      
      await dio.post('some-url', data: await createFormData());
    }
  9. How CookieManager and CookieJar work together

    main

    The CookieManager interceptor relies on the cookie_jar package to handle cookie storage and retrieval.

    • CookieJar: An in-memory implementation. Cookies are managed automatically but do not persist across app restarts.
    • PersistCookieJar: A file-based implementation that persists cookies to local storage. This is recommended for long-term sessions.

    Note for Flutter users: When using PersistCookieJar, the storage path must exist and have write access. It is recommended to use the path_provider package to obtain a valid directory (like getApplicationDocumentsDirectory()).

    import 'package:cookie_jar/cookie_jar.dart';
    import 'package:dio/dio.dart';
    import 'package:dio_cookie_manager/dio_cookie_manager.dart';
    import 'package:path/path' as path;
    
    // Example of persistent storage in Flutter
    Future<void> prepareCookieManager(Dio dio) async {
      final directory = await getApplicationDocumentsDirectory();
      final cookieJar = PersistCookieJar(
        ignoreExpires: true,
        storage: FileStorage(path.join(directory.path, "/.cookies/")),
      );
      dio.interceptors.add(CookieManager(cookieJar));
    }
  10. Use QueuedInterceptor for serial execution

    main
    While standard Interceptor instances process requests in parallel, QueuedInterceptor ensures that requests enter the interceptor sequentially. This is useful for scenarios like refreshing a security token (e.g., csrfToken) where multiple concurrent requests might otherwise trigger multiple redundant token refresh calls.
  11. Understand dio versioning and breaking changes

    main
    Major and minor version updates in dio may contain breaking changes. Before updating your project, you should review the official Migration Guide to understand the full scope of changes. For details on how the project handles versioning, refer to the COMPATIBILITY_POLICY.md file.
  12. Understand the Dio compatibility support range

    main

    The dio project follows a general compatibility policy regarding Dart SDK support. Typically, the oldest Dart SDK supported for any package is one that was released less than 2 years ago.

    However, this support range is subject to the following exceptions:

    • Dependency Requirements: The minimum SDK version must satisfy the requirements of all package dependencies (e.g., if a dependency requires Dart SDK >=3.0.0, the package will require at least 3.0.0).
    • Breaking Changes: Implementation details may become incompatible between the latest SDK and previous SDKs.
    • Security: Previous SDK versions may be dropped if they contain security issues that necessitate an upgrade.