A powerful code generation package for Dart that automatically creates type-safe HTTP clients from annotated abstract classes. Inspired by Retrofit and Chopper, this package eliminates boilerplate code and provides compile-time safety for REST API calls.
- Code Generation: Automatically generates HTTP client implementations from annotated classes
- Type Safety: Full compile-time type checking for requests and responses
- JSON Serialization: Automatic JSON encoding/decoding with custom serializers
- Multiple HTTP Methods: Support for GET, POST, PUT, DELETE, PATCH
- Flexible Base URLs: Configure base URLs per client or at runtime
- Stream Support: Handle streamed requests and responses
- Path Parameters: Dynamic URL path substitution with
@Path() - Query Parameters: Single parameters with
@Query()and multiple with@QueryAll() - Request Bodies: Support for JSON, raw strings, and complex object serialization
- Form Fields: Individual fields with
@Field()or grouped with@Fields() - Headers: Custom headers and multipart support
- URL Fragments: Fragment identifier support with
@Fragment()
- Generic Type Support: Full support for generic types like
Gen<Data>andList<Map<String, Gen<Data>>> - Record Types: Support for Dart 3 records including named records
({int id, String name}) - Raw Body Mode: Send pre-serialized strings with
@Body(raw: true) - Request Cancellation: Cancel requests with
@Cancel()parameter - Custom HTTP Clients: Integrate with any HTTP client (built-in support for Dio)
- Stream Bodies: Send data as streams for large uploads
Add the packages to your pubspec.yaml:
dependencies:
http_annotation: ^0.0.1
http: ^1.6.0
dev_dependencies:
http_generator: ^0.0.1
build_runner: ^2.11.1import 'package:http_annotation/http_annotation.dart';
import 'package:http/http.dart' as http;
part 'api_client.g.dart';
@RestClient('https://api.example.com')
abstract class ApiClient with _$ApiClient {
ApiClient(this.client);
final http.Client client;
@override
Future<StreamedResponse> $send(BaseRequest request) {
return client.send(request);
}
@override
@Get('/users/{id}')
Future<User> getUser(@Path('id') String id);
@override
@Post('/users')
Future<User> createUser(@Body() User user);
@override
@Get('/users')
Future<List<User>> getUsers(@Query('page') int page);
}@RestClient()
class ApiClient with _$ApiClient {
ApiClient({required this.baseUrl});
@override
final String baseUrl;
@override
@Get('/data')
Future<Response> getData();
}@RestClient('https://api.example.com')
abstract class AdvancedClient with _$AdvancedClient {
// Generic types
@override
@Get('/generic-data')
Future<Gen<List<User>>> getGenericData();
// Records (Dart 3)
@override
@Get('/record-data')
Future<({int id, String name, bool active})> getRecordData();
// Multiple parameters
@override
@Post('/complex/{id}')
Future<List<Map<String, Gen<User>>>> complexRequest(
@Path('id') String id,
@Query('search') String search,
@Fragment() String fragment,
@Body() Gen<Map<String, User>> body,
@Cancel() Future<void> cancelToken,
);
// Form fields
@override
@Post('/form-data')
Future<void> submitForm(
@Field('name') String name,
@Field('age') int age,
@fields Map<String, String> additionalFields,
);
// Raw body
@override
@Post('/raw')
Future<void> sendRaw(@Body(raw: true) String jsonString);
// Stream upload
@override
@Put('/upload')
Future<StreamedResponse> uploadStream(@Body() Stream<List<int>> data);
}Your data classes need fromJson and toJson methods:
class User {
final String name;
final int age;
User({required this.name, required this.age});
factory User.fromJson(Map<String, dynamic> json) => User(
name: json['name'] as String,
age: json['age'] as int,
);
Map<String, dynamic> toJson() => {
'name': name,
'age': age,
};
}
// Generic types need additional serializer functions
class Gen<T> {
final T data;
Gen(this.data);
factory Gen.fromJson(
Map<String, dynamic> json,
T Function(Object?) fromJsonT,
) => Gen(fromJsonT(json['data']));
Map<String, dynamic> toJson(Object? Function(T) toJsonT) => {
'data': toJsonT(data),
};
}Run the code generator:
dart pub run build_runner buildFor continuous generation during development:
dart pub run build_runner watchUse the provided Dio client adapter:
import 'package:http_dio_client/http_dio_client.dart';
import 'package:dio/dio.dart';
final dio = Dio();
final client = ApiClient(DioClient(dio));- Primitives:
String,int,double,bool - Collections:
List<T>,Map<String, T>,Set<T> - Records:
(int, String),({int id, String name}) - Generic Types:
Response<T>,List<Response<User>> - Streams:
Stream<List<int>>,Stream<Uint8List> - HTTP Types:
http.Response,http.StreamedResponse
Contributions are welcome! Please feel free to submit a Pull Request.