Flutterで郵便番号検索アプリを作ってみよう(解説編)

前回の「Flutterで郵便番号検索アプリを作ってみよう」では、皆さんにアプリが動く喜びを知ってもらうためにかなりの部分を端折って記事を書きました。

今回は、かなり詳しく解説していきたいと思います。只、プログラム初心者向けに作成しますので、かなり冗長になるかもしれませんが、よろしければお付き合いください。

目次

APIサービスとは

まず、APIサービスの解説からさせて下さい。

APIサービスとは、アプリケーションプログラミングインターフェース(API)の一種で、ソフトウェアやアプリケーションが他のプログラムと連携するための仕組みです。これにより、異なるソフトウェア間でデータを交換したり、機能を利用したりできます。

APIサービスを使う一番のメリットは、「再発明しなくてもよい」ということです。たとえば、天気予報のアプリを作る場合、自分で気象データを収集し分析する代わりに、気象情報を提供するAPIサービスを利用して、必要なデータを簡単に取得できます。

また、APIサービスは「レストランでの注文」に例えることができます。レストランで食事を注文する時、ウェイター(API)を通じてキッチン(サーバー)に何がほしいか伝えます。ウェイターはキッチンにあなたの注文を伝え、できあがった料理をあなたに届けます。APIもこれと同じで、アプリケーションがサーバーにデータをリクエストし、サーバーからのレスポンスを受け取る仲介者の役割を果たします。

APIサービスは、リクエストとレスポンスという形式で通信します。アプリケーションはAPIにリクエストを送信し、APIはそのリクエストを処理して適切なレスポンスを返します。これにより、アプリケーションは必要なデータを取得したり、遠隔地にあるサーバー上のプログラムを操作したりすることができます。

APIサービスは、ウェブベースのAPIやクラウドサービスが提供するAPIなど、さまざまな形で存在します。最終的にAPIサービスは、複雑な機能やデータを簡単に、そして効率的にアプリケーションに統合するための手段として広く利用されています。

ZipCloudとは

ZipCloudは日本の郵便番号を使って、住所情報を検索できるウェブAPIサービスです。これを使うことで、開発者は自分のアプリケーションやウェブサイトに郵便番号から住所を自動で引っ張ってくる機能を簡単に追加できます。

例えば、オンラインショッピングサイトで商品を購入する時、お客さんが郵便番号を入力すると、ZipCloud APIを通じて自動的に市区町村名や町域名がフォームに入力される、というような使い方が考えられます。これにより、お客さんは住所を一から全部入力する手間を省くことができます。

ZipCloudの使い方は簡単です。アプリケーションからZipCloudの提供するエンドポイント(ウェブ上の特定のURL)に対して、HTTPリクエストを送信し、パラメータに郵便番号を含めます。すると、ZipCloudのサービスはその郵便番号に紐づいた住所情報をレスポンスとして返します。このレスポンスは、通常JSONという形式で提供されるので、アプリケーションはこの情報を受け取って、必要な処理を行うことができます。

ZipCloudは、特にユーザーの入力を効率化し、住所入力に関するミスを減らすために役立つサービスです。アプリ開発の現場では、ユーザー体験を向上させるためにこのようなAPIサービスを組み込むことが一般的です。

ZipCloudの利用方法

ZipCloudのサイト

まず、ZipCloudのサイトへ移動します。

右の方に郵便番号検索APIと書かれていますので、「利用方法はこちら」と書かれたリンクをクリックしてください。

リクエストURL

特に難しいところは無いと思いますが、リクエストURLと記載されている箇所がエンドポイントとなります。

このエンドポイントを今回のアプリのmainUrlとして指定しています。

リクエストパラメータ

リクエストパラメータにzipcode、callback、limit の3つがあり、zipcodeのみが必須となっています。つまり、正しいzipcode(7桁の郵便番号)を指定すれば、正しいレスポンスが帰ってくるということです。

レスポンスフィールド

レスポンスフィールドにstatus、message、resultsがあり、results のaddress1、address2、address3を取得すれば、住所を取得できるということです。

これで、APIサービスの利用方法は分かったと思いますので、次はコードの解説を行いたいと思います。

ZipCodeクラスの解説

Setp8のZipCodeクラスについて詳しく解説します。

