retrofit 1.3.4

  • Readme
  • Changelog
  • Example
  • Installing
  • 98

Retrofit For Dart #

retrofit retrofit_generator Dart CI CircleCI Build Status

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: any
  logger: any  #for logging purpose

dev_dependencies:
  retrofit_generator: any
  build_runner: any

Define and Generate your API #

import 'package:json_annotation/json_annotation.dart';
import 'package:retrofit/retrofit.dart';
import 'package:dio/dio.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 {
  String id;
  String name;
  String avatar;
  String createdAt;

  Task({this.id, this.name, this.avatar, this.createdAt});

  factory Task.fromJson(Map<String, dynamic> json) => _$TaskFromJson(json);
  Map<String, dynamic> toJson() => _$TaskToJson(this);
}

then run the generator

# dart
pub run build_runner build

# flutter	
flutter pub run build_runner build

Use it #

import 'package:logger/logger.dart';
import 'package:retrofit_example/example.dart';
import 'package:dio/dio.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 #

Type 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_serializable is the recommanded to be used as the serialization tool.

...
@GET("/tasks")
  Future<List<Task>> getTasks();
}

@JsonSerializable()
class Task {
  String name;
  Task({this.name});
  factory Task.fromJson(Map<String, dynamic> json) => _$TaskFromJson(json);
}

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")
  Future<void> createNewTaskFromFile(@Part() File file);

  @POST("http://httpbin.org/post")
  @FormUrlEncoded()
  Future<String> postUrlEncodedFormData(@Field() String hello);

Get orignal HTTP reponse #

  @GET("/tasks/{id}")
  Future<HttpResponse<Task>> getTask(@Path("id") String id)

  @GET("/tasks")
  Future<HttpResponse<List<Task>>>> getTasks()

HTTP Header #

  • Add a HTTP header from the parameter of the method

      @GET("/tasks")
      Future<Task> getTasks(@Header("Content-Type") String contentType );
    
  • Add staitc HTTP headers

      @GET("/tasks")
      @Headers(<String, dynamic>{
          "Content-Type" : "application/json",
          "Custom-Header" : "Your header"
      })
      Future<Task> getTasks();
    

Error Handling #

catchError(Object) should be used for capturing the exception and failed response. You can get the detailed response info from DioError.response.

 client.getTask("2").then((it){
   logger.i(it);
 }).catchError((Object obj) {
    // non-200 error goes here.
    switch (obj.runtimeType) {
      case DioError:
        // Here's the sample to get the failed response error code and message
        final res = (obj as DioError).response;
        logger.e("Got error : ${res.statusCode} -> ${res.statusMessage}");
        break;
      default:
    }
  });

}

Multiple endpoints support #

If you want to use multiple endpoints to your RestClient, you should pass your base url when you initiate RestClient. Any value defined in RestApi will be ignored.

@RestApi(baseUrl: "this url will be ignored if baseUrl is passed")
abstract class RestClient {
  factory RestClient(Dio dio, {String baseUrl}) = _RestClient;
}

final client = RestClient(dio, baseUrl: "your base url");

If you want to use the base url from dio.option.baseUrl, which has lowest priority, please don't pass any parameter to RestApi annotation and RestClient's structure method.

Changelog #

1.3.4 #

  • Add dart json mapper deserialize support

1.3.3 #

  • [BERAKING CHANGE] Change Part's params to named parameters from optional ones.

1.3.2 #

  • Add contentType to Part annotaion

1.3.1 #

  • Add custom options support to each request
  • fix #132

1.3.0 #

  • Bumped retrofit's SDK requirement to >=2.6.0

1.2.0 #

  • Add HttpReponse to handle original response

1.1.0 #

  • [BREAKING CHANGE] Add new Part annotation to send multipart/form-data request. Field will not be used in the future, please use Part instead.

1.0.1 #

  • Add dio response type support

1.0.0 #

  • Support dio 3.0

0.6.3 #

  • Added autoCastResponse option to RestApi and all Method annotations (default : true)
  • Added auto_cast_response to builder options.
    • Users can specify this in build.yaml as global default

      targets:
        $default:
          sources: ['lib/**']
          builders:
            retrofit_generator|retrofit:
              enabled: true
              options:
                auto_cast_response: true
      
      

