TEXTBOOK SECTION / AI LEARNING

FlutterでJSONをMapに変換する

Flutterアプリケーション開発概論の「Flutter API連携入門|ポケモン図鑑アプリの作り方」より、FlutterでJSONをMapに変換するを解説。生成AI、AI活用、DX、業務改善を実践しながら学べるオンライン教材です。

9Flutter API連携入門|ポケモン図鑑アプリの作り方Flutter / iOS / Android / MacOS / Windows / 基礎から学ぶ / 開発 / アプリ開発

OVERVIEW

この節で学べること

概要を表示する
項目内容
教材名Flutterアプリケーション開発概論
Flutter API連携入門|ポケモン図鑑アプリの作り方
FlutterでJSONをMapに変換する
カテゴリFlutter / iOS / Android / MacOS / Windows / 基礎から学ぶ / 開発 / アプリ開発
学習内容生成AI、AI活用、DX、業務改善を実践しながら理解するための教材です。

TABLE OF CONTENTS

目次

CONTENT

ここから

前の節では、FlutterアプリからPokeAPIへアクセスし、response.body を画面に表示しました。

ただ、表示された内容はかなり長いJSON文字列でした。

そのままでは、ポケモンの名前だけ、画像URLだけ、高さだけ、という形で取り出すことができません。

そこで今回は、APIから返ってきたJSON文字列を、Flutterで扱いやすい Map に変換します。

Dartでは、JSON文字列を変換するときに dart:convertjsonDecode() を使います。公式ドキュメントでも、jsonDecode() は文字列を解析し、JSONオブジェクトとして返す関数として説明されています。


この節で作ること

この節では、次の流れを作ります。

作業内容
dart:convert を読み込むJSON変換用の標準ライブラリを使う
jsonDecode() を使うJSON文字列をDartのデータに変換する
Map<String, dynamic> にするキーで値を取り出せる形にする
ポケモン名を取り出すname の値を画面に表示する

今回は、まずピカチュウの名前 pikachu を取り出せる状態にします。


忙しい方はここだけ見て

JSON文字列をMapに変換する基本形は、次のコードです。

import 'dart:convert';

final Map<String, dynamic> data =
    jsonDecode(response.body) as Map<String, dynamic>;

final String name = data['name'] as String;

jsonDecode(response.body) によって、APIから返ってきたJSON文字列をDartで扱えるデータに変換できます。


JSONとMapの違い

JSONは、APIから返ってくる文字列形式のデータです。

たとえば、次のような形です。

{
  "id": 25,
  "name": "pikachu",
  "height": 4,
  "weight": 60
}

このままだと、Flutter側ではただの文字列に近い状態です。

そこで、Dartの Map に変換します。

{
  'id': 25,
  'name': 'pikachu',
  'height': 4,
  'weight': 60,
}

Mapにすると、キーを指定して値を取り出せます。

final String name = data['name'] as String;

つまり、こういうことです。

JSON文字列
   ↓ jsonDecode()
Map<String, dynamic>
   ↓ data['name']
pikachu

Dartの dart:convert ライブラリはJSONやUTF-8などの変換機能を提供しており、使用するには import 'dart:convert'; を書きます。


手順1:dart:convertを読み込む

lib/main.dart の一番上に、次のimportを追加します。

import 'dart:convert';

前の節のコードと合わせると、このようになります。

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

dart:convert は、Dartに最初から用意されている標準ライブラリです。

追加パッケージではないので、flutter pub add は必要ありません。


手順2:fetchPokemonDataの戻り値をMapに変える

前の節では、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;
}

今回は、戻り値を String ではなく Map<String, dynamic> に変えます。

/**
 * PokeAPIからピカチュウのデータを取得し、Mapに変換する関数。
 *
 * 入力: なし
 * 出力: ポケモン情報を持つMap<String, dynamic>
 */
Future<Map<String, dynamic>> 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通信に失敗しました');
  }

  final Map<String, dynamic> data =
      jsonDecode(response.body) as Map<String, dynamic>;

  return data;
}