Future型とは

ZipCodeクラスの2行目にFuture型があります。このFuture型について解説します。

Future型は、Dart言語で非同期処理を扱うための重要な概念です。Flutterのアプリ開発では、非同期処理が一般的に使われます。非同期処理とは、何らかの長い時間がかかる処理を行っている間に、他のコードの実行をブロックせずに続けることができるようにするものです。

Futureは、将来のある時点で値が利用可能になることを表します。例えば、インターネットからデータをダウンロードする操作を行うとき、そのデータがすぐには利用できないため、Futureを使ってデータが到着した時点で何かをするようにスケジュールを組みます。

簡単に言うと、Futureは約束された値です。まだ手に入っていないけれど、将来的には手に入る値のことを指します。ある関数がFutureを返すとき、それは「私はすぐには結果を返せないけれど、結果はいつかは返すから待っててね」と言っているようなものです。

Furureを使う一般的なパターンは、.then()メソッドを使用して、値が利用可能になった後で実行される処理をチェーンすることです。また、asyncawaitのキーワードを使って、非同期処理をより読みやすい同期的なスタイルで書くこともできます。この場合、awaitキーワードはFutureが完了するのを待ち、その結果を取得するのに使用されます。

Future<String> fetchData() {
  // 何らかの長い処理を模倣するために、非同期で1秒後に文字列を返す
  return Future.delayed(Duration(seconds: 1), () => 'データダウンロード完了');
}

main() async {
  print('データの要求を開始');
  String data = await fetchData();
  print(data); // 'データダウンロード完了'が出力される
}

このコードでは、fetchDataはすぐには文字列を返さず、1秒後に文字列を返すFutureを返します。main関数では、awaitを使用してfetchDataの結果が利用可能になるまで待ちます。結果が利用可能になると、その値をdata変数に代入し、次の行でそれを出力します。

jsonDecodeとは

11行目にあるjsonDecodeはDartプログラミング言語で提供されている関数で、JSON形式の文字列をDartのオブジェクトに変換するときに使います。JSON(JavaScript Object Notation)は、データをテキスト形式でやり取りするための標準フォーマットの一つです。ウェブAPIからのレスポンスなどでよく使われます。

簡単に言うと、jsonDecode関数は、JSON形式のデータをDartが理解できる形に「翻訳」するためのものです。たとえば、ウェブAPIから受け取ったJSON形式の文字列があったとします。この文字列はただのテキストであり、Dartの中で直接使うには適していません。jsonDecodeを使うことで、この文字列をDartのリストやマップなどのオブジェクトに変換し、プログラム内で扱えるようにします。

resultsの値

14行目のresultsの値がどのようになっているのかを知るために、ブラウザから直接下記の様に入力してみてください。

https://zipcloud.ibsnet.co.jp/api/search?zipcode=162−0042

下記の様に表示されるはずです。

つまり、resultsの中身はリストです。つまりresultsの0番目にマップが入っています。

addressの値

14〜15行目のaddrssの値は、上記のresultsのマップから、”address1″、”address2″、”address3″の値を取得して文字列として連結しています。そして、17行目で文字列として値を返しています。

それ以外は、「Fluttterで郵便番号を検索してみよう」で説明しています。

_TopPageクラスの解説

Step10の修正した_TopPageクラスを詳しく解説していきます。

アンダーバーの意味

1行目の_TopPageStateや2行目の_controller、3行目の_addressResult…..等、色々なクラスや変数、メソッドについていますが、このアンダーバーの解説をします。

Flutter(およびDart言語)において、メソッド名や変数名の前にアンダーバー( _ )が付いているとき、それはそのメソッドや変数が「プライベート」であることを意味します。つまり、そのメソッドや変数は、定義されているファイル内でのみアクセス可能であり、他のファイルからは直接アクセスすることはできません。

Dartでは、アクセス制限の範囲をファイル単位で設定します。これは他の言語で見られるクラス単位のプライベート(他のクラスから見えない)とは異なります。Dartでは、プライベートメンバーには同じソースファイル内からのみアクセスが可能です。

例えば、以下のようなクラスがあるとします:

class MyClass {
  int _privateVariable; // プライベート変数
  void _privateMethod() { // プライベートメソッド
    // ...
  }
}