0.6.2 #

  • fix: fix bad cast exception (#47)
  • add CancelToken, SendProgress, and ReceiveProgress (#46)

0.6.0 #

  • [BREAKING CHANGE] only works dart 2.2.2 and above
  • Added support to multiple clients with different base urls.

0.5.0 #

  • Fixed analysis report use retrofit.dart instead of http.dart and dio.dart to import classes

0.4.3 #

  • Updated docs and sample code

0.2.3 #

  • Added optional parameter fileName in @Field annotation for custom file name

0.2.2 #

  • add example

0.2.0 #

  • Added @Extra to pass extra options to dio requests, response, transformer and interceptors.

    Example :

    @http.POST('/path/')
    @dio.Extra({'my_key':'my_value'})
    Future<String>> myMethod();
    
  • Fixed general dart style and code conventions

  • Automatically null check with ArgumentError.checkNotNull for required parameters

  • Now SuperClasses can use forwarding/redirecting constructors instead of static instance() method

    Example :

    @RestApi(baseUrl: "https://httpbin.org/")
    abstract class RestClient {
        /// Forwarding constructor
        factory RestClient([Dio dio]) = _RestClient;
    }
    

0.1.0 #

  • fix health issues

0.0.1 #

  • init

example/lib/example.dart

import 'dart:convert';

import 'package:json_annotation/json_annotation.dart';
import 'package:retrofit/retrofit.dart';
import 'package:dio/dio.dart' hide Headers;
import 'dart:io';
import 'package:http_parser/http_parser.dart' show MediaType;

part 'example.g.dart';

@RestApi(baseUrl: "https://5d42a6e2bc64f90014a56ca0.mockapi.io/api/v1/")
abstract class RestClient {
  factory RestClient(Dio dio, {String baseUrl}) = _RestClient;

  @GET("/tags")
  Future<List<String>> getTags();

  @GET("/tasks")
  Future<List<Task>> getTasks();

  @GET("/tasks/{id}")
  Future<Task> getTask(@Path("id") String id);

  @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")
  Future<void> createNewTaskFromFile(@Part() File file);

  @Headers(<String, String>{"accept": "image/jpeg"})
  @GET("http://httpbin.org/image/jpeg")
  @DioResponseType(ResponseType.bytes)
  Future<List<int>> getFile();

  @POST("http://httpbin.org/post")
  @FormUrlEncoded()
  Future<String> postUrlEncodedFormData(@Field() String hello);

  @HEAD('/')
  Future<String> headRequest();

  @HEAD('/')
  Future headRquest2();

  @HEAD('/')
  Future<HttpResponse> headRquest3();

  @GET("/task/group")
  Future<List<TaskGroup>> grouppedTaskByDate();

  @GET("/task")
  Future<HttpResponse<List<Task>>> getTasksWithReponse();

  @DELETE("/tasks/{id}")
  Future<HttpResponse<void>> deleteTaskWithResponse(@Path() String id);

  @POST("/post")
  Future<String> postFormData(@Part() Task task, {@Part() File file});

  @POST("/post")
  Future<String> postFormData2(@Part() List<Map<String, dynamic>> task,
      @Part(contentType: 'application/json') File file);

  @POST("/post")
  Future<String> postFormData3(
      {@Part(value: "customfiles", contentType: 'application/json')
          List<File> files,
      @Part()
          File file});

  @POST("/post")
  Future<String> postFormData4(@Part() List<Task> tasks, @Part() File file);

  @POST("/post")
  Future<String> postFormData5(
    @Part() List<Task> tasks,
    @Part() Map<String, dynamic> map,
    @Part() int a, {
    @Part() bool b,
    @Part() double c,
    @Part() String d,
  });

  @GET('/demo')
  Future<String> queries(@Queries() Map<String, dynamic> queries);

  @GET("/get")
  Future<String> namedExample(@Query("apikey") String apiKey,
      @Query("scope") String scope, @Query("type") String type,
      {@Query("from") int from});

  @POST("/postfile")
  @Headers(<String, dynamic>{
    "Content-Type": "application/octet-stream",
    "Ocp-Apim-Subscription-Key": "abc"
  })
  Future<String> postFile({@Body() File file});

  @GET("")
  Future<String> testCustomOptions(@DioOptions() Options options);
}

@JsonSerializable()
class Task {
  String id;
  String name;
  String avatar;
  String createdAt;

  Task({this.id, this.name, this.avatar, this.createdAt});

  factory Task.fromJson(Map<String, dynamic> json) => _$TaskFromJson(json);
  Map<String, dynamic> toJson() => _$TaskToJson(this);
}

@JsonSerializable()
class TaskGroup {
  DateTime date;
  List<Task> todos;
  List<Task> completed;
  List<Task> inProgress;

  TaskGroup({this.date, this.todos, this.completed, this.inProgress});

  factory TaskGroup.fromJson(Map<String, dynamic> json) =>
      _$TaskGroupFromJson(json);
  Map<String, dynamic> toJson() => _$TaskGroupToJson(this);
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  retrofit: ^1.3.4

2. Install it

You can install packages from the command line:

with pub:


$ pub get

with Flutter:


$ flutter pub get

Alternatively, your editor might support pub get or flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:retrofit/retrofit.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
96
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
98
Learn more about scoring.

We analyzed this package on Jun 5, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.2
  • pana: 0.13.8-dev

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.6.0 <3.0.0
dio ^3.0.1 3.0.9
meta ^1.1.6 1.1.8
Transitive dependencies
charcode 1.1.3
collection 1.14.12
http_parser 3.1.4
path 1.7.0
source_span 1.7.0
string_scanner 1.0.5
term_glyph 1.1.0
typed_data 1.1.6