ここが今回の中心です。

final Map<String, dynamic> data =
    jsonDecode(response.body) as Map<String, dynamic>;

jsonDecode() の戻り値は dynamic です。 そのため、今回は as Map<String, dynamic> と書いて、Mapとして扱うことを明示しています。


手順3:ポケモン名を取り出す

Mapに変換できると、キーを指定して値を取り出せます。

PokeAPIのポケモンデータには、name というキーがあります。

final String name = data['name'] as String;

これで、pikachu という文字列を取り出せます。

たとえば、画面に表示する文章を作るなら、次のように書けます。

final String name = data['name'] as String;

setState(() {
  _pokemonName = name;
});

長いJSONをそのまま表示していた状態から、必要な情報だけを取り出す段階に進みます。


手順4:画面表示をJSON全文から名前表示に変える

前の節では、状態変数をこのようにしていました。

String _pokemonData = 'まだデータを取得していません';

今回は、名前を表示するので、変数名を少し分かりやすくします。

String _pokemonName = 'まだデータを取得していません';

そして、ボタンを押したときの処理を次のように変えます。

/**
 * PokeAPIからデータを取得し、ポケモン名を画面に表示する関数。
 *
 * 入力: なし
 * 出力: _pokemonNameを更新して画面を再描画する
 */
Future<void> _loadPokemonData() async {
  final Map<String, dynamic> data = await fetchPokemonData();
  final String name = data['name'] as String;

  setState(() {
    _pokemonName = name;
  });
}

これで、画面には長いJSONではなく、pikachu だけが表示されます。


今回の完成コード

lib/main.dart を次のようにします。

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

/**
 * アプリの起点になる関数。
 *
 * 入力: なし
 * 出力: MyAppを起動する
 */
void main() {
  runApp(const MyApp());
}

/**
 * PokeAPIからピカチュウのデータを取得し、Mapに変換する関数。
 *
 * 入力: なし
 * 出力: ポケモン情報を持つMap<String, dynamic>
 */