この場合、_privateVariableと_privateMethodはMyClassが定義されているファイル内でのみアクセス可能です。他のファイルからはこれらに直接アクセスすることはできません。

プライベートメソッドや変数を使用する理由は、カプセル化を促進し、クラスの公開インターフェースを制限することです。これにより、クラスの内部実装の詳細を隠蔽し、外部から不用意に変更されることを防ぐことができます。また、クラスの使用者に対して、利用すべきメソッドやプロパティだけを提示することで、クラスの使いやすさを向上させることができます。

TextEditingControllerとは

TextEditingControllerはテキストフィールドを制御するためのクラスです。ユーザーがアプリ内でテキストを入力するときに、そのテキストの値を読んだり更新したりすることができます。

例えば、ユーザーがフォームに名前やコメントを入力する際、TextEditingControllerを使うことで、その入力値を取得したり、プログラムからテキストフィールドの内容を変更したりすることが可能になります。また、テキストフィールドの初期値を設定したり、内容をクリアしたりする際にも使用します。

TextEditingControllerを使う典型的なステップは以下の通りです

  1. TextEditingControllerのインスタンスを作成します。
  2. TextFieldウィジェットのcontrollerプロパティにこのインスタンスを割り当てます。
  3. ユーザーの入力を読み取るには、TextEditingControllerのtextプロパティを使用します。
  4. 必要に応じて、TextEditingControllerを使ってテキストフィールドの値を更新します。
  5. ウィジェットが不要になったら、リソースを解放するためにTextEditingControllerを破棄します。

constとfinalの違い

コードの中にfinalとかconstとか有りますが、このconstとfainalの違いを解説します。

constとfinalはDart(およびFlutter)で変数を宣言する際に使われるキーワードで、両者は似ていますが、その用途と意味には重要な違いがあります。

const
  • constで宣言された変数はコンパイル時定数です。つまり、プログラムがコンパイルされる段階で値が固定されます。
  • const変数の値はリテラル(具体的な値)か、コンパイル時にその値が確定しているconstの式でなければなりません。
  • constを使うと、その変数はプログラム全体で一つのインスタンスを共有します。これによりメモリの効率が良くなります。
const double pi = 3.14; // コンパイル時に値が設定される
final
  • finalで宣言された変数は実行時定数です。これは、変数が最初に参照されたときに一度だけ値がセットされ、それ以降は変更できないことを意味します。
  • final変数はコンパイル時に値が分からなくても、実行時に一度だけ値をセットできます。例えば、データベースから取得したデータやユーザーの入力など、実行時にのみ決定される値を保持するのに適しています。
  • 各final変数はそれぞれが値のインスタンスを持ちます。
final DateTime currentTime = DateTime.now(); // 実行時に値が設定される
 要するに
  • constはコンパイル時に値が決まり、変更不可能な定数を宣言するのに使います。プログラム内で常に同じ値である必要があります。
  • finalは一度値が設定されると変更不可能ですが、その値が実行時に決定される場合に使います。

contとfinalはどちらも不変性を保証しますが、constはプログラムの実行前に値が固定されるのに対して、finalはプログラムの実行中に一度だけ値が設定されるという点で異なります。このため、どちらを使用するかは、変数に割り当てる値がいつ決定されるかによって決まります。

最後に

今回のアプリは只APIからデータを取得し、住所を表示するだけの単純なアプリでしたが、色々ブラッシュアップする必要があると思います。

例えば、ZipCloudのレスポンスサンプルの様に、同じ郵便番号で地番が複数ある場合の処理もしなければなりません。

また、エラーハンドリングが不十分です。catchブロック内でエラーメッセージを単にプリントしていますが、ユーザーにフィードバックを与えるためにエラーメッセージをUIに表示するなどの処理も必要だと思われます。

そして、API呼び出しはUIのビルドメソッド内で行われており、これはあまり良い方法ではありません。非同期処理はFutureBuilderやStateのライフサイクルメソッド内で行うほうがいいでしょう。

実際のユーザーインタラクションを考えると、郵便番号が入力されていない場合に検索を実行しないようにするなど、入力検証も不足しています。

これらのことを考慮して各自でブラッシュアップしてみてください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次