[Flutter]RetrofitでポケモンAPIを叩く
はじめに
こんにちは!mobileコースのko_chaです。
先日PokeAPIなるものを発見したのでこれを叩いてみよう、という記事です。ポケモンに関する情報をいっぱい返してくれるAPIだそうです(僕はポケモンを一作もやったことがなくデータを見てもよく分かりませんが、、)
公式ドキュメント
まずは公式ドキュメントから。
ポケモン、わからないよ...
Type=タイプってことだけは分かりましたので、Typeを指定して返ってくるポケモンたちの名前を表示させてみたいと思います。
httpを使っても良いですが、せっかくなのでretrofitパッケージを使ってみます。
これまた公式ドキュメント。
loggerは使いませんでしたので削除。しかし初知りでしたがすごいですねこれ...
そろそろprintを使ったデバッグから卒業したいところ。AndroidStudioのデバッグもうまく使えていませんしね、
この通りに追加しまして
dependencies:
retrofit:
dio:
logger: //僕は削除しました。
dev_dependencies:
retrofit_generator:
build_runner:
json_serializable:
Define and Generate your APIの部分を改造していきます。
RestClient部分を作る
まずは通信の核となる部分。
エラーが出ていても大丈夫です。下記をターミナルで実行することで、いい感じに必要なファイルを作ってくれます。
flutter pub run build_runner build --delete-conflicting-outputs
baseUrlを書き換え、今回はタイプを指定したいので、url末尾を変えられるようにしました。({type}の部分)これも公式の下の方にのっていますのでご安心ください。
@RestApi(baseUrl: "https://pokeapi.co/api/v2")
abstract class RestClient {
factory RestClient(Dio dio, {String baseUrl}) = _RestClient;
@GET("/type/{type}")
Future<PokeType> getNamesWithType(@Path("type") String type);
}
modelを作る
Typeの中にTypePokemonのリスト"pokemon"があって、その中にintの"slot", Pokemonの"pokemon"、またこの"pokemon"の中にname,urlが入っている状態です。(複雑ですね...)
まずは一番下層のPokemon型を作成。
import 'package:json_annotation/json_annotation.dart';
part 'pokemon.g.dart';
@JsonSerializable(fieldRename: FieldRename.snake)
class Pokemon {
String? name;
String? url;
Pokemon({this.name, this.url});
factory Pokemon.fromJson(Map<String, dynamic> json) =>
_$PokemonFromJson(json);
Map<String, dynamic> toJson() => _$PokemonToJson(this);
}
次にTypePokemon
import 'package:json_annotation/json_annotation.dart';
import 'package:poke_api/model/pokemon.dart';
part 'type_pokemon.g.dart';
@JsonSerializable(fieldRename: FieldRename.snake)
class TypePokemon {
int? slot;
Pokemon? pokemon;
#省略
}
最後にType型のモデル。始め"Type"としていたのですが、Type classはもう既に存在してるよと怒られてしまったので、PokeTypeに変更です。
import 'package:json_annotation/json_annotation.dart';
import 'package:poke_api/model/type_pokemon.dart';
part 'type.g.dart';
@JsonSerializable(fieldRename: FieldRename.snake)
class PokeType {
int? id;
String? name;
List<TypePokemon>? pokemon;
#省略
}
Viewを作る
以下の文を書き換えます。
import 'package:logger/logger.dart';
import 'package:retrofit_example/example.dart';
import 'package:dio/dio.dart';
final logger = Logger();
void main(List<String> args) {
final dio = Dio(); // Provide a dio instance
dio.options.headers["Demo-Header"] = "demo header"; // config your dio headers globally
final client = RestClient(dio);
client.getTasks().then((it) => logger.i(it));
}
まずはmainの中身を関数化
Future<PokeType> fetchData() async {
var type = "ground"; //じめんタイプを指定。ここを変えれば他のタイプに。
final dio = Dio();
final client = RestClient(dio);
dio.options.headers["Demo-Header"] = "demo header";//なくてもOKでした。
final data = await client.getNamesWithType(type);
return data;
}
お次は簡単にViewを書いていきます
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("PokeApi"),
),
body: FutureBuilder<PokeType>(
future: fetchData(),//
builder: (BuildContext context, AsyncSnapshot<PokeType> snapshot) {
return ListView.builder(
//データ有ならListViewの長さを返ってきたデータの中のpokemon分の長さに。
//違ったら0に。
itemCount: snapshot.hasData ? snapshot.data!.pokemon!.length : 0,
itemBuilder: (context, index) {
final pokeData = snapshot.data!.pokemon![index].pokemon!;
return snapshot.hasData
? ListTile(
title: Text(pokeData.name!),
)
: const Center(
child: CircularProgressIndicator(),
);
},
);
},
),
);
}
よしよし。
最後に
作ってみると行数の少ないコードですが、意外と苦戦しました。知らないものを理解するのって難しいですね、、
アドベントカレンダー明日はKaoru Mataraiさんです!!!お楽しみに!