Future<Map<String, dynamic>> 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通信に失敗しました');
  }

  final Map<String, dynamic> data =
      jsonDecode(response.body) as Map<String, dynamic>;

  return data;
}

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 _pokemonName = 'まだデータを取得していません';

  /**
   * PokeAPIからデータを取得し、ポケモン名を画面に表示する関数。
   *
   * 入力: なし
   * 出力: _pokemonNameを更新して画面を再描画する
   */
  Future<void> _loadPokemonData() async {
    final Map<String, dynamic> data = await fetchPokemonData();
    final String name = data['name'] as String;

    setState(() {
      _pokemonName = name;
    });
  }

  /**
   * ポケモン図鑑のトップ画面を構築する関数。
   *
   * 入力: BuildContext
   * 出力: Scaffold
   */
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ポケモン図鑑'),
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              ElevatedButton(
                onPressed: _loadPokemonData,
                child: const Text('APIからデータを取得する'),
              ),
              const SizedBox(height: 24),
              Text(
                _pokemonName,
                style: const TextStyle(
                  fontSize: 28,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

実行して確認する

ターミナルでアプリを起動します。

flutter run

画面に表示されたボタンを押します。

APIからデータを取得する

ボタンを押したあと、画面に次のように表示されれば成功です。

pikachu

前の節では長いJSON文字列が表示されていました。

今回は、その中から name だけを取り出せています。

ここまで来ると、API通信が少し「アプリらしい処理」に変わってきます。


Mapから値を取り出す基本形

Mapから値を取り出すときは、次のように書きます。

final String name = data['name'] as String;
final int height = data['height'] as int;
final int weight = data['weight'] as int;

PokeAPIのポケモンデータでは、heightweight も取得できます。

たとえば、次のように表示用の文章を作れます。

final String name = data['name'] as String;
final int height = data['height'] as int;
final int weight = data['weight'] as int;

final String message = '名前: $name\n高さ: $height\n重さ: $weight';

ただし、画像URLなどは少し深い場所に入っています。

それは次の節以降で扱います。


少しだけ深いJSONを見る

PokeAPIのデータには、単純な値だけでなく、入れ子になったデータもあります。

たとえば、画像URLは次のような場所にあります。

sprites → front_default

Mapで取り出すと、次のような書き方になります。

final Map<String, dynamic> sprites =
    data['sprites'] as Map<String, dynamic>;

final String imageUrl = sprites['front_default'] as String;

少し階段を降りるような感覚です。

data
  └─ sprites
      └─ front_default

ただし、この節ではまず name の取得だけで十分です。

画像表示は後の節で、Image.network() と一緒に扱います。


よくあるエラー

1. jsonDecodeが使えない

次のようなエラーが出る場合があります。

The function 'jsonDecode' isn't defined.

この場合は、dart:convert のimportが抜けています。

import 'dart:convert';

これを main.dart の一番上に追加してください。


2. 型変換でエラーになる

次のようなコードを書くと、型が曖昧になりやすいです。

final data = jsonDecode(response.body);

短く書けますが、教材では最初のうちは型を明確にしておく方が安全です。

final Map<String, dynamic> data =
    jsonDecode(response.body) as Map<String, dynamic>;

Dartはnull safetyや強い型付けを持つ言語として説明されており、学習段階でも型を明示しておくと、エラーの場所を見つけやすくなります。


3. data['Name'] と書いてしまう

JSONのキーは、大文字・小文字を区別します。

PokeAPIでは name です。

正しい例です。

final String name = data['name'] as String;

間違いやすい例です。

final String name = data['Name'] as String;

Name と書くと、値が取れません。


今回はまだやらないこと

この節では、JSON文字列をMapに変換し、ポケモン名を取り出しました。

ただし、まだ次の作業は行いません。

まだやらないこと理由
検索番号を入力する次の節以降でTextFieldを使う
画像URLを表示するImage.network() の節で扱う
カードUIに整えるUI表示の節で扱う
ローディング表示通信中の表示の節で扱う
エラー表示失敗時の表示の節で扱う

今は、JSONをMapに変換する一点に絞ります。


確認問題

問1

JSON文字列をDartのデータに変換するために使う関数は何ですか?

答え。

jsonDecode()

問2

jsonDecode() を使うために必要なimportはどれですか?

答え。

import 'dart:convert';

問3

APIから返ってきたJSON文字列は、どこに入っていますか?

答え。

response.body

問4

Mapから name を取り出すコードはどれですか?

答え。

final String name = data['name'] as String;

まとめ

この節では、APIから返ってきたJSON文字列を、DartのMapに変換しました。

今回の大事な流れは、次の通りです。

流れコード
JSON変換ライブラリを読み込むimport 'dart:convert';
JSON文字列をMapに変換するjsonDecode(response.body) as Map<String, dynamic>
名前を取り出すdata['name'] as String
画面を更新するsetState()

JSONをMapに変換できると、APIデータを自由に扱えるようになります。

長い文字のかたまりだったJSONが、必要な情報を取り出せる「材料」に変わりました。

次の節では、ポケモン番号を入力して検索できるようにしていきます。

FAQ

よくある質問

FlutterでJSONをMapに変換するは医療関係者向けだけの内容ですか。
医療分野の例が含まれる場合もありますが、医療関係者だけに限定した内容ではありません。生成AI、AI活用、DX、業務改善、プロトタイプ開発など、一般的なAI学習の事例として読める内容です。
AI初心者でも読めますか。
はい。AIをこれから学ぶ方、数学が苦手な方、仕事でAIを使いたい方にも読み進めやすいように、教材の章と節の流れに沿って整理しています。
サムネイル画像は必ず表示されますか。
はい。教材にcoverUrlが設定されている場合はその画像を表示し、未設定の場合は代替サムネイル画像を表示します。
Flutterアプリケーション開発概論のほかの章も読めますか。
はい。教材トップから章立てを確認でき、前後の節へもページ下部のナビゲーションから移動できます。