15
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Flutter通信ライブラリ選定 ~ 選ばれたのはRetrofitでした ~

Last updated at Posted at 2022-04-07

Http通信を簡単に行うためのライブラリ選定を行いました

結論から言うと dio + Retrofit を採用しました
Flutter Dartの筆者は経験が薄い人間であることをご認識ください!!(コメントやLGTMいただけると嬉しいです!)
こちらの記事には dio + Retrofit での実装のみ記載します。(他の方法については個別記事を作成しましたのでそちらをご確認いただけると幸いです。)

選定対象のライブラリと簡単な特徴

1: dioのみ → Flutter : dioのみでの通信基盤作成

・httpの次に利用されているライブラリ
・リクエストクラスを個別に作成する必要あり

2: dio + Retrofit

・Retrofitがそもそもdioと一緒に使うことを想定している
・Android KotlinのRetrofit同様に個別にリクエストクラスを作成する必要はない
・レスポンスをオブジェクトで受け取ることができる ← これが他のライブラリと異なり、選ばれた理由 そして必要なクラスやファイルが少ない

3: http → Flutter : httpでの通信基盤作成

・公式が開発したライブラリである
・正直あまりdioと使い勝手に差がないように感じた。(細かいところを見たら差があるかもしれませんが、、、)

4: chopper → Flutter : chopperでの通信基盤作成

・httpをラップしたライブラリである
・retrofitを意識して作られているため、実装感は似ているがgenerateされるのは少し違う感じがあった

実装

特徴を書くよりもコードを書いた方がわかりやすいと思うので、
RepositoryからAPIを呼び出しレスポンスオブジェクトを返すところまで記載します。

前提
  1. pubspec.yamlにfreezedfreezed_annotationjson_serializablebuild_runnerを固定で入れておいてください。
  2. 叩く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();

  @override
  Interceptors get interceptors {
    final interceptor = super.interceptors;
    interceptor.add(
      InterceptorsWrapper(
        onResponse: (response, handler) {
          var res = response;
          if (response.statusCode == 200 && res.data['base'] != null) {
            res.data = res.data['base'];
          }
          return handler.next(res);
        }
      )
    );
    return interceptor;
  }
}

サービスごとに作成する(generatedなので下記ファイルを作成したら $flutter pub run build_runner buildをする)

auth_service.dart
part 'auth_service.g.dart'

@RestApi()
abstract class AuthService {
  factory Client(MyDio dio) = _AuthService;

  @POST("/fuga/piyo/login")
  Future<LoginResponse> login(@Body() LoginRequestParameter parameter);
}

RepositoryからAuthServiceのloginを呼び出す

login_repository.dart
abstract class LoginDataSource {
  Future<Result<LoginResponse>> login(LoginModel loginModel);
}

class LoginRepository extends LoginDataSource {
  @override
  Future<Result<LoginResponse>> login(LoginModel loginModel) async {
    // LoginRequestParameterとResponseオブジェクトはfreezedとjson_serializableを利用してfromJsonのfactoryを作成しておくこと
    final parameter = LoginRequestParameter(id: loginModel.id, pass: loginModel.pass);

    try {
      return Result.success(await AuthService(MyDio()).login(parameter));
    } catch (error) {
      return Result.failure();
    }
}

リザルトクラス(上記同様に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>;
}

ログインリクエストのパラメータ(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);
}
15
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?