Flutter備忘録

# はじめに
Udemyで習った内容の備忘録のためのものです．

# 到達目的

- [x] 例外や失敗時のための処理の仕方
- [x] 使用した関数の理解

# 内容
下記が理解したいコードです，郵便番号のAPIの実装のためのコードです．
```dart
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  TextEditingController controller = TextEditingController();
  List<String> items = [];
  String errorMessage = '';

  Future<void> loadZipCode(String zipCode) async {
    setState(() {
      errorMessage = 'APIレスポンス待ち';
    });

    final response = await http.get(
        Uri.parse('https://zipcloud.ibsnet.co.jp/api/search?zipcode=$zipCode'));

    if (response.statusCode != 200) {
      // 失敗
      return;
    }
    //成功
    final body = json.decode(response.body) as Map<String, dynamic>;
    final results = (body['results'] ?? []) as List<dynamic>;

    if (results.isEmpty) {
      setState(() {
        errorMessage = 'そのような郵便番号の住所はありません';
      });
    } else {
      setState(() {
        errorMessage = '';
        items = results
            .map((result) =>
                "${result['address1']}${result['address2']}${result['address3']}")
            .toList(growable: false);
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: TextField(
          controller: controller,
          keyboardType: TextInputType.number,
          onChanged: (value) {
            if (value.isNotEmpty) {
              loadZipCode(value);
            }
          },
        ),
      ),
      body: ListView.builder(
        itemBuilder: (context, index) {
          if (errorMessage.isNotEmpty) {
            return ListTile(title: Text(errorMessage));
          } else {
            return ListTile(title: Text(items[index]));
          }
        },
        itemCount: items.length,
      ),
    );
  }
}

```

### 1.MyAppWidget

<details><summary>MyApp</summary> 

```dart

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

```

</details>

```main```関数に呼び出されるルートWidgetで，このWidgetで宣言されたものから実行されていく．最初に渡す引数やUI構築のための変数などもここで宣言される

#### 解釈
アプリケーションを構築するための最初のWidgetで初期設定などここで設定する．

### 2. MyhomePageウィジェット


<details><summary>MyhomePage </summary>


```dart

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  TextEditingController controller = TextEditingController();
  List<String> items = [];
  String errorMessage = '';
	．．．．
}

```
</details>


上記のコードについて, ```MyApp```で呼び出されるなウィジェット．ここで変化する対象である変数やウィジェットを宣言する．

### 3. loadZipCode関数


<details><summary>loadZipCode</summary>

```dart

Future<void> loadZipCode(String zipCode) async {
  setState(() {
    errorMessage = 'APIレスポンス待ち';
  });

  final response = await http.get(
      Uri.parse('https://zipcloud.ibsnet.co.jp/api/search?zipcode=$zipCode'));

  if (response.statusCode != 200) {
    // 失敗
    return;
  }
  //成功
  final body = json.decode(response.body) as Map<String, dynamic>;
  final results = (body['results'] ?? []) as List<dynamic>;

  if (results.isEmpty) {
    setState(() {
      errorMessage = 'そのような郵便番号の住所はありません';
    });
  } else {
    setState(() {
      errorMessage = '';
      items = results
          .map((result) =>
              "${result['address1']}${result['address2']}${result['address3']}")
          .toList(growable: false);
    });
  }
  
```

</details>

上記の内容についてわからない単語がちょくちょく出てきたので，下記にまとめました．

| 項目 | 内容 | 解釈 |
| - | - | - |
| Future<void> | 非同期プログラミング.操作が完了した場合のみ通知し，値は返さない関数| 成功したら通知のみしてくれるvoid関数  |
| async & await | 非同期プログラミングのための処理．特定タスクの完了を待たずに実行させることを目的としている． | asyncは非同期関数であることの宣言．awaitはどの処理を待つかを宣言するためのもの |
| dynamic | any型 | どんな変数でも行ける．しかし，どんな値をコンパイラ時点で扱うのかはわからない |

```mermaid

graph TD;

A[関数の実行]-->B[setState内で宣言された変数に更新される];

B-->C[API通信が完了するまで待つ];

C-->|成功|D[結果の変数を格納];

D-->|失敗|E[何も返さない];

D-->F[変数はあるか];

F-->|Yes|G[結果を整理してWidgetの変更をFWに通知];

F-->|No|H[errorメッセージを変更して，Widgetの変更をFWに通知];

```

### 5. Buildメソッド

<details><summary>loadZipCode</summary>

```dart

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: TextField(
        controller: controller,
        keyboardType: TextInputType.number,
        onChanged: (value) {
          if (value.isNotEmpty) {
            loadZipCode(value);
          }
        },
      ),
    ),
    body: ListView.builder(
      itemBuilder: (context, index) {
        if (errorMessage.isNotEmpty) {
          return ListTile(title: Text(errorMessage));
        } else {
          return ListTile(title: Text(items[index]));
        }
      },
      itemCount: items.length,
    ),
  );
}
  
```

</details>

上記のコードが基本的なUIについての内容です．```TextFiled```がテキストを読み込むためのUIで，ListViewがAPI通信で得た内容の表示のためのコードです．
```itemCount: items.length,```はListViewのための引数です．ListViewのアイテム表示をいくつするかを表したものです．