本章のトピック
Step4では非同期処理で RESAS から取得したデータを jsonDecode
を用いて Json パースし、その値をアプリで表示していました。本章では City というモデルを作成し、取得したデータを構造化し扱いやすい形に書き換えます。
- Classes | Dart を使って市区町村データを構造化する
- enum を使って市区町村の種類を列挙
Cityモデルの作成
Dart におけるモデルは単なるクラスでデータ構造を表現するのに役立ちます。クラスについては以下をご参照ください。
こちらもわかりやすいです。
例えば、市区町村の一覧を取得する API では以下がデータとして取得できますが、これらを City クラスとしてまとめて定義しておくイメージです。モデル定義がなされていないと、データを取り出す時に毎回"cityName"というプロパティ名を指定して JSON から取得する必要がありまた、本来1つの塊として扱われるデータなので、塊として定義しておくのが望ましいです。
{
"prefCode": 1, # 都道府県コード
"cityCode": "01100", # 市区町村コード
"cityName": "札幌市", # 市区町村名
"bigCityFlag": "2" # 特別区・行政区フラグ(0:一般の市区町村、1:政令指定都市の区、2:政令指定都市の市、3:東京都23区)
},
定義は簡単です。まず、city
フォルダ直下に city.dart
を作成し、以下を記述します。細かい解説は割愛しますが、上記で言及した RESAS API で取得できるデータの Key をそのままフィールド名として定義しています(prefCode
など)。
class City {
City({
required this.prefCode,
required this.cityCode,
required this.cityName,
required this.bigCityFlag,
});
// 今回の肝で、JSONを引数に取り中身をそれぞれ展開しCityクラスに変換して返却しています。
factory City.fromJson(Map<String, dynamic> json) {
return City(
prefCode: json['prefCode'] as int,
cityCode: json['cityCode'] as String,
cityName: json['cityName'] as String,
bigCityFlag: json['bigCityFlag'] as String,
);
}
int prefCode;
String cityCode;
String cityName;
String bigCityFlag;
}
次に list_page.dart
の jsonDecode 部分に以下を追記し、cities で書き換えます。
final json = jsonDecode(snapshot.data!)['result'] as List;
final items = json.cast<Map<String, dynamic>>();
+ final cities = items.map(City.fromJson).toList();
return ListView.builder(
- itemCount: items.length,
+ itemCount: cities.length,
itemBuilder: (context, index) {
- final item = items[index];
+ final city = cities[index];
return ListTile(
- title: Text(item['cityName'] as String),
+ title: Text(city.cityName),
subtitle: const Text('政令指定都市'),
trailing: const Icon(Icons.navigate_next),
onTap: () {
Navigator.of(context).push<void>(
MaterialPageRoute(
builder: (context) => CityDetailPage(
- city: item['cityName'] as String,
+ city: city.cityName,
),
),
);
},
);
},
);
City モデルの cityName は String(文字列)で扱うことがすでに定義されているので、as String
のような変換をわざわざする必要がなくなります。同様に、subtitle に bigCityFlag を指定してみましょう。
return ListTile(
title: Text(city.cityName),
- subtitle: const Text('政令指定都市'),
+ subtitle: Text(city.bigCityFlag),
trailing: const Icon(Icons.navigate_next),
);
このように City モデルであらかじめ定義したフィールドはピリオドで繋いで取得できます。ここまでできると、以下のような画面となるはずです。
Cityモデルでパースした表示 |
---|
![]() |
enumを使って市区町村の種類を管理する
前のステップで、subtitle に city.bigCityFlag
を指定した影響で、政令指定都市で固定されていたテキストが数字に変わってしまいました。改めてこの bigCityFlag を RESAS API のドキュメントで確認してみましょう。
ドキュメントに記載の通り、この値はフラグとなっており数値と特別区/行政区が1対1で管理されているようです。例えば、"0"の場合は一般市区町村、といった具合です。
この数値を元に if 文や switch 文を使って分岐してもよいのですが、こういった網羅性が必要な値を管理するには enum を利用するのがおすすめです。
city フォルダ直下に city_type.dart
を作成し以下を書きます。
enum CityType {
/// 一般の市区町村
general('一般の市区町村'),
/// 政令指定都市の区
designatedWard('政令指定都市の区'),
/// 政令指定都市の市
designatedCity('政令指定都市の市'),
/// 東京都23区
designatedTokyoWard('東京都23区'),
;
const CityType(this.label);
final String label;
}
これで CityType という型で"general"は一般の市区町村といった具合の対応関係を作ることができました。
次に city.dart
を書き換えます。bigCityFlag は数字しか情報をもっていなかったですが、先ほど enum で定義した CityType に帰ることで、対応関係を持つ型にできました。あわせて命名も適切な形で cityType に変更しました。
+ import 'city_type.dart';
class City {
City({
required this.prefCode,
required this.cityCode,
required this.cityName,
- required this.bigCityFlag,
+ required this.cityType,
});
factory City.fromJson(Map<String, dynamic> json) {
return City(
prefCode: json['prefCode'] as int,
cityCode: json['cityCode'] as String,
cityName: json['cityName'] as String,
- bigCityFlag: json['bigCityFlag'] as String,
+ cityType: CityType.values[int.parse(json['bigCityFlag'] as String)],
);
}
int prefCode;
String cityCode;
String cityName;
- String bigCityFlag;
+ CityType cityType;
// ref. https://dart.dev/language/operators
bool operator ==(Object other) {
return other is City &&
other.prefCode == prefCode &&
other.cityCode == cityCode &&
other.cityName == cityName &&
- other.bigCityFlag == bigCityFlag;
+ other.cityType == cityType;
}
int get hashCode;
}
最後に、list_page.dart
の subtitle を修正します。
ListTile(
title: Text(city.cityName),
- subtitle: Text(city.bigCityFlag),
+ subtitle: Text(city.cityType.label),
trailing: const Icon(Icons.navigate_next),
);
以下の表示となるはずです。
CityTypeを使って行政区を適切化 |
---|
![]() |
良いですね。元々"政令指定都市"とベタ書きだったものが、RESAS のデータに差し替わって数値となり、今回の実装で適切な文字列として表示されました。市区町村の名前と subtitle で指定した特別区・行政区の表示がしっかりあっていることがわかります。
これで市区町村の一覧画面の実装は完了です。
本章は以上です。