リファレンス
Reference

HTTPにリクエストする

URLをコピーする Twitterでシェアする Facebookでシェアする

HTTP に情報をリクエストして表示します。

HTTP メソッドを使って、情報をリクエストして表示

今回の例では、オープンソースの API である PokéAPi にリクエストして、ポケモン図鑑を作ってみましょう。

PokéAPI にはポケモンに関する様々な情報が用意されていて、必要なデータを簡単に取得できます。

ここでは、 PokéAPi に用意されているポケモンリソースの URI にアクセスして、ポケモンの正面画像を表示させます。

完成したアプリは、以下のようになります。

プラグインのインストール

pubspec.yamldependencies ブロックの中に、 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/';
}

次に、 httpget メソッドを使って、PokéAPi にリクエストを送り、取得できたレスポンスを response に格納します。

final response = await http.get(url);

この response の中身は、主に 3 つの情報パーツに区分されます。

  1. レスポンスステータスコード
  2. レスポンスヘッダ
  3. レスポンスボディ

http を使ってリクエストするときには、通信トラブルやサーバーエラーなどによって失敗することがあります。

そこで、 HTTP レスポンスステータスコードを確認して、リクエストが成功したかどうかを確認します。

このステータスコードは、responsestatusCode から取得できます。

基本的な形としては以下のようになります。

if (response.statusCode == 200) {
  // ステータスコード 200 はリクエストが成功した場合に返されるコード
  // HTTP リクエストに成功したときの処理
} else {
  // エラーハンドリングの処理
throw Exception('Failed to fetch image URL');
}

以下に記載していく処理は、すべて HTTP リクエストに成功したときの処理となります。

リクエストしたポケモンの情報は、レスポンスボディパーツにまとめられています。

そのため、 responsebody にアクセスしていきます。

取得したボディの情報は 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');
      }
    }
  }
}

参考リンク