3 Star 5 Fork 0

Trevor Wang / retrofit.dart

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MIT

Retrofit For Dart

retrofit retrofit_generator Pub Likes Testing

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.0.0 <5.0.0'
  logger: any  #for logging purpose
  json_annotation: ^4.8.1

dev_dependencies:
  retrofit_generator: '>=7.0.0 <8.0.0'   // required dart >=2.19
  build_runner: '>=2.3.0 <4.0.0'
  json_serializable: ^6.6.2

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);
}

then run the generator

# dart
dart pub run build_runner build

# flutter	
flutter pub run build_runner build

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

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 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;
}

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 original HTTP response

  @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 static HTTP headers
  import 'package:dio/dio.dart' hide 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((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:
    break;
  }
});

Relative API baseUrl

If you want to use a relative baseUrl value in the RestApi annotation of the RestClient, you need to specify a baseUrl in dio.options.baseUrl.

@RestApi(baseUrl: '/tasks')
abstract class RestClient {
  factory RestClient(Dio dio, {String baseUrl}) = _RestClient;

  @GET('{id}')
  Future<HttpResponse<Task>> getTask(@Path('id') String id);

  @GET('')
  Future<HttpResponse<List<Task>>> getTasks();
}

dio.options.baseUrl = 'https://5d42a6e2bc64f90014a56ca0.mockapi.io/api/v1';
final client = RestClient(dio);

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.

Multithreading (Flutter only)

If you want to parse models on a separate thread, you can take advantage of the compute function, just like Dio does when converting String data responses into json objects.

For each model that you use you will need to define 2 top-level functions:

FutureOr<Task> deserializeTask(Map<String, dynamic> json);
FutureOr<dynamic> serializeTask(Task object);

If you want to handle lists of objects, either as return types or parameters, you should provide List counterparts:

FutureOr<List<Task>> deserializeTaskList(Map<String, dynamic> json);
FutureOr<dynamic> serializeTaskList(List<Task> objects);

Finally, make sure you set your @RestApi to use the Parser.FlutterCompute parser:

@RestApi(parser: Parser.FlutterCompute)

E.g.

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

  @GET('/task')
  Future<Task> getTask();

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

  @POST('/task')
  Future<void> updateTasks(Task task);

  @POST('/tasks')
  Future<void> updateTasks(List<Task> tasks);
}

Task deserializeTask(Map<String, dynamic> json) => Task.fromJson(json);

List<Task> deserializeTaskList(List<Map<String, dynamic>> json) =>
    json.map((e) => Task.fromJson(e)).toList();

Map<String, dynamic> serializeTask(Task object) => object.toJson();

List<Map<String, dynamic>> serializeTaskList(List<Task> objects) =>
    objects.map((e) => e.toJson()).toList();

N.B. Avoid using Map values, otherwise multiple background isolates will be spawned to perform the computation, which is extremely intensive for Dart.

abstract class RestClient {
  factory RestClient(Dio dio, {String baseUrl}) = _RestClient;

  // BAD
  @GET('/tasks')
  Future<Map<String, Task>> getTasks();

  @POST('/tasks')
  Future<void> updateTasks(Map<String, Task> tasks);

  // GOOD
  @GET('/tasks_names')
  Future<TaskNames> getTaskNames();

  @POST('/tasks_names')
  Future<void> updateTasks(TaskNames tasks);
}

TaskNames deserializeTaskNames(Map<String, dynamic> json) =>
    TaskNames.fromJson(json);

@JsonSerializable
class TaskNames {
  const TaskNames({required this.taskNames});

  final Map<String, Task> taskNames;

  factory TaskNames.fromJson(Map<String, dynamic> json) =>
      _$TaskNamesFromJson(json);
}

Hide generated files

For the project not to be confused with the files generated by the retrofit you can hide them.

Android studio

File -> Settings -> Editor -> File Types

Add "ignore files and folders"

*.g.dart

Credits

  • JetBrains. Thanks for providing the great IDE tools.

Contributors ✨

Thanks goes to these wonderful people:

Contributions of any kind welcome!

Activities

Alt

MIT License Copyright (c) 2019 Trevor Wang 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.

简介

retrofit.dart is an dio client generator using source_gen and inspired by Chopper and Retrofit. 展开 收起
Dart 等 6 种语言
MIT
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Dart
1
https://gitee.com/trevor/retrofit.dart.git
git@gitee.com:trevor/retrofit.dart.git
trevor
retrofit.dart
retrofit.dart
master

搜索帮助