LoginSignup
1
1

More than 1 year has passed since last update.

Flutter : httpでの通信基盤作成

Last updated at Posted at 2022-04-07

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

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

簡単な特徴

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

実装

特徴を書くよりもコードを書いた方がわかりやすいと思うので、
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などのキーが固定で入っている場合を考慮)
    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);
}
1
1
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
1
1