[Flutter]httpを使ってAPIと通信する（取得データ活用編）

# 投稿の経緯
今回は以前投稿した、「httpを使ってAPIと通信する（データ取得編）」の続きです。
取得したデータをModelに格納してWidgetでデータを表示したいと思います。

前回の投稿はこちら↓

https://qiita.com/ken_sasaki2/items/936143cd0613a29b4f44


# 環境
Flutter version：3.7.3
Dart version：2.19.2
IDE：Android Studio


# Modelに取得データを格納する
取得したデータの中から今回必要な`weather`の部分をModelに格納します。

```
response: {
    "lat":35.6514,
    "lon":139.6367,
    "timezone":"Asia/Tokyo",
    "timezone_offset":32400,
    "current":{
        "dt":1678618031,
        "sunrise":1678568265,
        "sunset":1678610717,
        "temp":288.71,
        "feels_like":288.12,
        "pressure":1022,
        "humidity":69,
        "dew_point":283.05,
        "uvi":0,
        "clouds":75,
        "visibility":10000,
        "wind_speed":4.63,
        "wind_deg":160,

        // 'weather'のデータを格納する
        "weather":[
            {
                "id":803,
                "main":"Clouds",
                "description":"曇りがち",
                "icon":"04n"
            }
        ]
    }
}
```
Modelクラスを作成します。

```.dart
class WeatherModel {
  final String main;
  final String description;
  final String icon;

  WeatherModel({
    required this.main,
    required this.description,
    required this.icon
  });

  factory WeatherModel.fromJson(Map<String, dynamic> json) {
    var weather = json['weather'];
    var data = weather[0];

    var model = WeatherModel(
        main: data['main'],
        description: data['description'],
        icon: data['icon']
    );

    return model;
  }
}
```

.fromJsonで`Map<String, dynamic>型`のjsonを受け取ってWeatherModel内でデータを格納するように書きました。呼び出し元の関数は以下の通り。

```.dart
Future<WeatherModel?>get() async {
    const lat = '35.65138';
    const lon = '139.63670';
    const key = 'aa50ce0598c22de20f75cf3ea31ac312';

    const domain = 'https://api.openweathermap.org';
    const pass = '/data/2.5/onecall';
    const query = '?lat=$lat&lon=$lon&exclude=daily&lang=ja&appid=$key';

    var url = Uri.parse(domain + pass + query);
    debugPrint('url: $url');

    var response = await http.get(url);

    if (response.statusCode == 200) {
      var body = response.body;
      var decodeData = jsonDecode(body);
      var json = decodeData['current'];
      var model = WeatherModel.fromJson(json);

      return model;
    }
    return null;
}
```
レスポンスのデータにアクセスするために`jsonDecode`したデータを`WeatherModel.fromJson`に渡し、`Future<WeatherModel?>型`でModelを返しています。

次は作成したModelを使ってWidgetを構築していきます。


# ModelのデータでWidgetを構築する
APIと通信して、取得したデータをモデルに格納する一連のコードは非同期でおこなわれているので、Widgetの構築には`FutureBuilder`を使ってみます。

## FutureBuilderとは
「Future = 非同期処理」の結果を受けてWidgetを構築する仕組みです。（便利！）

```.dart
FutureBuilder(
    future: , // 何かしらの非同期処理
    builder: (BuildContext context, AsyncSnapshot snapshot) {
        if (snapshot.hasData) {
            // 処理終了後に呼ばれる
        } else {
            // 処理中に呼ばれる
        }
    }
);
```

非同期で終了を待ちたい処理を`future`に、その結果を受けてWidgetを構築する処理を`builder`に定義します。builderには`BuildContext`と`AsyncSnapshot`の2つの引数が必要です。

この内、AsyncSnapshotにfutureで指定した非同期処理の結果が入るので、そのデータを使ってWidgetを構築します。ちなみにAsyncSnapshotには型を指定することもできます。

処理終了/処理中 なのかは`snapshot.hasData`で判断できます。
「true = 処理終了」「false = 処理中」といった状態です。非同期処理が終了していると`snapshot.data`から結果にアクセスでき、そのデータを使ってWidgetを構築します。

注意点は**hasDataのtrue/falseの両方にWidgetを構築する必要がある**ということです。


## 実際に使ってみた

```.dart
@override
Widget build(BuildContext context) {
  return FutureBuilder(
      future: get(),
      builder: (BuildContext context, AsyncSnapshot<WeatherModel?> snapshot) {
        if (snapshot.hasData) {
          var data = snapshot.data;

          return Scaffold(
            body: Center(
                child: Text('現在の天気は${data?.description}です！')
            )
          );
        } else {
          return const Scaffold(
              body: Center(
                  child: Text('処理中...')
              )
          );
        }
      }
    );
  }
}
```

非同期処理中は「処理中...」と表示され、処理終了後はAPIから取得した天気情報が表示されるシンプルな画面です。

## 実際の動作
![AnyConv.com__動作確認 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643372/50cbf1c5-aac8-cc74-45db-f9507d123ac1.gif)

このように非同期処理の処理中/処理終了でWidgetを自動で切り替えてくれる`FutureBuilder`は便利ですね！


# おわりに
前回の記事と合わせたここまでの実装で

httpでAPIと通信して
↓
取得データをModelに格納して
↓
Modelを使ってWidgetを構築

といった一連の流れはできたかなと思います。

引き続きFlutterに関する学びを発信していきます。最後までご覧いただきありがとうございました！