LoginSignup
3
2

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-04-07

こちらも合わせてご覧ください

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

簡単な特徴

  1. 新しいAPIごとに作成する必要があるのは、
    ・Repositoryのクラス(メソッド)
    ・Requestクラス
    ・RequestParameterクラス
    ・Response受け取るクラス
  2. Client.callから帰ってくるクラスが>のため都度呼び出し元でfromJsonをしてオブジェクトに変換しなければならない

実装

特徴を書くよりもコードを書いた方がわかりやすいと思うので、
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();
}

作成した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);
}
3
2
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
3
2