๐ AutoPilot API
Zero-boilerplate smart API engine for Flutter
Only 2 dependencies ยท No Dio ยท Production Ready ยท Works with every state manager
Features โข Installation โข Quick Start โข Usage โข State Managers โข API Reference
๐ฏ Why AutoPilot?
Every Flutter project has this problem โ you write the same boilerplate for every single API call:
// โ The OLD painful way โ 40+ lines, copy-pasted everywhere
Future<UserModel?> getUser(int id) async {
try {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('token');
final response = await http.get(
Uri.parse('$baseUrl/users/$id'),
headers: {
'Authorization': 'Bearer $token',
'Content-Type' : 'application/json',
'Accept' : 'application/json',
},
).timeout(const Duration(seconds: 30));
if (response.statusCode == 200) {
final json = jsonDecode(response.body);
if (json['status'] == true) {
return UserModel.fromJson(json['data']);
} else {
throw Exception(json['message']);
}
} else if (response.statusCode == 401) {
// handle unauthorized...
} else if (response.statusCode == 422) {
// handle validation...
}
} on SocketException {
throw Exception('No internet');
} on TimeoutException {
throw Exception('Timeout');
} catch (e) {
rethrow;
}
return null;
}
// โ
The AutoPilot way โ 4 lines, everything automated
final res = await api.get(
endpoint : '/users/1',
parser : UserModel.fromJson,
);
if (res.isSuccess) print(res.data);
else print(res.message);
AutoPilot automatically handles:
| What | How |
|---|---|
| ๐ Token injection | Auto reads from secure storage, injects into every request |
| ๐ Token refresh | On 401 โ refresh โ retry original request automatically |
| ๐ Internet check | Detects offline before request, returns clean error |
| โฑ Timeout | Configurable, returns typed exception |
| ๐ Retry | Exponential backoff on network failures |
| ๐พ Caching | Memory + disk, configurable duration per-request |
| โก Deduplication | Same concurrent requests โ 1 network call |
| ๐ฆ Parsing | Auto parse JSON โ your model via parser function |
| ๐จ Logging | Beautiful colored logs with full payload in debug |
| ๐ด Error handling | Typed exceptions, global error callback |
| โณ Loader | Global loading state via callback |
โจ Features
- ๐ GET, POST, PUT, PATCH, DELETE โ all HTTP methods
- ๐ Multipart upload โ single file, multiple files, with fields
- โฌ๏ธ File download โ with progress callback
- ๐ Auto token injection โ SharedPreferences + memory cache
- ๐ Auto token refresh โ on 401, retry original request
- ๐พ Two-layer cache โ memory (instant) + disk (persistent)
- โก Request deduplication โ prevent duplicate concurrent requests
- ๐ Retry with exponential backoff โ configurable attempts
- ๐ Connectivity check โ pure Dart, no extra package
- ๐ฆ Smart response parser โ envelope + list + raw support
- ๐จ Colored ANSI logs โ full request/response payload
- ๐ Works with all state managers โ GetX, Riverpod, Bloc, Provider, MobX
- ๐ Runtime environment switching โ staging โ production
- ๐ฃ Extension methods โ
.handle(),.mapData() - ๐งช 25+ unit tests included
- ๐ MIT License โ free for personal & commercial use
๐ฆ Installation
Add to your pubspec.yaml:
dependencies:
autopilot_api: ^1.0.0
Or if using as local package:
dependencies:
autopilot_api:
path: ../autopilot_api
flutter pub get
Only 2 transitive dependencies:
http+shared_preferences
โก Quick Start
Step 1 โ Initialize once in main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await AutoPilotApi.init(
baseUrl : 'https://api.example.com/v1',
enableLogs : true, // ๐จ beautiful colored logs
printPayload : true, // ๐ full JSON in debug console
enableCache : true, // ๐พ auto cache GET responses
maxRetries : 3, // ๐ retry on network failure
);
runApp(const MyApp());
}
Step 2 โ Call APIs anywhere in your app
final api = AutoPilotApi.instance;
final res = await api.get(
endpoint : '/users/1',
parser : UserModel.fromJson,
);
if (res.isSuccess) {
print(res.data); // โ
UserModel
} else {
print(res.message); // โ error message
}
That's it. No repositories. No try-catch. No headers. No token injection. No JSON decoding.
๐ง Full Configuration
await AutoPilotApi.init(
// โโ Required โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
baseUrl : 'https://api.example.com/v1',
// โโ Auth โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
tokenType : 'Bearer', // default
enableTokenRefresh : true,
onRefreshToken : () async {
// your refresh logic โ return new access token
final res = await AuthService.refreshToken();
return res.accessToken;
},
// โโ Network โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
timeoutSeconds : 30, // default
maxRetries : 3, // retry attempts
retryDelay : Duration(seconds: 1), // exponential: 1s, 2s, 3s
// โโ Cache โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
enableCache : true,
cacheDuration : Duration(minutes: 5), // default
// โโ Debug Logs โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
enableLogs : true, // auto false in release
printPayload : true, // print full JSON body
prettyPrint : true, // formatted vs minified
// โโ Global Loader โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
enableGlobalLoader : true,
onLoadingChanged : (loading) {
// hook into your state manager
Get.find<AppController>().isLoading(loading); // GetX
// or: ref.read(loadingProvider.notifier).state = loading; // Riverpod
},
// โโ Global Error Handler โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
onError : (message, statusCode) {
Get.snackbar('Error', message); // GetX
// or: ScaffoldMessenger.of(context).showSnackBar(...);
},
// โโ Analytics / Hooks โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
onRequestSent : (url, method) => Analytics.log('$method $url'),
onResponseReceived : (url, code, time) => Analytics.log('$code ${time.inMilliseconds}ms'),
// โโ Global Headers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
globalHeaders : {
'X-App-Version' : '1.0.0',
'X-Platform' : Platform.isAndroid ? 'android' : 'ios',
'X-Locale' : 'en',
},
// โโ Response Envelope โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// Match your backend's JSON structure
successKey : 'status', // your API's success field name
successValue : true, // value that means success
messageKey : 'message', // your API's message field name
dataKey : 'data', // your API's data field name
// โโ Performance โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
enableDeduplication : true, // prevent duplicate concurrent GETs
);
๐ All Requests
GET
// Single object
final res = await api.get<UserModel>(
endpoint : '/users/1',
parser : UserModel.fromJson,
);
// List
final res = await api.get<List<UserModel>>(
endpoint : '/users',
parser : (json) => (json as List).map(UserModel.fromJson).toList(),
);
// With query params โ /posts?userId=1&page=2&limit=10
final res = await api.get<List<PostModel>>(
endpoint : '/posts',
queryParams : {'userId': 1, 'page': 2, 'limit': 10},
parser : (json) => (json as List).map(PostModel.fromJson).toList(),
);
// With cache
final res = await api.get(
endpoint : '/app/config',
parser : ConfigModel.fromJson,
useCache : true,
cacheDuration : Duration(hours: 1), // override global duration
);
// With custom headers
final res = await api.get(
endpoint : '/premium/content',
headers : {'X-Plan': 'premium'},
parser : ContentModel.fromJson,
);
POST
// Login
final res = await api.post<LoginModel>(
endpoint : '/auth/login',
body : {'email': email, 'password': password},
parser : LoginModel.fromJson,
);
// Create resource
final res = await api.post<PostModel>(
endpoint : '/posts',
body : {
'title' : 'My Post',
'content' : 'Hello World',
'tags' : ['flutter', 'dart'],
},
parser: PostModel.fromJson,
);
// Without parser (just check success)
final res = await api.post(
endpoint : '/auth/logout',
body : {'device_id': deviceId},
);
if (res.isSuccess) navigateToLogin();
PUT / PATCH
// Full update
final res = await api.put<UserModel>(
endpoint : '/users/1',
body : {'name': 'John', 'email': 'john@example.com', 'age': 25},
parser : UserModel.fromJson,
);
// Partial update
final res = await api.patch<UserModel>(
endpoint : '/users/1',
body : {'name': 'New Name'}, // only changed fields
parser : UserModel.fromJson,
);
DELETE
final res = await api.delete(endpoint: '/posts/1');
if (res.isSuccess) print('Deleted! ${res.message}');
MULTIPART โ File Upload
// Single file โ shorthand
final res = await api.multipart<UploadModel>(
endpoint : '/profile/photo',
fileKey : 'image',
filePath : pickedFile.path,
parser : UploadModel.fromJson,
);
// Multiple files
final res = await api.multipart(
endpoint : '/gallery/upload',
files : [
MultipartFileModel(key: 'photos', path: img1.path),
MultipartFileModel(key: 'photos', path: img2.path),
MultipartFileModel(key: 'photos', path: img3.path),
],
);
// Files + form fields
final res = await api.multipart<UploadModel>(
endpoint : '/documents/upload',
files : [
MultipartFileModel(
key : 'document',
path : file.path,
fileName : 'report_2025.pdf', // custom filename
mimeType : 'application/pdf', // custom mime type
),
],
fields : {
'title' : 'Q4 Report',
'category' : 'finance',
'year' : '2025',
},
parser : UploadModel.fromJson,
);
// Video upload
final res = await api.multipart(
endpoint : '/videos/upload',
files : [
MultipartFileModel(
key : 'video',
path : videoFile.path,
mimeType : 'video/mp4',
),
],
);
DOWNLOAD
final res = await api.download(
endpoint : '/reports/january-2025.pdf',
savePath : '/storage/emulated/0/Download/report.pdf',
onProgress : (received, total) {
final percent = (received / total * 100).toInt();
print('Downloading: $percent%');
setState(() => downloadProgress = percent / 100);
},
);
if (res.isSuccess) {
print('Saved to: ${res.data}'); // res.data = file path
}
๐ฆ ApiResponse<T>
Every request returns a standardized ApiResponse<T>:
final res = await api.get(endpoint: '/users/1', parser: UserModel.fromJson);
// Core fields
res.isSuccess // bool โ true if request succeeded
res.data // T? โ your parsed model (null on failure)
res.message // String โ success msg or error msg from server
res.statusCode // int โ 200, 201, 400, 401, 404, 422, 500...
res.raw // dynamic โ raw JSON response body
res.errors // Map<String, dynamic>? โ validation errors
res.requestId // String? โ short tracing ID e.g. "a3f2b1c9"
res.responseTime // Duration? โ how long the request took
res.timestamp // DateTime โ when response was received
// Status helpers
res.isClientError // statusCode 400โ499
res.isServerError // statusCode 500+
res.isUnauthorized // statusCode == 401
res.isForbidden // statusCode == 403
res.isNotFound // statusCode == 404
res.isValidationError // statusCode == 422
res.isTimeout // statusCode == 408
// Data helpers
res.dataOr(UserModel.guest) // returns fallback if data is null
res.dataOrThrow // throws StateError if data is null
// Validation error helpers
res.validationError('email') // โ "Email is required"
res.validationError('phone') // โ "Invalid phone number"
res.allErrors // all validation errors as single string
๐ Token Management
// After login โ token auto-injected into every subsequent request
await AutoPilotApi.setToken(loginRes.data!.accessToken);
// Save both tokens
await AutoPilotApi.setTokens(
accessToken : loginRes.data!.accessToken,
refreshToken : loginRes.data!.refreshToken,
);
// Logout โ clear all tokens
await AutoPilotApi.clearTokens();
Tokens stored in SharedPreferences with in-memory cache for fast access. Auto-injected as Authorization: Bearer <token> header.
๐พ Caching
Two-layer cache: memory (instant, lost on restart) + disk (SharedPreferences, survives restart).
// Enable globally in init
await AutoPilotApi.init(
baseUrl : '...',
enableCache : true,
cacheDuration : Duration(minutes: 5),
);
// Override per-request
final res = await api.get(
endpoint : '/app/config',
useCache : true,
cacheDuration : Duration(hours: 24), // cache for 1 day
);
// Invalidate specific endpoint
await api.invalidateCache('/app/config');
// Wipe all cache
await api.clearCache();
Cache key is built from URL + sorted query params โ same request always hits same cache entry.
๐ Retry with Exponential Backoff
await AutoPilotApi.init(
baseUrl : '...',
maxRetries : 3, // retry 3 times
retryDelay : Duration(seconds: 1), // 1s โ 2s โ 3s (exponential)
);
Retries on: network errors, socket exceptions, connection drops, timeouts.
Does NOT retry on: 4xx client errors (400, 401, 403, 404, 422).
๐ Auto Token Refresh
await AutoPilotApi.init(
baseUrl : '...',
enableTokenRefresh : true,
onRefreshToken : () async {
// Called automatically on 401
final refreshToken = await TokenManager.getRefreshToken();
final response = await http.post(
Uri.parse('https://api.example.com/auth/refresh'),
body: jsonEncode({'refresh_token': refreshToken}),
headers: {'Content-Type': 'application/json'},
);
final json = jsonDecode(response.body);
return json['access_token'] as String?; // return new token
},
);
// Now ALL your requests auto-handle 401:
// Request โ 401 โ refresh token โ retry original request โ โ
// Developer writes NOTHING extra.
โก Request Deduplication
Prevents the same GET request from hitting the server twice when called simultaneously:
// Both widgets call this at the same time
// Result: only ONE network request is made
final results = await Future.wait([
api.get(endpoint: '/user/me', parser: UserModel.fromJson),
api.get(endpoint: '/user/me', parser: UserModel.fromJson),
api.get(endpoint: '/user/me', parser: UserModel.fromJson),
]);
// All 3 get the same result from 1 request โก
๐จ Debug Logs
AutoPilot prints beautiful colored logs in debug mode โ automatically disabled in release builds:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๐ REQUEST [a3f2b1c9] 14:32:01.432
โ POST https://api.example.com/v1/auth/login
โ โณ Headers
โ Content-Type: application/json
โ Authorization: ***masked***
โ โณ Body
โ {
โ "email": "john@example.com",
โ "password": "***"
โ }
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
RESPONSE [a3f2b1c9] 14:32:01.687
โ Status: 200 โฑ 255ms
โ https://api.example.com/v1/auth/login
โ โณ Payload
โ {
โ "status": true,
โ "message": "Login successful",
โ "data": {
โ "token": "eyJhbGci...",
โ "user": { "id": 1, "name": "John" }
โ }
โ }
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๐ฅ ERROR [b7d3e2f1] 14:35:22.901
โ https://api.example.com/v1/posts/999
โ Status: 404
โ Resource not found.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
await AutoPilotApi.init(
enableLogs : true, // default: kDebugMode (auto false in release)
printPayload : true, // show full request/response JSON
prettyPrint : true, // formatted JSON (false = minified)
);
๐ฃ Extension Methods
// .handle() โ clean success/failure without if/else
await api.get(endpoint: '/me', parser: UserModel.fromJson)
.handle(
onSuccess : (data, message) => setState(() => user = data),
onFailure : (message, code) => showSnackbar(message),
);
// .mapData() โ transform data type in the chain
final countRes = await api
.get<String>(endpoint: '/stats/count')
.mapData((s) => int.parse(s)); // ApiResponse<int>
print(countRes.data); // int
๐ Runtime Environment Switching
// Switch between environments without restarting
api.reconfigure(
(config) => config.copyWith(baseUrl: 'https://staging.api.example.com'),
);
// Switch back to production
api.reconfigure(
(config) => config.copyWith(baseUrl: 'https://api.example.com'),
);
๐ State Manager Integration
AutoPilot works with every Flutter state manager โ zero conflicts, zero setup.
GetX
class UserController extends GetxController {
final _api = AutoPilotApi.instance;
final user = Rx<UserModel?>(null);
final isLoading = false.obs;
final error = RxString('');
Future<void> fetchUser() async {
isLoading.value = true;
final res = await _api.get(
endpoint : '/users/me',
parser : UserModel.fromJson,
);
isLoading.value = false;
if (res.isSuccess) user.value = res.data;
else error.value = res.message;
}
Future<void> updateProfile(Map<String, dynamic> data) async {
final res = await _api.patch(
endpoint : '/users/me',
body : data,
parser : UserModel.fromJson,
);
if (res.isSuccess) {
user.value = res.data;
Get.snackbar('โ
', res.message);
}
}
}
Provider
class UserProvider extends ChangeNotifier {
final _api = AutoPilotApi.instance;
UserModel? user;
bool isLoading = false;
String error = '';
Future<void> fetchUser() async {
isLoading = true; notifyListeners();
final res = await _api.get(
endpoint : '/users/me',
parser : UserModel.fromJson,
);
isLoading = false;
if (res.isSuccess) user = res.data;
else error = res.message;
notifyListeners();
}
}
Riverpod
// FutureProvider
final userProvider = FutureProvider<UserModel?>((ref) async {
final res = await AutoPilotApi.instance.get(
endpoint : '/users/me',
parser : UserModel.fromJson,
);
if (!res.isSuccess) throw res.message;
return res.data;
});
// AsyncNotifier (recommended for mutations)
class UserNotifier extends AsyncNotifier<UserModel?> {
@override
Future<UserModel?> build() async => null;
Future<void> fetch() async {
state = const AsyncLoading();
final res = await AutoPilotApi.instance.get(
endpoint : '/users/me',
parser : UserModel.fromJson,
);
state = res.isSuccess
? AsyncData(res.data)
: AsyncError(res.message, StackTrace.current);
}
}
final userNotifierProvider =
AsyncNotifierProvider<UserNotifier, UserModel?>(() => UserNotifier());
Bloc / Cubit
// Cubit (simpler)
class UserCubit extends Cubit<UserState> {
final _api = AutoPilotApi.instance;
UserCubit() : super(UserInitial());
Future<void> fetchUser() async {
emit(UserLoading());
final res = await _api.get(
endpoint : '/users/me',
parser : UserModel.fromJson,
);
if (res.isSuccess) emit(UserLoaded(res.data!));
else emit(UserError(res.message));
}
Future<void> uploadPhoto(String path) async {
emit(UserLoading());
final res = await _api.multipart(
endpoint : '/users/photo',
fileKey : 'image',
filePath : path,
parser : UserModel.fromJson,
);
if (res.isSuccess) emit(UserLoaded(res.data!));
else emit(UserError(res.message));
}
}
// Bloc (event-based)
class UserBloc extends Bloc<UserEvent, UserState> {
final _api = AutoPilotApi.instance;
UserBloc() : super(UserInitial()) {
on<FetchUserEvent>(_onFetch);
on<UpdateUserEvent>(_onUpdate);
}
Future<void> _onFetch(FetchUserEvent e, Emitter<UserState> emit) async {
emit(UserLoading());
final res = await _api.get(endpoint: '/users/me', parser: UserModel.fromJson);
res.isSuccess ? emit(UserLoaded(res.data!)) : emit(UserError(res.message));
}
Future<void> _onUpdate(UpdateUserEvent e, Emitter<UserState> emit) async {
emit(UserLoading());
final res = await _api.patch(endpoint: '/users/me', body: e.data, parser: UserModel.fromJson);
res.isSuccess ? emit(UserLoaded(res.data!)) : emit(UserError(res.message));
}
}
MobX
part 'user_store.g.dart';
class UserStore = _UserStore with _$UserStore;
abstract class _UserStore with Store {
final _api = AutoPilotApi.instance;
@observable UserModel? user;
@observable bool isLoading = false;
@observable String error = '';
@action
Future<void> fetchUser() async {
isLoading = true;
final res = await _api.get(endpoint: '/users/me', parser: UserModel.fromJson);
isLoading = false;
if (res.isSuccess) user = res.data;
else error = res.message;
}
}
๐ Architecture
autopilot_api/
โโโ lib/
โ โโโ autopilot_api.dart โ barrel export (import this)
โ โโโ core/
โ โ โโโ autopilot_core.dart โ ๐ง main engine (AutoPilotApi class)
โ โโโ models/
โ โ โโโ api_response.dart โ ApiResponse<T> wrapper
โ โ โโโ autopilot_config.dart โ AutoPilotConfig with copyWith
โ โ โโโ multipart_file_model.dart โ MultipartFileModel
โ โโโ auth/
โ โ โโโ token_manager.dart โ SharedPrefs + memory token storage
โ โโโ cache/
โ โ โโโ cache_service.dart โ memory + disk two-layer cache
โ โโโ connectivity/
โ โ โโโ connectivity_service.dart โ pure Dart internet check
โ โโโ exceptions/
โ โ โโโ api_exception.dart โ typed exceptions hierarchy
โ โโโ extensions/
โ โ โโโ response_extensions.dart โ .handle(), .mapData()
โ โโโ logger/
โ โ โโโ autopilot_logger.dart โ colored ANSI debug logger
โ โโโ parsers/
โ โ โโโ response_parser.dart โ smart JSON โ model parser
โ โโโ queue/
โ โ โโโ request_queue.dart โ request deduplication
โ โโโ retry/
โ โโโ retry_service.dart โ exponential backoff retry
โโโ example/
โ โโโ lib/main.dart โ full working demo app
โโโ test/
โ โโโ autopilot_test.dart โ 25+ unit tests
โโโ LICENSE
โโโ CHANGELOG.md
โโโ pubspec.yaml
๐ API Reference
AutoPilotApi
| Method | Description |
|---|---|
AutoPilotApi.init(...) |
Initialize โ call once in main() |
AutoPilotApi.instance |
Get singleton instance |
AutoPilotApi.setToken(token) |
Save access token |
AutoPilotApi.setTokens(...) |
Save access + refresh token |
AutoPilotApi.clearTokens() |
Clear all tokens (logout) |
api.get(...) |
GET request |
api.post(...) |
POST request |
api.put(...) |
PUT request |
api.patch(...) |
PATCH request |
api.delete(...) |
DELETE request |
api.multipart(...) |
File upload (single/multiple) |
api.download(...) |
File download with progress |
api.invalidateCache(endpoint) |
Clear cache for endpoint |
api.clearCache() |
Clear all cached responses |
api.reconfigure(fn) |
Update config at runtime |
ApiResponse<T>
| Property | Type | Description |
|---|---|---|
isSuccess |
bool |
Request succeeded |
data |
T? |
Parsed model |
message |
String |
Server message |
statusCode |
int |
HTTP status code |
raw |
dynamic |
Raw JSON body |
errors |
Map? |
Validation errors |
requestId |
String? |
Tracing ID |
responseTime |
Duration? |
Request duration |
isUnauthorized |
bool |
statusCode == 401 |
isValidationError |
bool |
statusCode == 422 |
dataOr(fallback) |
T |
Safe data with fallback |
validationError(field) |
String? |
First error for field |
allErrors |
String |
All errors as string |
MultipartFileModel
| Property | Type | Description |
|---|---|---|
key |
String |
Form field name |
path |
String |
Absolute file path |
fileName |
String? |
Custom filename |
mimeType |
String? |
Custom MIME type |
๐งช Testing
# Run all tests
flutter test
# Run with coverage
flutter test --coverage
๐ Response Envelope Support
AutoPilot supports any backend response structure. Configure the keys to match your API:
// Your API returns:
// { "success": true, "msg": "OK", "result": { ... } }
await AutoPilotApi.init(
baseUrl : '...',
successKey : 'success', // default: 'status'
successValue : true, // default: true
messageKey : 'msg', // default: 'message'
dataKey : 'result', // default: 'data'
);
// Flat response (no envelope) โ also works automatically
// { "id": 1, "name": "John" }
๐ค Contributing
Contributions are welcome! Please open an issue or submit a PR.
๐ License
MIT License
Copyright (c) 2025 AutoPilot API
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Made with โค๏ธ for the Flutter community
โญ Star on GitHub โข ๐ฆ View on pub.dev โข ๐ Report Bug
Libraries
- auth/token_manager
- autopilot_api
- AutoPilot API โ Zero-boilerplate smart API engine for Flutter.
Only depends on
http+shared_preferences. - cache/cache_service
- connectivity/connectivity_service
- core/autopilot_core
- exceptions/api_exception
- extensions/response_extensions
- logger/autopilot_logger
- models/api_response
- models/autopilot_config
- models/multipart_file_model
- parsers/response_parser
- queue/request_queue
- retry/retry_service