こちらも合わせてご覧ください
Flutter通信ライブラリ選定 ~ 選ばれたのはRetrofitでした ~
簡単な特徴
- 新しいAPIごとに作成する必要があるのは、
・Repositoryのクラス(メソッド)
・Requestクラス
・RequestParameterクラス
・Response受け取るクラス - Client.callから帰ってくるクラスが>のため都度呼び出し元でfromJsonをしてオブジェクトに変換しなければならない
実装
特徴を書くよりもコードを書いた方がわかりやすいと思うので、
RepositoryからAPIを呼び出しレスポンスオブジェクトを返すところまで記載します。
前提
- pubspec.yamlに
freezed
とfreezed_annotation
とjson_serializable
とbuild_runner
を固定で入れておいてください。 - 叩くAPIとしては、下記のようなログインAPIを例として挙げます
・Url →https://domain-hoge.com/fuga/piyo/login
・Method →POST
・Header →{ Content-Type: "applicatioin/json", "piyo": "hogehoge" }
・BodyParameter →{ id: String, pass: String}
・ResponseJson →{ base: { bar: String, toto: String } }
(レスポンスにbaseなどのキーが固定で入っている場合を考慮)
dioのみ(dioをpubspec.yamlに追記する)
カスタムDioクラス
my_dio.dart
class MyDio with DioMixin implements Dio {
@override
set options(BaseOptions _options) {
_options.baseUrl = "https://domain-hoge.com";
_options.headers = {
"Content-Type": "application/json",
"piyo": "hogehoge"
};
super.options = _options;
}
@override
HttpClientAdapter get httpClientAdapter => DefaultHttpClientAdapter();
}
作成したDioを使ってリクエストを送るClient
client.dart
class Client {
// APIRequestクラスとResultクラス、APIMethodは後述する
static Future<Result<Map<String, dynamic>>> call(APIRequest request) async {
final dio = MyDio();
dio.options = BaseOptions(
method: request.method.name.toUpperCase(),
);
Response response;
try {
if (request.method == APIMethod.get) {
response = await dio.request(
request.path,
queryParameters: request.parameter?.toJson(),
);
} else {
response = await dio.request(
request.path,
data: request.parameter?.toJson(),
);
}
} catch (e) {
return Result.failure();
}
return Result.success(response.data["base"]);
}
}
RepositoryからClient.callを呼び出す
login_repository.dart
abstract class LoginDataSource {
Future<Result<LoginResponse>> login(LoginModel loginModel);
}
class LoginRepository extends LoginDataSource {
@override
Future<Result<LoginResponse>> login(LoginModel loginModel) async {
final parameter = LoginRequestParameter(id: loginModel.id, pass: loginModel.pass);
final request = LoginRequest(parameter);
final result = await Client.call(request);
Result<LoginResponse> response = Result.failure();
result.when(
success: (Map<String, dynamic> json) {
response = Result.success(LoginResponse.fromJson(json));
},
failure: () {
response = Result.failure();
}
);
return response;
}
}
リクエスト等の基幹クラス
http_request_base.dart
// リクエストの基幹
abstract class HttpRequestBase {
HttpMethod get method;
String get path;
RequestParameter? parameter;
}
enum HttpMethod {
post,
get,
put,
patch,
delete
}
// リクエストパラメータのBase
abstract class RequestParameter {
Map<String, dynamic> toJson();
}
リザルトクラス(上記同様にflutter pub run build_runner build ~ する)
result.dart
part 'result.freezed.dart';
@freezed
class Result<T> with _$Result<T> {
const factory Result.success(T value) = Success<T>;
const factory Result.failure() = Failure<T>;
}
ログインリクエスト
login_request.dart
class LoginRequest implements HttpRequestBase {
LoginRequest(this.parameter);
@override
APIMethod method = APIMethod.post;
@override
String path = "/fuga/piyo/login";
@override
RequestParameter? parameter;
}
ログインリクエストのパラメータ(freezedなので下記ファイルを作成したら、$flutter pub run build_runner build --delete-conflicting-outputs
を実行する)
login_request_parameter.dart
part 'login_request_parameter.freezed.dart';
part 'login_request_parameter.g.dart';
@freezed
class LoginRequestParameter with _$LoginRequestParameter {
factory LoginRequestParameter({
required String id,
required String pass,
}) = _LoginRequestParameter;
factory LoginRequestParameter.fromJson(Map<String, dynamic> json) => _$LoginRequestParameterFromJson(json);
}
レスポンス受け取るクラス(上記同様にflutter pub run build_runner build ~ する)
login_response.dart
part 'login_response.freezed.dart';
part 'login_response.g.dart';
@freezed
class LoginResponse with _$LoginResponse {
factory LoginResponse({
required String bar,
required String toto,
}) = _LoginResponse;
factory LoginResponse.fromJson(Map<String, dynamic> json) => _$LoginResponseFromJson(json);
}