dio_extended 1.0.20
dio_extended: ^1.0.20 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. It also ships with ShakeForChucker — shake your device to instantly open the Chucker network inspector.
📑 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: ^1.0.20
Then run:
flutter pub get
Note
chucker_flutter and shake are bundled as dependencies — no need to add them separately.
⚡ 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. Headers are an async Future, so for static headers simply wrap them in Future.value().
import 'package:dio_extended/diox.dart';
// Static headers
final api = DioExtended(
baseUrl: 'https://api.example.com',
headers: Future.value({
'Accept': 'application/json',
'Content-Type': 'application/json',
}),
);
// Async headers (e.g. fetched from secure storage)
final api = DioExtended(
baseUrl: 'https://api.example.com',
headers: _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.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(),
),
),
);
}
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!