CONTENT
ここから
前の節では、Flutterプロジェクトに http パッケージを追加しました。
ここから、いよいよFlutterアプリからPokeAPIへアクセスします。
この節では、まずAPIからデータを取得するところまでを作ります。
まだ、きれいなカードUIにはしません。
JSONを細かく整理する作業も、次の節で行います。
今回は、Flutterアプリが外部APIにアクセスして、データを受け取れる状態を作ることが目的です。
この節で作ること
今回作るのは、次のような流れです。
| 作業 | 内容 |
|---|---|
| APIのURLを用意する | PokeAPIのポケモン取得URLを使う |
http.get() を使う | FlutterからAPIへリクエストを送る |
| レスポンスを確認する | 通信が成功したか確認する |
| 取得結果を画面に表示する | 返ってきたJSON文字列を画面に出す |
今回は例として、ピカチュウのデータを取得します。
https://pokeapi.co/api/v2/pokemon/25
忙しい方はここだけ見て
APIからデータを取得する基本形は、次のコードです。
/**
* PokeAPIからポケモンデータを文字列として取得する関数。
*
* 入力: なし
* 出力: APIから返ってきたJSON文字列
*/
Future<String> fetchPokemonData() async {
final Uri url = Uri.https(
'pokeapi.co',
'/api/v2/pokemon/25',
);
final http.Response response = await http.get(url);
if (response.statusCode != 200) {
throw Exception('API通信に失敗しました');
}
return response.body;
}
この関数を呼び出すことで、FlutterアプリからPokeAPIのデータを取得できます。
API通信の基本イメージ
FlutterアプリからAPIを呼び出す流れは、とてもシンプルです。
Flutterアプリ
↓ リクエスト
PokeAPI
↓ レスポンス
JSONデータ
今回使う http.get() は、「このURLのデータをください」とお願いする処理です。
PokeAPIからは、ポケモンの名前、画像、タイプ、高さ、重さなどが入ったJSONデータが返ってきます。
ただし、この節ではJSONの中身を細かく整理しません。
まずは「取得できた」という感覚をつかみます。
手順1:main.dartを開く
lib/main.dart を開きます。
前の節で追加した、次のimportがあることを確認してください。
import 'package:http/http.dart' as http;
なければ追加します。
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
手順2:APIからデータを取得する関数を作る
まず、main.dart の中に次の関数を追加します。
/**
* PokeAPIからピカチュウのデータを取得する関数。
*
* 入力: なし
* 出力: APIから返ってきたJSON文字列
*/
Future<String> fetchPokemonData() async {
final Uri url = Uri.https(
'pokeapi.co',
'/api/v2/pokemon/25',
);
final http.Response response = await http.get(url);
if (response.statusCode != 200) {
throw Exception('API通信に失敗しました');
}
return response.body;
}
ここで大事なのは、次の3つです。
| コード | 意味 |
|---|---|
Uri.https() | APIのURLを作る |
await http.get(url) | APIへアクセスする |
response.body | APIから返ってきた中身を取り出す |
response.body には、JSON形式の文字列が入っています。
asyncとawaitについて
API通信は、すぐに結果が返ってくるとは限りません。
インターネットを通して外部サーバーへアクセスするため、少し時間がかかります。
そこで使うのが、async と await です。
Future<String> fetchPokemonData() async {
final http.Response response = await http.get(url);
return response.body;
}
ざっくり言うと、次のような意味です。
| 書き方 | 意味 |
|---|---|
Future<String> | あとでStringが返ってくる |
async | 時間のかかる処理を書く関数 |
await | 結果が返ってくるまで待つ |
API通信では、この形をよく使います。
手順3:画面に表示する準備をする
今回は、ボタンを押したらAPI通信を行い、取得したJSON文字列を画面に表示します。
まず、状態を持てるように StatefulWidget を使います。
class PokemonHomePage extends StatefulWidget {
const PokemonHomePage({super.key});
@override
State<PokemonHomePage> createState() => _PokemonHomePageState();
}
次に、状態管理用のクラスを作ります。
class _PokemonHomePageState extends State<PokemonHomePage> {
String _pokemonData = 'まだデータを取得していません';
/**
* ボタンを押したときにPokeAPIからデータを取得する関数。
*
* 入力: なし
* 出力: 画面に表示する文字列を更新する
*/
Future<void> _loadPokemonData() async {
final String result = await fetchPokemonData();
setState(() {
_pokemonData = result;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('ポケモン図鑑'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: <Widget>[
ElevatedButton(
onPressed: _loadPokemonData,
child: const Text('APIからデータを取得する'),
),
const SizedBox(height: 16),
Expanded(
child: SingleChildScrollView(
child: Text(_pokemonData),
),
),
],
),
),
);
}
}
これで、ボタンを押すとPokeAPIにアクセスし、返ってきたJSON文字列が画面に表示されます。
今回の完成コード
lib/main.dart を次のようにします。
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
/**
* アプリの起点になる関数。
*
* 入力: なし
* 出力: MyAppを起動する
*/
void main() {
runApp(const MyApp());
}
/**
* PokeAPIからピカチュウのデータを取得する関数。
*
* 入力: なし
* 出力: APIから返ってきたJSON文字列
*/
Future<String> fetchPokemonData() async {
final Uri url = Uri.https(
'pokeapi.co',
'/api/v2/pokemon/25',
);
final http.Response response = await http.get(url);
if (response.statusCode != 200) {
throw Exception('API通信に失敗しました');
}
return response.body;
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
/**
* アプリ全体のUIを構築する関数。
*
* 入力: BuildContext
* 出力: MaterialApp
*/
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ポケモン図鑑',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.red),
useMaterial3: true,
),
home: const PokemonHomePage(),
);
}
}
class PokemonHomePage extends StatefulWidget {
const PokemonHomePage({super.key});
@override
State<PokemonHomePage> createState() => _PokemonHomePageState();
}
class _PokemonHomePageState extends State<PokemonHomePage> {
String _pokemonData = 'まだデータを取得していません';
/**
* PokeAPIからデータを取得し、画面表示用の文字列を更新する関数。
*
* 入力: なし
* 出力: _pokemonDataを更新して画面を再描画する
*/
Future<void> _loadPokemonData() async {
final String result = await fetchPokemonData();
setState(() {
_pokemonData = result;
});
}
/**
* ポケモン図鑑のトップ画面を構築する関数。
*
* 入力: BuildContext
* 出力: Scaffold
*/
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('ポケモン図鑑'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: <Widget>[
ElevatedButton(
onPressed: _loadPokemonData,
child: const Text('APIからデータを取得する'),
),
const SizedBox(height: 16),
Expanded(
child: SingleChildScrollView(
child: Text(_pokemonData),
),
),
],
),
),
);
}
}
実行して確認する
ターミナルでアプリを起動します。
flutter run
画面に次のボタンが表示されます。
APIからデータを取得する
ボタンを押して、長いJSON文字列が表示されれば成功です。
表示される内容の中には、次のような情報が含まれます。
"name":"pikachu"
この文字が見えたら、PokeAPIからピカチュウのデータを取得できています。
コードの流れを確認する
今回のコードは、次の順番で動いています。
1. ボタンを押す
2. _loadPokemonData() が呼ばれる
3. fetchPokemonData() が呼ばれる
4. http.get() でPokeAPIへアクセスする
5. response.body でJSON文字列を受け取る
6. setState() で画面を更新する
7. 取得したデータが画面に表示される
ポイントは、setState() です。
Flutterでは、画面に表示している値を変えたいときに setState() を使います。
setState(() {
_pokemonData = result;
});
この処理によって、画面が更新されます。
よくあるエラー
1. httpが見つからない
次のようなエラーが出る場合があります。
Target of URI doesn't exist: 'package:http/http.dart'
この場合は、httpパッケージが追加されていません。
次のコマンドを実行します。
flutter pub add http
そのあと、もう一度実行します。
flutter pub get
2. Androidで通信できない
Androidエミュレーターで通信がうまくいかない場合、まずインターネットに接続されているか確認してください。
通常、HTTPS通信であればそのまま動くことが多いです。
今回のURLも https:// から始まっています。
https://pokeapi.co/api/v2/pokemon/25
それでも動かない場合は、エミュレーターを再起動してから、もう一度試します。
3. ボタンを押しても画面が変わらない
この場合は、setState() の中で _pokemonData を更新しているか確認します。
正しい例です。
setState(() {
_pokemonData = result;
});
悪い例です。
_pokemonData = result;
このように setState() なしで値だけ変えても、画面が更新されないことがあります。
今回はまだやらないこと
この節では、APIからデータを取得するところまでを行いました。
ただし、次のことはまだ行いません。
| まだやらないこと | 理由 |
|---|---|
| JSONをMapに変換する | 次の節で扱う |
| ポケモン名だけを取り出す | JSON変換後に行う |
| 画像を表示する | 画像URLの取得後に行う |
| ローディング表示 | 後の節で扱う |
| エラー表示の作り込み | 後の節で扱う |
一気に作ろうとすると、どこでつまずいたのか分かりにくくなります。
まずは、APIからデータを取れる。
そこだけを、確実に押さえます。
確認問題
問1
FlutterからAPIへGETリクエストを送るときに使うコードはどれですか?
答え。
final http.Response response = await http.get(url);
問2
APIから返ってきた中身を取り出すには、どのプロパティを使いますか?
答え。
response.body
問3
通信が成功したかどうかを確認するために使う値は何ですか?
答え。
response.statusCode
問4
画面の表示内容を更新するときに使うFlutterの関数は何ですか?
答え。
setState()
まとめ
この節では、FlutterアプリからPokeAPIへアクセスし、APIデータを取得しました。
今回できるようになったことは、次の3つです。
| できるようになったこと | 内容 |
|---|---|
| APIのURLを作る | Uri.https() を使う |
| APIへアクセスする | http.get() を使う |
| 取得結果を画面に出す | response.body をTextで表示する |
この段階では、JSON文字列がそのまま表示されているため、まだ少し読みにくいです。
次の節では、このJSON文字列をFlutterで扱いやすい形に変換します。
つまり、Map に変換して、ポケモンの名前や画像URLなどを取り出せるようにしていきます。