使用パッケージ
Dio と json_serializable を使ったHTTPリクエストコード部分を自動生成してくれるもの。
叩くAPIは openweathermap を使わせてもらう。
使い方
pubspec.yaml にパッケージを追加
dependencies:
json_annotation: ^4.8.1
dio: ^5.1.2
retrofit: ^4.0.1
dev_dependencies:
build_runner: ^2.4.4
json_serializable: ^6.7.0
retrofit_generator: ^7.0.1
HTTPリクエスト用の抽象クラスを用意
import 'package:dio/dio.dart';
import 'package:flutter_retrofit/utils/city_weather.dart';
import 'package:retrofit/retrofit.dart';
part 'rest_client.g.dart';
@RestApi(baseUrl: 'https://api.openweathermap.org/data/2.5')
abstract class RestClient {
factory RestClient(Dio dio, {String baseUrl}) = _RestClient;
@GET('/weather')
Future<City> getCityWeather(
@Query("lat") String lat,
@Query("lon") String lon,
@Query("appid") String appid,
@Query("units") String units,
@Query("lang") String lang
);
}
見たままではあるが、
@RestApi()
でベースとなるURLを定義して、
@GET()
で各APIの定義を行っていく。
@Query()
でクエリパラメータを記述することも可能。
生成後のファイルを参照してる部分もあるのでこの時点でエラーが出てるのは気にせず次に進む
コード生成
flutter pub run build_runner build
これを叩くことにより
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'rest_client.dart';
// **************************************************************************
// RetrofitGenerator
// **************************************************************************
// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers
class _RestClient implements RestClient {
_RestClient(
this._dio, {
this.baseUrl,
}) {
baseUrl ??= 'https://api.openweathermap.org/data/2.5';
}
final Dio _dio;
String? baseUrl;
@override
Future<City> getCityWeather(
String lat,
String lon,
String appid,
String units,
String lang,
) async {
const _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{
r'lat': lat,
r'lon': lon,
r'appid': appid,
r'units': units,
r'lang': lang,
};
final _headers = <String, dynamic>{};
final Map<String, dynamic>? _data = null;
final _result =
await _dio.fetch<Map<String, dynamic>>(_setStreamType<City>(Options(
method: 'GET',
headers: _headers,
extra: _extra,
)
.compose(
_dio.options,
'/weather',
queryParameters: queryParameters,
data: _data,
)
.copyWith(baseUrl: baseUrl ?? _dio.options.baseUrl)));
final value = City.fromJson(_result.data!);
return value;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||
requestOptions.responseType == ResponseType.stream)) {
if (T == String) {
requestOptions.responseType = ResponseType.plain;
} else {
requestOptions.responseType = ResponseType.json;
}
}
return requestOptions;
}
}
のようなメソッドが自動生成される。
(端折ってしまったが、getCityWeather()内の final value = City.fromJson(_result.data!);
で、 fromJson()
メソッドを使用してレスポンスをクラス形式に変換している。ここの変換にjson_serializableで用意したコードが必要となる。(json_serializableで用意するクラスとしては、特別なものではなく、Retrofitを使用しない場合(Dioをそのまま使用する場合)と同等のもので問題ない))
これを使用して下記のようなコードを書くと結果が取得できる。
// 東京タワーの緯度経度
final lat = '35.6579781';
final lon = '139.7433048';
final appid = 'xxxxxxxxxx';
final units = 'metric';
final lang = 'ja';
final dio = Dio();
final client = RestClient(dio);
final City city = await client.getCityWeather(lat, lon, appid, units, lang);
// とりあえず場所と天気だけ表示
print('CityWeathre: ${city.id}, ${city.name}, ${city.weather?[0].description}');
使ってみての感想
HTTPリクエスト部分と結果のマッピング部分を、
定義クラスだけ用意すれば自動生成してくれるのは便利。
だが、HTTPアクセスからマッピングまでが
final City city = await client.getCityWeather(lat, lon, appid, units, lang);
に隠蔽されているので、
定義するときに一部の記述を間違えたなどでうまく動かないときに、
どこが悪いのかを探すのがちょっと大変。