dio_extended 2.0.0
dio_extended: ^2.0.0 copied to clipboard
a thin wrapper over the dio HTTP client that simplifies networking in Flutter.
🚀 DioExtended #
A thin, result-based wrapper over dio that makes networking in Flutter clean, safe, and boilerplate-free. #
DioExtended returns a consistent ApiResult<T> for every request, so error handling and JSON parsing stay simple and predictable. Optional Chucker helpers are exposed from a separate import so the core package stays platform-neutral.
📑 Table of Contents #
| Section | Description |
|---|---|
| ✨ Features | What you get out of the box |
| 📦 Installation | Add the package to your project |
| ⚡ Quick Start | Up and running in 30 seconds |
| 🌐 DioExtended | The core networking client |
| 🔐 Token Refresh | Automatic 401 handling |
| 📳 ShakeForChucker | Debug by shaking your phone |
| ❓ FAQ | Common questions answered |
✨ Features #
| Feature | Description | |
|---|---|---|
| 🧩 | Simplified API | Every call returns a clean, typed ApiResult<T> interface. |
| 🔄 | Automatic JSON Parsing | Decode responses into your models with a simple parseData function. |
| 🔐 | Built-in Token Refresh | Override one method to auto-handle expired tokens and retry requests. |
| 📳 | Shake for Debugging | Open the network inspector with a shake gesture — perfect for QA. |
| 🧱 | FormData Safe | Correctly separates Content-Type handling for FormData vs JSON, even on retry. |
📦 Installation #
Add dio_extended to your pubspec.yaml:
dependencies:
dio_extended: ^2.0.0
Then run:
flutter pub get
Note
Chucker helpers are available from package:dio_extended/diox_chucker.dart.
Important
Since 2.0.0, Chucker APIs are no longer exported from package:dio_extended/diox.dart. If you use ShakeForChucker, ShakeChuckerConfigs, or createChuckerInterceptor(), add a separate import for package:dio_extended/diox_chucker.dart.
⚡ Quick Start #
import 'package:dio_extended/diox.dart';
class CrudService extends DioExtended {
CrudService() : super(baseUrl: 'https://jsonplaceholder.typicode.com');
Future<ApiResult<List<PostModel>>> getPosts() {
return callApiRequest<List<PostModel>>(
request: () => get('/posts'),
parseData: (data) =>
(data as List).map((e) => PostModel.fromJson(e)).toList(),
);
}
}
// ...
final result = await CrudService().getPosts();
if (result.isSuccess) {
print(result.data); // 🎉 Typed list of PostModel
} else {
print(result.message); // ⚠️ Friendly error message
}
🌐 DioExtended: Simplified Networking #
DioExtended is the core of this package. It streamlines HTTP requests and returns a consistent ApiResult<T> object for all calls, making error handling and data parsing straightforward.
🔧 Initialization
Set up your API client with a base URL and default headers. Use headers for synchronous values and headersAsync when headers need async preparation.
import 'package:dio_extended/diox.dart';
// Static headers
final api = DioExtended(
baseUrl: 'https://api.example.com',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
);
// Async headers (e.g. fetched from secure storage)
final api = DioExtended(
baseUrl: 'https://api.example.com',
headersAsync: _buildAuthHeaders(),
);
static Future<Map<String, String>?> _buildAuthHeaders() async {
await Future.delayed(const Duration(seconds: 3)); // fetch simulation
return {
'Authorization': 'Bearer your_token_here',
'Custom-Header': 'CustomValue',
};
}
// Access the underlying Dio instance directly when needed
final dioInstance = api.dio;
Prefer a dedicated, independent service? Just extend DioExtended:
class CrudService extends DioExtended {
CrudService() : super(baseUrl: 'YOUR-BASE-URL');
// All DioExtended functions are now available here.
}
📥 GET Request Example
Provide a parseData function to map the JSON response into your model.
/// Returns an [ApiResult] with a list of [PostModel] on success.
Future<ApiResult<List<PostModel>>> getPosts() async {
return await callApiRequest<List<PostModel>>(
request: () => get('/posts'),
parseData: (data) => (data as List)
.map((itemJson) => PostModel.fromJson(itemJson))
.toList(),
);
}
/// Single-object variant.
Future<ApiResult<PostModel>> getPost() async {
return await callApiRequest<PostModel>(
request: () => get('/posts/1'),
parseData: (data) => PostModel.fromJson(data),
);
}
Using callApiRequest handles fetching and parsing. On the business-logic side, just check isSuccess:
final result = await _service.getPosts();
if (result.isSuccess) {
// ✅ Your logic here — result.data is fully typed
}
🔐 Token Refresh (Optional) #
To handle automatic token refresh, simply override handleTokenExpired. The library calls this callback when a request fails with a 401 status (or a custom code via tokenExpiredCode), then retries the original request.
Tip
- The interceptor safely separates
Content-Typebehavior forFormDataand non-FormDatarequests, including on retry. - In
handleTokenExpired, return auth-related headers only (e.g.Authorization). Avoid setting a globalContent-Typefrom refresh headers.
class CrudService extends DioExtended {
CrudService()
: super(
baseUrl: 'https://jsonplaceholder.typicode.com',
tokenExpiredCode: 401,
);
/// Override to fetch a new auth token when the current one expires.
@override
Future<dynamic> handleTokenExpired() async {
final newHeader = await fetchNewAuth();
// Return as a Map, e.g. {'Authorization': 'Bearer xxx'}
return newHeader;
}
}
📳 ShakeForChucker: Debug with a Shake #
ShakeForChucker integrates with chucker_flutter to open the network inspection UI whenever you shake the device — ideal for developers and QA testers.
⚙️ Setup
Wrap your MaterialApp with ShakeForChucker, and attach ShakeChuckerConfigs.navigatorKey to your app's navigatorKey.
import 'package:flutter/material.dart';
import 'package:dio_extended/diox_chucker.dart';
void main() {
// Initialize Chucker BEFORE runApp().
ShakeChuckerConfigs.initialize(
showOnRelease: true,
showNotification: true,
);
runApp(
ShakeForChucker(
// Number of shakes needed to trigger Chucker (default: 3)
shakeCountTriggered: 3,
child: MaterialApp(
title: 'DioExtended Demo',
// Required so Chucker can show its inspector reliably.
navigatorKey: ShakeChuckerConfigs.navigatorKey,
home: const MyHomePage(),
),
),
);
}
If you also want requests from DioExtended to appear in Chucker, register the optional interceptor:
import 'package:dio_extended/diox.dart';
import 'package:dio_extended/diox_chucker.dart';
final api = DioExtended(
baseUrl: 'https://api.example.com',
interceptors: [createChuckerInterceptor()],
);
Important
The older navigatorObservers: [ShakeChuckerConfigs.navigatorObserver] approach is now deprecated. Use navigatorKey instead — it works reliably even with nested navigators.
❓ FAQ #
What does callApiRequest return on error?
It returns an ApiResult<T> with isSuccess == false and a human-readable message. No exceptions are thrown for expected network/HTTP failures, so you can branch safely on isSuccess.
Can I still access the raw Dio instance?
Yes. Every DioExtended exposes the underlying client via api.dio, so you can add interceptors, configure timeouts, or call advanced Dio APIs directly.
Does Chucker run in release builds?
Only if you opt in. Set showOnRelease: true in ShakeChuckerConfigs.initialize(...). By default Chucker is intended for debug/QA usage.
Made with ❤️ for the Flutter community.
⭐ If this package helps you, consider starring the repository!