smart_api 1.0.3
smart_api: ^1.0.3 copied to clipboard
Dynamic Dio wrapper for Flutter — auto raw/multipart detection, global loader/snackbar/toast hooks, session-expiry handling, and no-internet retry, no try/catch boilerplate.
smart_api #
Your previous BaseClient + ApiProvider pattern worked, but required writing the same 15-line try/catch block for every new project. This package centralizes that logic once, making it fully reusable across all your projects.
Solves #
- Raw vs. multipart confusion — Send a plain
Map; if it contains aFileor a valid local file path, the package automatically createsFormDataand converts it to aMultipartFile. HTTP URL strings or plain text remain raw JSON. No manual conversions required. - Dynamic UI hooks (Loader / Snackbar / Toast) — Zero hard-coded UI packages (GetX, Fluttertoast, custom widgets, etc.). Set your handlers once inside
SmartApiHooks, and every call uses them automatically. A built-in, dependency-freeSmartApiToastis included if you don't want to reach for a third-party toast package at all. - Centrally handled status codes — 401/403 (session expired), timeouts, connection errors, and generic server errors are handled in one place instead of being rewritten across every API call.
- No-internet retry flow — The
onNoInternet(retry)hook receives a retry callback. Simply callretry()from your "No Internet" screen to re-fire the exact failed request. - One line per call —
SmartApiClient.instance.post(...)— NoApiRequest, noRequestType, and no repetitiveDataResponse.fromJsonboilerplate.
Setup (once, in main.dart) #
import 'package:smart_api/smart_api.dart';
SmartApiConfig.init(
baseUrl: "https://api.example.com",
headerBuilder: () => {"Authorization": "Bearer $token"},
);
SmartApiHooks.showLoader = Utils.showLoading;
SmartApiHooks.hideLoader = Utils.hideLoading;
SmartApiHooks.showMessage = (msg, {type}) => Utils.showAlert(message: msg);
SmartApiHooks.onSessionExpired = () => Get.offAllNamed(AppRoutes.signIn);
SmartApiHooks.onNoInternet = (retry) => Get.to(() => NoInternetPage(onRetry: retry));
Call an API #
final res = await SmartApiClient.instance.post<UserModel>(
"/api/login",
body: {"email": email, "password": password},
fromJson: (json) => UserModel.fromJson(json),
);
if (res.success) {
// res.data is already typed as UserModel
} else {
Utils.showAlert(message: res.message);
}
File upload — no separate handleMultipartRequest needed:
await SmartApiClient.instance.put("/api/editProfile", body: {
"name": name,
"image": pickedImage.path, // auto-detected and converted to multipart
});
See example/lib/main.dart for a full, runnable demo (GET/POST calls, no-internet retry, and session-expiry handling against a free test API).
Backend response shape differs per project? #
Two levels of control:
1. Just rename keys (most cases) — no custom parser required:
SmartApiConfig.init(
baseUrl: "...",
keyMap: const SmartApiKeyMap(
successKeys: ['ok'],
successValues: [true],
messageKeys: ['msg'],
dataKeys: ['payload'],
),
);
2. Fully custom shape (nested envelopes, etc.) — write a custom response parser:
SmartApiConfig.init(
baseUrl: "...",
responseParser: (json) => SmartApiEnvelope(
success: json['meta']['ok'] == true,
message: json['meta']['msg'] ?? '',
data: json['payload'],
),
);
File uploads — auto or explicit, single or multiple, custom key names #
By default (autoDetectFiles: true), any File or valid local file path inside body is auto-converted to multipart. Disable it globally or per request to use explicit file bindings:
// Single image with a custom field name:
files: [SmartApiFile.single(key: "image", path: path)]
// Multiple images under the same field:
files: SmartApiFile.list(key: "images", paths: [p1, p2, p3])
// Different keys per file (e.g., front/back ID + selfie):
files: [
SmartApiFile.single(key: "license_front", path: frontPath),
SmartApiFile.single(key: "license_back", path: backPath),
SmartApiFile.single(key: "profile_image", path: selfiePath),
]
Passing files always forces multipart mode, regardless of the autoDetectFiles setting. Standard text fields can still be passed in body alongside files.
Query params on any method, not just GET #
SmartApiClient.instance.post(
"/api/roomSearch",
body: data, // JSON or multipart body
queryParams: {"id": roomId} // Appends ?id=... regardless of HTTP method
);
Built-in toast/snackbar — SmartApiToast #
No third-party toast package required. Sixteen-style-worth of look-and-feel is driven by one small config table instead of copy-pasted widgets, so picking a style is just an enum value:
SmartApiHooks.showMessage = (msg, {type}) => SmartApiToast.show(
navigatorKey.currentContext!,
msg,
style: SmartApiToast.styleForMsgType(type), // or any SmartApiToastStyle directly
);
Available styles: info, success, error, warning, elite, gradient,
timer, map, welcome, human, typewriter.
Future Roadmap Ideas #
- Add
retryCounttoSmartApiRequestfor automatic retries on 5xx responses. - Add response caching (per-endpoint TTL) for list and query endpoints.
- Add a
downloadFile()helper with progress callbacks using the same hook architecture.