fast_dio

Dio-based HTTP client for Flutter with the same developer experience as fast_http.

It provides a typed request layer (GenericRequest), global headers, structured errors, multipart uploads, query parameters, download/stream support, and Dio interceptors — without exposing low-level Dio setup in every feature.

Features

  • Same API pattern as fast_http: FastDio, FastDioHeader, RequestApi, GenericRequest
  • JSON parsing with getObject, getList, getResponse, getBytes
  • Global and per-request headers (static + async dynamic headers)
  • Centralized error handling with ServerException and RequestErrorModel
  • Status-code hooks for auth flows (401, 406, etc.)
  • Query parameters support
  • Multipart uploads with named fields via RequestFile
  • Per-request Dio options: timeouts, CancelToken, progress callbacks
  • File download and streamed responses
  • Interceptor helpers and built-in logging support
  • Optional Chucker Flutter inspector (useChucker) for in-app request/response UI
  • Exports fpdart for functional error handling

Getting started

Add the dependency:

dependencies:
  fast_dio: ^1.0.0

Or from GitHub:

dependencies:
  fast_dio:
    git:
      url: https://github.com/El-sayed-mahmoud/fast_dio.git

Usage

Initialize

import 'package:fast_dio/fast_dio.dart';

void setupHttp() {
  FastDio.initialize(
    genericDataKey: 'data',
    checkStatusKey: 'status',
    checkResponseIsSuccess: (response) => true,
    getErrorMessageFromResponse: (response) {
      return response['message']?.toString() ?? 'An error occurred';
    },
    onGetResponseStatusCode: (statusCode) {
      if (statusCode == 401) {
        // handle logout / redirect
      }
    },
    connectTimeout: const Duration(seconds: 30),
    receiveTimeout: const Duration(seconds: 60),
  );

  FastDioHeader().addHeader('Accept', '*/*');
  FastDioHeader().addHeader('content-type', 'application/json');
  FastDioHeader().addDynamicHeader(
    'Authorization',
    () async => 'Bearer YOUR_TOKEN',
  );

  FastDio.addLogInterceptor(responseBody: true);
}

Chucker inspector (on-device)

Enable in-app request/response inspection while testing on a real device or emulator:

import 'package:chucker_flutter/chucker_flutter.dart';
import 'package:fast_dio/fast_dio.dart';
import 'package:flutter/foundation.dart';

void setupHttp() {
  FastDio.initialize(
    onGetResponseStatusCode: (statusCode) {},
    useChucker: kDebugMode,
  );
}

// In MaterialApp — required for Chucker notifications and screens:
MaterialApp(
  navigatorKey: ChuckerFlutter.navigatorKey,
  home: const HomePage(),
);

Or add manually:

FastDio.addChuckerInterceptor();

Notes:

  • useChucker takes precedence over usePrettyLogger when both are true.
  • On Android, set minSdkVersion to at least 22.
  • Chucker runs in debug mode by default; set ChuckerFlutter.showOnRelease = true only if you need it in release builds.

GET object

final request = GenericRequest<UserModel>(
  fromMap: UserModel.fromJson,
  method: RequestApi.get(
    url: 'https://api.example.com/user/1',
    queryParameters: {'lang': 'ar'},
  ),
);

final user = await request.getObject();

POST with body

final request = GenericRequest<LoginResponse>(
  fromMap: LoginResponse.fromJson,
  method: RequestApi.post(
    url: 'https://api.example.com/login',
    body: {'email': email, 'password': password},
  ),
);

final response = await request.getObject();

Upload file

await RequestApi.post(
  url: 'https://api.example.com/upload',
  body: {'title': 'avatar'},
  isMultipartRequest: true,
  files: [
    RequestFile(
      field: 'file',
      file: await MultipartFile.fromFile(path, filename: 'avatar.jpg'),
    ),
  ],
).request();

Download file

final path = await RequestApi.get(
  url: 'https://api.example.com/report.pdf',
).download(savePath: '/path/to/report.pdf');

