FlutterでJSONデータのHTTP通信

（2021/12/12追記）以下の記事の情報は古いです。[Freezed](https://pub.dev/packages/freezed)を使用しましょう。

## はじめに
FlutterでJSONデータのHTTP通信をする方法に関しては[公式のドキュメント] (https://flutter.io/docs/cookbook/networking/fetch-data)で基本的な説明があります。
しかし、JSONをリクエストパラメータとしてPOSTする部分の記載が無かったので、そこを簡単に説明をしようと思います。

## コード
モデルクラスをJSON化してencodeしたものをbodyで渡します。
あとはヘッダーのContent-Typeをapplication/jsonにする事を忘れずに。

```dart
class SampleRequest {
  final String key;
  final int order;

  SampleRequest({
    this.key,
    this.order
  });

  Map<String, dynamic> toJson() => {
    'key': key,
    'order': order,
  };
}

class SampleResponse {
  final number sampleId;
  final String sampleName;

  SampleResponse({
    this.sampleId,
    this.sampleName
  });

  factory SampleResponse.fromJson(Map<String, dynamic> json) {
    return Spot(
      sampleId: json['sampleId'],
      sampleName: json['sampleName'],
    );
  }
}

Future<SampleResponse> _requestSample() async {
  var url = "http://example.com/sample"
  var request = new SampleRequest(key: "key", order: 0);
  final response = await http.post(url,
      body: json.encode(request.toJson()),
      headers: {"Content-Type": "application/json"});

  if (response.statusCode == 200) {
    // If server returns an OK response, parse the JSON
    return SampleResponse.fromJson(json.decode(response.body));
  } else {
    // If that response was not OK, throw an error.
    throw Exception('Failed to load post');
  }
}
```
といった感じのコードになります。

## さいごに
つい先日、Flutter製のアプリを無事リリースする事ができました。
知り合いが開発する[Korette](https://korette.fun/)というWebサービスのアプリ版です。
ダウンロードは[こちら](https://koretteapp.firebaseapp.com/download.html)から。観光地や地域にスポットを当てたクイズアプリです。ぜひ遊んでみてください！
![iphone_1.jpg](https://qiita-image-store.s3.amazonaws.com/0/108914/27f751a1-d6d5-d004-8b8b-9c6baef1e24d.jpeg)


## おまけ
HTTP通信のモデルクラスを各API毎にゴリゴリ描いてたら、パラメータの多い時にとんでもなく大変でした。
例えばパラメータが10個あればコンストラクタやtoJsonやfromJsonで40行と中々の量に...

```spot.dart
class Spot {
  final num id;
  final String spotName;
  final String spotDescription;
  final String adress;
  final num latitude;
  final num longitude;
  final num townId;
  final String townName;
  final num prefectureId;
  final String prefectureName;

  Spot({
    this.id, 
    this.spotName, 
    this.spotDescription, 
    this.adress, 
    this.latitude, 
    this.longitude, 
    this.townId, 
    this.townName, 
    this.prefectureId, 
    this.prefectureName, 
  });

  factory Spot.fromJson(Map<String, dynamic> json) {
    return Spot(
      id: json['id'],
      spotName: json['spotName'],
      spotDescription: json['spotDescription'],
      adress: json['adress'],
      latitude: json['latitude'],
      longitude: json['longitude'],
      townId: json['townId'],
      townName: json['townName'],
      prefectureId: json['prefectureId'],
      prefectureName: json['prefectureName'],
    );
  }

  Map<String, dynamic> toJson() => {
    'id': id,
    'spotName': spotName,
    'spotDescription': spotDescription,
    'adress': adress,
    'latitude': latitude,
    'longitude': longitude,
    'townId': townId,
    'townName': townName,
    'prefectureId': prefectureId,
    'prefectureName': prefectureName,
  };
}
```
この地味に苦しい繰り返し作業はrubyでコンバータを適当に作って解決しました。
[converter.rb](https://gist.github.com/oshima819/53d86c12ba21a0078215e9eafc9c3323)
これを使用すると、

```spot.dart
class Spot {
  final num id;
  final String spotName;
  final String spotDescription;
  final String adress;
  final num latitude;
  final num longitude;
  final num townId;
  final String townName;
  final num prefectureId;
  final String prefectureName;
}
```
とだけ書いて、
```ruby coverter.rb spot.dart```
というコマンドを叩けば、上記のコンストラクタやtoJsonやfromJsonが含まれたdartファイルが生成されます。
無駄な改行とかあると動かないかもしれないので注意してください。

そんなこんなで自動化してたわけですが、後から調べたら[json_annotation](https://pub.dartlang.org/packages/json_annotation)というpackageがあって、それを使用すれば十分かもしれません。