HTTP に情報をリクエストして表示します。
HTTP メソッドを使って、情報をリクエストして表示
今回の例では、オープンソースの API である PokéAPi にリクエストして、ポケモン図鑑を作ってみましょう。
PokéAPI にはポケモンに関する様々な情報が用意されていて、必要なデータを簡単に取得できます。
ここでは、 PokéAPi に用意されているポケモンリソースの URI にアクセスして、ポケモンの正面画像を表示させます。
完成したアプリは、以下のようになります。

プラグインのインストール
pubspec.yaml
の dependencies
ブロックの中に、
http: ^0.12.2
を記述します。
例えば、
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.0
http: ^0.12.2
のようになります。
http
の ^0.12.2
はプラグインのバージョンです。
こちら に最新のバージョンが載っていますので確認してください。
そして、ターミナルを開き、
$ flutter packages get
とします。これでインストールは完了です。
使い方
まず、ライブラリを使用したい .dart
ファイルの一番上の方に、
import 'package:http/http.dart' as http;
import 'dart:convert';
と書き、HTTP にリクエストするためのライブラリ http
を使えるようにします。
ポケモン画像のデータを取得
State
の中に、 List 型のデータ構造 imageUrls
を定義し、取得したポケモン画像データにアクセスできる URL アドレスを納められるようにします。
List<String> imageUrls;
各ポケモンの情報は、 http://pokeapi.co/api/v2/pokemon/{id or name}/
にあります。
{}で閉じられている中に、各ポケモンに割り当てられている id またはポケモンの英語名でリクエストすることができます。
例えば、 http://pokeapi.co/api/v2/pokemon/pikachu/
や http://pokeapi.co/api/v2/pokemon/25/
になります。
今回の例ではポケモン図鑑を作ることを目標にしているので、複数のポケモンのデータをリクエストする必要があります。
そのため、 for ループ文を作って、カウンターとして利用する変数 index
をポケモンの id として使います。
こうすることで、 1 から maxNumber
(ここでは定数 385) までのポケモンのデータをリクエストできます。
for (int index = 1; index <= maxNumber; index++) {
final url = 'http://pokeapi.co/api/v2/pokemon/$index/';
}
次に、 http
の get
メソッドを使って、PokéAPi にリクエストを送り、取得できたレスポンスを response
に格納します。
final response = await http.get(url);
この response
の中身は、主に 3 つの情報パーツに区分されます。
- レスポンスステータスコード
- レスポンスヘッダ
- レスポンスボディ
http
を使ってリクエストするときには、通信トラブルやサーバーエラーなどによって失敗することがあります。
そこで、 HTTP レスポンスステータスコードを確認して、リクエストが成功したかどうかを確認します。
このステータスコードは、response
の statusCode
から取得できます。
基本的な形としては以下のようになります。
if (response.statusCode == 200) {
// ステータスコード 200 はリクエストが成功した場合に返されるコード
// HTTP リクエストに成功したときの処理
} else {
// エラーハンドリングの処理
throw Exception('Failed to fetch image URL');
}
以下に記載していく処理は、すべて HTTP リクエストに成功したときの処理となります。
リクエストしたポケモンの情報は、レスポンスボディパーツにまとめられています。
そのため、 response
の body
にアクセスしていきます。
取得したボディの情報は JSON 形式となっているため、 Map (連想配列:「キー:値」)のデータ構造へと変換します。
var jsonResponse = json.decode(response.body);
そして、今回のポケモン図鑑作成に使用する画像 URL アドレスは、以下の階層に位置しています。
{
...
'sprites': {
'back_default': "https://..省略...png"
'back_female': null
'back_shiny': "https://..省略...png"
'back_shiny_female': null
'front_default': "https://..省略...png"
'front_female': null
'front_shiny': "https://..省略...png"
'front_shiny_female': null
}
...
}
ポケモンの正面画像を取得するためには、以下のようにアクセスしていきます。
final imageUrl = jsonResponse['sprites']['front_default'];
最後に、取得した URL が格納された imageURL
を、最初に用意した List 型のデータ構造 imageUrls
に後ろから順番に追加( add
)していきます。
if (response.statusCode == 200) {
var jsonResponse = json.decode(response.body);
final imageUrl = jsonResponse['sprites']['front_default'];
setState(() {
imageUrls.add(imageUrl);
});
}
実際使うときは、 json_serializable
を使うと、もっとスマートに書けます。
詳しくは、 こちら を参照してください。
Future<void> fetchImageUrls() async {
for (int index = 1; index <= maxNumber; index++) {
final url = 'http://pokeapi.co/api/v2/pokemon/$index/';
final response = await http.get(url);
if (response.statusCode == 200) {
var jsonResponse = json.decode(response.body);
final imageUrl = jsonResponse['sprites']['front_default'];
setState(() {
imageUrls.add(imageUrl);
});
} else {
throw Exception('Failed to fetch image URL');
}
}
}
ポケモン画像の表示
最後に、取得したポケモン画像データの一覧を表示していきます。
ここで、たくさんの数(または無限)の要素が入ったリストを表示する GridView.builder
を使って、横にポケモン 3 体ずつ順番に並べていきます。
また、それぞれの画像を表示する方法として、外部パッケージの cached_network_image を使っています。
このパッケージを使うと、画像をキャッシュしてくれたり、画像データを取得するまでの時間に表示させるローディングインディケーターや、 エラーが起きたときに表示させるアイコンなども簡単に実装できます。

GridView.builder(
itemCount: imageUrls.length, // 表示するアイテム数
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, // 1行に表示するアイテム数を 3 に設定
),
itemBuilder: (context, int index) {
return SizedBox(
height: 100,
child: CachedNetworkImage(
imageUrl: imageUrls[index],
placeholder: (context, url) => Center(
child: new CircularProgressIndicator(),
),
errorWidget: (context, url, error) => new Icon(Icons.error),
),
);
},
),
全体のコード例は、以下のようになります。
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:cached_network_image/cached_network_image.dart';
const maxNumber = 385;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'わかりやすい'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> imageUrls;
@override
void initState() {
imageUrls = [];
fetchImageUrls();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: GridView.builder(
itemCount: imageUrls.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
itemBuilder: (context, int index) {
return SizedBox(
height: 100,
child: CachedNetworkImage(
imageUrl: imageUrls[index],
placeholder: (context, url) => Center(
child: new CircularProgressIndicator(),
),
errorWidget: (context, url, error) => new Icon(Icons.error),
),
);
},
),
);
}
Future<void> fetchImageUrls() async {
for (int index = 1; index <= maxNumber; index++) {
final url = 'http://pokeapi.co/api/v2/pokemon/$index/';
final response = await http.get(url);
if (response.statusCode == 200) {
var jsonResponse = json.decode(response.body);
final imageUrl = jsonResponse['sprites']['front_default'];
setState(() {
imageUrls.add(imageUrl);
});
} else {
throw Exception('Failed to fetch image URL');
}
}
}
}