Json to Dartを使ってWEB APIから取得したJsonをFlutterで取り扱うTIPS

2021-05-30

はじめに

Flutter(Dart)でJsonを取得して構造体に詰め込みたい場面が多々ありました。 Json to Goのようなサイトがあれば自分で定義しなくて済むのに…と探していました。 ありましたよ。嬉しくて嬉しくてたまんねぇですよ。

Json to Dart

JSON to Dart

JsonファイルからそのJsonをDart構造体に詰め込むことができるコードを生成するサイトです。 これを使うことで、Jsonを取得できるWEBAPIを使ったアプリ開発がめっちゃやりやすくなります。

郵便番号検索API

JSONを取得できるということなので、今回はよく例題に上がる 郵便番号検索APIで試そうと思います。 https://zipcloud.ibsnet.co.jp/api/search?zipcode=7830060 取得したJsonをJson to Dartにかけていきます。

{
    "message": null,
    "results": [
        {
            "address1": "高知県",
            "address2": "南国市",
            "address3": "蛍が丘",
            "kana1": "コウチケン",
            "kana2": "ナンコクシ",
            "kana3": "ホタルガオカ",
            "prefcode": "39",
            "zipcode": "7830060"
        }
    ],
    "status": 200
}

コード実装を進めます。

フォルダ構成

C:.
│  main.dart
│
├─json
│      JsonStruct.dart
│
├─models
│      logic.dart
│
└─view
        ui.dart

json/JsonStruct.dart

ツールをかけて取得結果を貼り付け、一部コードを修正します。 (Null Safetyなどの兼ね合いで最新のDartだとエラーが出るため)

ほとんど変換しただけですが、とりあえず貼り付けます。

class Autogenerated {
  Null message;
  late List<Results> results;
  late int status;

  Autogenerated({this.message, required this.results, required this.status});

  Autogenerated.fromJson(Map<String, dynamic> json) {
    message = json['message'];
    if (json['results'] != null) {
      results = <Results>[];
      json['results'].forEach((v) {
        results.add(new Results.fromJson(v));
      });
    }
    status = json['status'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['message'] = this.message;
    data['results'] = this.results.map((v) => v.toJson()).toList();
    data['status'] = this.status;
    return data;
  }
}

class Results {
  String? address1;
  String? address2;
  String? address3;
  String? kana1;
  String? kana2;
  String? kana3;
  String? prefcode;
  String? zipcode;

  Results(
      {required this.address1,
      required this.address2,
      required this.address3,
      required this.kana1,
      required this.kana2,
      required this.kana3,
      required this.prefcode,
      required this.zipcode});

  Results.fromJson(Map<String, dynamic> json) {
    address1 = json['address1'];
    address2 = json['address2'];
    address3 = json['address3'];
    kana1 = json['kana1'];
    kana2 = json['kana2'];
    kana3 = json['kana3'];
    prefcode = json['prefcode'];
    zipcode = json['zipcode'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['address1'] = this.address1;
    data['address2'] = this.address2;
    data['address3'] = this.address3;
    data['kana1'] = this.kana1;
    data['kana2'] = this.kana2;
    data['kana3'] = this.kana3;
    data['prefcode'] = this.prefcode;
    data['zipcode'] = this.zipcode;
    return data;
  }
}

models/logic.dart

郵便番号検索APIでJsonを取得するコードを書いていきます。 http.Responseを使って指定したURLからJsonを取得します。 取得結果をJson to Dartで作成したAutogeneratedクラスのfromJsoin()で取り込むことで構造体への詰め込みが完了します。 後は、取得したい構造体の中身を指定すると取り出せます。

strtmp変数はchangeNotifyしたあとUIから読み出すための変数です。 取り出した値の一部を格納します。

void requestSample() async {
    var uri =
            Uri.parse('https://zipcloud.ibsnet.co.jp/api/search?zipcode=0791100');

    http.Response res = await http.get(uri);
    if (res.statusCode == 200) {
        String data = res.body;
        Map<String, dynamic> map = jsonDecode(data);
        var human = Autogenerated.fromJson(map);
        // String address1 = map['results'][0]['address1'];
        strtmp = human.results[0].address1!;
        print(human.results[0].address1);
    } else {
        throw Exception('Failed to load post');
    }
}

全部コードを置いていく

正直上に書いたコードとimportだけで十分ですが、 前回の書いた記事のコードをベースに分離しているのでそのままです。

Provider Patternを使い、FlutterアプリUIとロジックを分離するための考察

import 'dart:convert';

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

import 'package:mvvm_hotpepper/json/JsonStruct.dart';

class CountModel extends ChangeNotifier {
  /// 初期値
  int count = 0;
  String address = "ウマ娘";
  String res = "";

  String strtmp = "";

  /// count の更新メソッド
  void increment() {
    count++;
    changeText();
    requestSample();
    notifyListeners();
  }

  void changeText() {
    if (count > 5) {
      address = "ウマ娘プリティーダービー";
    }
  }

  void requestSample() async {
    var uri =
        Uri.parse('https://zipcloud.ibsnet.co.jp/api/search?zipcode=0791100');

    http.Response res = await http.get(uri);
    if (res.statusCode == 200) {
      String data = res.body;
      Map<String, dynamic> map = jsonDecode(data);
      var human = Autogenerated.fromJson(map);
      // String address1 = map['results'][0]['address1'];
      strtmp = human.results[0].address1!;
      print(human.results[0].address1);
    } else {
      throw Exception('Failed to load post');
    }
  }
}

ui.dart

前回記事と同様にUI用のStatelessWidgetを作っていきます。 中身はTextで先程詰め込んだstrtmpの値が更新されたら描画しています。

class PostcodeAddress extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text(
      /// context からModelの値が使える
      '${Provider.of<CountModel>(context).strtmp}',
      style: Theme.of(context).textTheme.headline4,
    );
  }
}

ツールの実行結果

右下の虫眼鏡アイコンを叩くと結果のような値が取得できます。 今は、URLに郵便番号直打ちですがInputボックスなどと組み合わせれば郵便番号検索ができます。 ちなみに、画面描画の都合上虫眼鏡を2回クリックしないと住所が出ないですが…まぁ些細な話ですよ。

まとめ

これでJson to Dartの使い方を理解できたと思います。 これがあれば、HOTPPER APIや津々浦々の様々なAPIを叩いてFlutterから取得できるようになります。

UIと分離しているのである程度中規模コードも作ることができそうです。

やったね。