Stream response

final body = await RequestApi.get(
  url: 'https://api.example.com/stream',
).requestStream();

final stream = body.stream;

Progress

FastDio.progressUpdates.listen((progress) {
  print('${progress.direction}: ${progress.percentage}%');
});

Example

A minimal end-to-end setup with Chucker in debug mode and a typed GET request:

import 'package:fast_dio/fast_dio.dart' hide State;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  setupHttp();
  runApp(const MyApp());
}

void setupHttp() {
  FastDio.initialize(
    genericDataKey: 'data',
    checkStatusKey: 'status',
    checkResponseIsSuccess: (response) => response['status'] == true,
    getErrorMessageFromResponse: (response) {
      return response['message']?.toString() ?? 'An error occurred';
    },
    onGetResponseStatusCode: (statusCode) {
      if (statusCode == 401) {
        // handle logout / redirect
      }
    },
    connectTimeout: const Duration(seconds: 30),
    receiveTimeout: const Duration(seconds: 60),
    useChucker: kDebugMode,
  );

  FastDioHeader().addHeader('Accept', 'application/json');
  FastDioHeader().addDynamicHeader(
    'Authorization',
    () async => 'Bearer YOUR_TOKEN',
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: ChuckerFlutter.navigatorKey,
      home: const HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  String _result = 'Tap the button to load a user';

  Future<void> _loadUser() async {
    try {
      final request = GenericRequest<UserModel>(
        fromMap: UserModel.fromJson,
        method: RequestApi.get(
          url: 'https://jsonplaceholder.typicode.com/users/1',
        ),
      );

      final user = await request.getResponse();
      setState(() => _result = '${user.name} (${user.email})');
    } on ServerException catch (e) {
      setState(() => _result = e.errorMessageModel.statusMessage);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('fast_dio example')),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(24),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(_result, textAlign: TextAlign.center),
              const SizedBox(height: 16),
              FilledButton(
                onPressed: _loadUser,
                child: const Text('Load user'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class UserModel {
  const UserModel({required this.name, required this.email});

  final String name;
  final String email;

  factory UserModel.fromJson(dynamic json) {
    return UserModel(
      name: json['name']?.toString() ?? '',
      email: json['email']?.toString() ?? '',
    );
  }
}

Run the sample app from the package repo:

cd example
flutter pub get
flutter run

Repository with fpdart (Either)

static Future<Either<Failure, UserModel>> getUserData({String? userId}) async {
  try {
    // profileUrl / userUrl are your own endpoint builders
    final url = userId == currentUserId ? profileUrl : userUrl(userId: userId);

    UserModel response = await GenericRequest<UserModel>(
      method: RequestApi.get(url: url),
      fromMap: UserModel.fromJson,
    ).getObject();
    return Right(response);
  } on ServerException catch (failure) {
    return Left(ServerFailure(failure.errorMessageModel));
  }
}

static Future<Either<Failure, List<UserModel>>> getUserList() async {
  try {
    List<UserModel> response = await GenericRequest<UserModel>(
      method: RequestApi.get(url: usersListUrl),
      fromMap: UserModel.fromJson,
    ).getList();
    return Right(response);
  } on ServerException catch (failure) {
    return Left(ServerFailure(failure.errorMessageModel));
  }
}

static Future<Either<Failure, bool>> logout() async {
  try {
    bool response = await GenericRequest<bool>(
      method: RequestApi.get(url: logoutUrl),
      fromMap: (_) => true,
    ).getObject();
    return Right(response);
  } on ServerException catch (failure) {
    return Left(ServerFailure(failure.errorMessageModel));
  }
}
result.fold(
  (failure) => showError(failure.errorModel.statusMessage),
  (user) => showProfile(user),
);

API overview

fast_http fast_dio
FastHttp FastDio
FastHttpHeader FastDioHeader
RequestApi RequestApi
GenericRequest GenericRequest
dartz export fpdart export

Additional information