mobile_api 0.0.11+18
mobile_api: ^0.0.11+18 copied to clipboard
Typed REST and GraphQL API helpers for Flutter apps, with token refresh, secure cache storage, multipart uploads, and network checks.
mobile_api #
A Dart/Flutter package for REST and GraphQL API integration.
Features #
- REST client with shared headers, JSON decoding, typed success/failure results, and one retry after token refresh
- GraphQL client with typed list/detail helpers and refresh support
- Secure token/cache helpers backed by
flutter_secure_storage - Optional network connectivity checks
- Multipart/form-data upload support
- Optional logger endpoint for captured API errors
Getting started #
Add the package to your pubspec.yaml:
dependencies:
mobile_api: ^0.0.9+16
Import it:
import 'package:mobile_api/mobile_api.dart';
Configuration #
final cache = ICacheRepoImpl();
final apiConfig = ApiConfig(
apiUrl: Uri.parse('https://api.example.com'),
refreshTokenPath: '/api/accounts/refresh',
loggerPath: '/logger',
appCache: cache,
checkNetwork: CheckNetwork(),
headers: const {
HttpHeadersConst.marketplace: 'ml',
HttpHeadersConst.userAgent: 'mlApp',
HttpHeadersConst.acceptLanguage: 'tr',
'referer': 'https://teststore.hibeta.kz/',
'x-hibeta-country-locked': 'true',
'x-hibeta-language': 'tr',
'x-hibeta-storefront-host': 'teststore.hibeta.kz',
'x-hibeta-tenant-country': 'KZ',
'x-hibeta-tenant-mode': 'single-country',
},
);
Tokens are read from CoreCacheKey.accessToken and CoreCacheKey.refreshToken.
await cache.saveAll(
CacheKeyBundle.tokenPair(
access: accessToken,
refresh: refreshToken,
),
);
Projects with a custom refresh payload or token model can extend
IBOAuth2Token and configure the refresh parser:
class ProjectToken extends IBOAuth2Token {
ProjectToken({
required super.accessToken,
required super.refreshToken,
required this.tenantId,
});
factory ProjectToken.fromJson(Map<String, dynamic> json) => ProjectToken(
accessToken: json['accessToken'] as String,
refreshToken: json['refreshToken'] as String,
tenantId: json['tenantId'] as String,
);
final String tenantId;
}
final apiConfig = ApiConfig<ProjectToken>(
apiUrl: Uri.parse('https://api.example.com'),
refreshTokenPath: '/api/accounts/refresh',
appCache: cache,
refreshBodyBuilder: (refreshToken) => {
'refresh_token': refreshToken,
'grant_type': 'refresh_token',
'client_id': 'mobile-app',
},
refreshTokenFromJson: ProjectToken.fromJson,
);
REST usage #
final httpApi = IHttpImpl(apiConfig: apiConfig);
final result = await httpApi.baseMethod<UserResponse, DefaultErrorResponse>(
'/api/users/me',
requestType: RequestType.get,
dataFromJson: UserResponse.fromJson,
errorFromJson: DefaultErrorResponse.fromJson,
);
switch (result) {
case Success(value: final user):
// use user
case Failure(exception: final error):
// handle error
}
Repositories can pass operation-specific headers directly without creating a wrapper API client:
final result = await httpApi.baseMethod<OrderResponse, DefaultErrorResponse>(
'/api/orders',
requestType: RequestType.post,
dataFromJson: OrderResponse.fromJson,
errorFromJson: DefaultErrorResponse.fromJson,
body: order.toJson(),
headers: {'Idempotency-Key': operationId},
);
Use baseMethodJson when a successful response can be a top-level JSON array
or scalar. The decoder receives the complete decoded JSON value:
final result = await httpApi.baseMethodJson<List<UserResponse>, DefaultErrorResponse>(
'/api/users',
requestType: RequestType.get,
dataFromJson: (json) => (json as List<Object?>)
.map((item) => UserResponse.fromJson(item as Map<String, dynamic>))
.toList(),
errorFromJson: DefaultErrorResponse.fromJson,
);
For endpoints that return 204 No Content or another successful empty body,
provide an emptySuccessBuilder so the typed result can still be a success:
final result = await httpApi.baseMethod<BooleanResponse, DefaultErrorResponse>(
'/api/users/me',
requestType: RequestType.delete,
dataFromJson: BooleanResponse.fromJsonPhone,
errorFromJson: DefaultErrorResponse.fromJson,
emptySuccessBuilder: (_) => BooleanResponse(isSuccess: true),
);
GraphQL usage #
final graphQlApi = IGraphQlImpl(
apiConfig: apiConfig.copyWith(
apiUrl: Uri.parse('https://api.example.com/graphql'),
),
);
final result = await graphQlApi.queryList<UserResponse, DefaultErrorResponse>(
path: '''
query users {
users {
pageInfo { hasNextPage }
items { id name }
}
}
''',
field: 'users',
dataFromJson: UserResponse.fromJson,
errorFromJson: DefaultErrorResponse.fromJson,
);
Development certificates #
By default, the package uses normal platform certificate validation. For local development against a host with a self-signed certificate, set allowBadCertificates: true in ApiConfig. Do not enable it for production builds.