こちらも合わせてご覧ください
Flutter通信ライブラリ選定 ~ 選ばれたのはRetrofitでした ~
簡単な特徴
- 新しいAPIごとに作成する必要があるのは、
・Repositoryのクラス(メソッド)
・Requestクラス
・RequestParameterクラス
・Response受け取るクラス - Client.callから帰ってくるクラスがStringのため一度
<Map<String, dynamic>>
のJsonに変換し、さらに呼び出し元でfromJsonをしてオブジェクトに変換しなければならない - dioと似ているが少し楽になっている
実装
特徴を書くよりもコードを書いた方がわかりやすいと思うので、
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などのキーが固定で入っている場合を考慮)
httpをpubspec.yamlに追記する
具体的実装
リクエストを送るClient
client.dart
class Client extends BaseClient {
Future<Response> call(RequestBase request) {
var json = jsonEncode(request.parameter);
switch (request.apiMethod) {
case HttpMethod.post:
return post(request.url, headers: request.headers, body: json);
case HttpMethod.get:
return get(request.url, headers: request.headers);
case HttpMethod.put:
return put(request.url, headers: request.headers, body: json);
case HttpMethod.patch:
return patch(request.url, headers: request.headers, body: json);
case HttpMethod.delete:
return delete(request.url, headers: request.headers, body: json);
}
}
@override
Future<StreamedResponse> send(BaseRequest request) {
// TODO: implement send
throw UnimplementedError();
}
}
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);
try {
final response = await Client().call(request);
final data = json.decode(response.body) as Map<String, dynamic>;
return Result.success(LoginResponse.fromJson(data["base"]));
} catch (error) {
return Result.failure();
}
}
}
リクエスト等の基幹クラス
http_request_base.dart
// リクエストの基幹
abstract class HttpRequestBase {
HttpMethod get apiMethod;
String get path;
RequestParameter? get parameter;
String get method => apiMethod.name.toUpperCase();
Uri get url {
if (apiMethod == HttpMethod.get && parameter != null) {
return Uri.https("domain-hoge.com", path, parameter!.toJson());
}
return Uri.https("domain-hoge.com", path);
}
Map<String, String> get headers => { Content-Type: "applicatioin/json", "piyo": "hogehoge" };
}
enum HttpMethod {
post,
get,
put,
patch,
delete
}
リザルトクラス(上記同様に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 extends HttpRequestBase {
LoginRequest(this.loginParameter) : super();
final LoginRequestParameter loginParameter;
@override
HttpMethod get apiMethod => HttpMethod.post;
@override
String get path => "/fuga/piyo/login";
@override
RequestParameter? get parameter => loginParameter;
}
ログインリクエストのパラメータ(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);
}