Retrofit.dart
retrofit.dart is an dio client generator using source_gen and inspired by Chopper and Retrofit.
Install / Use
/learn @trevorwang/Retrofit.dartREADME
Retrofit For Dart
retrofit.dart is a type conversion dio client generator using source_gen and inspired by Chopper and Retrofit.
Usage
Generator
Add the generator to your dev dependencies
dependencies:
retrofit: ^4.9.0
logger: ^2.6.0 # for logging purpose
json_annotation: ^4.9.0
dev_dependencies:
retrofit_generator: ^10.0.1
build_runner: ^2.6.0
json_serializable: ^6.10.0
Define and Generate your API
import 'package:dio/dio.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:retrofit/retrofit.dart';
part 'example.g.dart';
@RestApi(baseUrl: 'https://5d42a6e2bc64f90014a56ca0.mockapi.io/api/v1/')
abstract class RestClient {
factory RestClient(Dio dio, {String? baseUrl}) = _RestClient;
@GET('/tasks')
Future<List<Task>> getTasks();
}
@JsonSerializable()
class Task {
const Task({this.id, this.name, this.avatar, this.createdAt});
factory Task.fromJson(Map<String, dynamic> json) => _$TaskFromJson(json);
final String? id;
final String? name;
final String? avatar;
final String? createdAt;
Map<String, dynamic> toJson() => _$TaskToJson(this);
}
Configuration
You can customize the code generation behavior by creating a build.yaml file in your project root:
targets:
$default:
builders:
retrofit_generator:
options:
# Control whether to add '// dart format off/on' comments (default: true)
format_output: true
# Enable automatic response type casting (default: true)
auto_cast_response: true
# Generate empty request body for methods without parameters (default: false)
empty_request_body: false
# Enable useResult annotation for methods (default: false)
use_result: false
format_output
By default, retrofit_generator wraps the generated code with // dart format off and // dart format on comments to preserve the formatting. If you're combining retrofit with other generators (like riverpod) and need more control over formatting, you can disable this:
targets:
$default:
builders:
retrofit_generator:
options:
format_output: false
then run the generator
# dart
dart pub run build_runner build
# for watch mode (recommended during development)
dart pub run build_runner watch
Lean Builder Support (Experimental)
Retrofit now has experimental support for lean_builder, a faster build system for Dart. While lean_builder support is still under development, the infrastructure has been added for future use.
Important: lean_builder is an optional dependency and is NOT required to use retrofit_generator. It's only needed if you want to try the experimental lean_builder support.
To prepare for lean_builder support, add it to your dev_dependencies:
dev_dependencies:
lean_builder: ^0.1.2 # Optional - only if you want to use lean_builder
Note: For now, please continue using build_runner as shown above. Full lean_builder integration will be available in a future release once lean_builder reaches stability.
Use it
import 'package:dio/dio.dart';
import 'package:logger/logger.dart';
import 'package:retrofit_example/example.dart';
final logger = Logger();
void main(List<String> args) {
final dio = Dio(); // Provide a dio instance
dio.options.headers['Demo-Header'] = 'demo header'; // config your dio headers globally
final client = RestClient(dio);
client.getTasks().then((it) => logger.i(it));
}
More
Types
Types conversion
Before you use the type conversion, please make sure that a
factory Task.fromJson(Map<String, dynamic> json)must be provided for each model class.json_serializableis recommended to be used as the serialization tool.
@GET('/tasks')
Future<List<Task>> getTasks();
@JsonSerializable()
class Task {
const Task({required this.name});
factory Task.fromJson(Map<String, dynamic> json) => _$TaskFromJson(json);
final String name;
}
For enums, we rely on the
toString()method to convert it to a string. Override thetoString()method to return the value you want.
enum Status {
pending,
completed;
@override
String toString() => name;
}
@GET('/tasks/{status}')
Future<List<Task>> getTasksByStatus(@Path() Status status);
Using dart_mappable
You can use dart_mappable for type conversion by setting the parser to Parser.DartMappable:
@RestApi(
baseUrl: 'https://api.example.com',
parser: Parser.DartMappable,
)
abstract class ApiService {
factory ApiService(Dio dio) = _ApiService;
@GET('/tasks')
Future<List<Task>> getTasks();
}
@MappableClass()
class Task with TaskMappable {
const Task({this.id, this.name});
final String? id;
final String? name;
}
Don't forget to add the required dependencies:
dependencies:
dart_mappable: ^4.2.0
dev_dependencies:
dart_mappable_builder: ^4.2.0
And generate the code:
dart run build_runner build
For a complete example, see the example_dartmappable directory.
Typed extras
If you want to add static extra to all requests.
class MetaData extends TypedExtras {
final String id;
final String region;
const MetaData({required this.id, required region});
}
@MetaData(
id: '1234',
region: 'ng',
)
@GET("/get")
Future<String> fetchData();
HTTP Methods
The HTTP methods in the below sample are supported.
@GET('/tasks/{id}')
Future<Task> getTask(@Path('id') String id);
@GET('/demo')
Future<String> queries(@Queries() Map<String, dynamic> queries);
@GET('https://httpbin.org/get')
Future<String> namedExample(
@Query('apikey') String apiKey,
@Query('scope') String scope,
@Query('type') String type,
@Query('from') int from,
);
@PATCH('/tasks/{id}')
Future<Task> updateTaskPart(
@Path() String id,
@Body() Map<String, dynamic> map,
);
@PUT('/tasks/{id}')
Future<Task> updateTask(@Path() String id, @Body() Task task);
@DELETE('/tasks/{id}')
Future<void> deleteTask(@Path() String id);
@POST('/tasks')
Future<Task> createTask(@Body() Task task);
@POST('http://httpbin.org/post')
@MultiPart()
Future<void> createNewTaskFromFile(@Part() File file);
@POST('http://httpbin.org/post')
@FormUrlEncoded()
Future<String> postUrlEncodedFormData(@Field() String hello);
Runtime Content-Type for Multipart Uploads
Use @PartMap() to provide runtime metadata (like contentType and fileName) for multipart file uploads:
@POST('/api/files')
@MultiPart()
Future<void> uploadFile({
@Part(name: 'file') required File file,
@PartMap() Map<String, dynamic>? metadata,
});
// Usage - Upload different file types to the same endpoint
// Upload a JPEG image
await client.uploadFile(
file: File('/path/to/image.jpg'),
metadata: {
'file_contentType': 'image/jpeg',
'file_fileName': 'photo.jpg',
},
);
// Upload a PDF document
await client.uploadFile(
file: File('/path/to/document.pdf'),
metadata: {
'file_contentType': 'application/pdf',
'file_fileName': 'report.pdf',
},
);
The @PartMap() annotation accepts a Map<String, dynamic> with keys in the format:
'<partName>_contentType'- Sets the content type for the part'<partName>_fileName'- Sets the file name for the part
Fallback behavior:
- Runtime values from
@PartMap()override static values from@Part()annotation - If
@PartMap()value is not provided, uses static value from@Part()annotation - If neither is provided:
fileNamedefaults to the file's actual name (extracted from file path)contentTypedefaults tonull(Dio will auto-detect based on file extension)
Dynamic Field Names for Multiple Files
Use @Part() with Map<String, File> to upload multiple files with dynamic field names:
@POST('/api/files')
@MultiPart()
Future<void> uploadFiles(@Part() Map<String, File> files);
// Usage - Upload multiple files with custom field names
await client.uploadFiles({
'image[0]': File('/path/to/photo1.jpg'),
'image[1]': File('/path/to/photo2.jpg'),
'document': File('/path/to/report.pdf'),
});
This feature also supports:
Map<String, MultipartFile>- For files already wrapped in MultipartFileMap<String, List<int>>- For raw byte data- Nullable maps:
Map<String, File>?
Use cases:
- Uploading arrays of files where each file needs a unique indexed name (e.g.,
image[0],image[1]) - Uploading files to endpoints that require specific field names determined at runtime
- Sending multiple files of different types in a single request
Get original HTTP response
@GET('/tasks/{id}')
Future<HttpResponse<Task>> getTask(@Path('id') String id);
@GET('/tasks')
Future<HttpResp
Related Skills
node-connect
348.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
108.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
348.0kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
348.0kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
