Flutterアプリケーションで郵便番号情報を取得するためには、APIを提供するサービスと通信する必要があります。ここでは、郵便番号APIを利用して情報を取得する一連の流れと、そのために必要なパッケージの使用方法を紹介します。
今回は、Flutterのパッケージ使用方法でご紹介したDioパッケージを使ってアプリを作成します。
プロジェクトの作成
郵便番号APIはZip Cloudを利用しようと思いますので、zip_cloud名でプロジェクトを作成します。
main.dartの作成
デフォルトアプリの全削除
デフォルトのアプリを全削除して、main.dartを作成し直します。
新しいmain.dartの記述
main.dartを下記の様に記述します。
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: TopPage(),
);
}
}
- ここまでは、「Flutterで初めてのプロジェクト」に記載していますので、問題は無いと思います。
- 唯一違うのはhomeでScaffoldWidgetでは無く、TopPageクラスを指定していることです。
- 尚、このままでは、エラーになりますので、TopPageクラスを作成します。
TopPageクラスの作成
main.dartにTopPageクラスを追加します。stfと入力すれば、コード補完が出ますので、ほとんどの部分は自動生成されるはずです。
class TopPage extends StatefulWidget {
const TopPage({Key? key}) : super(key: key);
@override
State<TopPage> createState() => _TopPageState();
}
class _TopPageState extends State<TopPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('郵便番号検索'),
),
);
}
}
簡単にコードの説明を下記に記載しておきます。
- 1行目:StatefulWidgetを継承しています。これは、ウィジェットがアプリケーションのライフサイクル中に変更され得る状態を持っていることを意味します。
- 2行目:コンストラクタでは、オプショナルなkeyを受け取り、親クラスのコンストラクタに渡しています。keyはウィジェットの一意性を保証するために使用されます。
- 5行目:ウィジェットの状態を管理する_TopPageStateクラスのインスタンスを生成します。
- 8〜20行目:Stateクラスを継承し、TopPageウィジェットの状態を保持します。
- 10行目:buildメソッドは、ウィジェットのビジュアル表現を構築するためにフレームワークによって呼び出されます。
- 11行目:Scaffoldウィジェットは、マテリアルデザインのレイアウト構造を提供します。これにより、アプリバーとそのタイトル、本文などの主要なUI要素を配置できます。
- 12〜14行目:AppBarウィジェットは、アプリケーションの上部に表示されるバーを提供し、titleプロパティにはTextウィジェットを使用して「郵便番号検索」というタイトルを表示しています。
Dioのインストール
この記事では、Dioを使用して郵便番号情報を取得する方法を紹介します。
Dioとは
Dioは高機能なHTTPクライアントライブラリであり、エラーハンドリング、インターセプタ、フォームデータの送信など、標準のhttpパッケージよりも豊富な機能を提供します。
Dioの検索
- pub.dev で dio を検索します。
- Installingタブを調べるとコマンドでのインストール方法が出ていますので、AndroidStudioのターミナルから、下記のコマンドを実行します。
flutter pub add dio
これでDioパッケージがインストールできました。
ZipCodeクラスの作成
ZipCodeクラスを作成
郵便番号から住所を検索するためのコードを記述します。
class ZipCode {
static Future<String> searchAddressFromZipCode(String? code) async {
Dio dio = Dio();
String mainUrl = 'https://zipcloud.ibsnet.co.jp/api/search';
Map<String, dynamic> queryParameters = {'zipcode': code};
try {
var res = await dio.get(
mainUrl,
queryParameters: queryParameters,
);
if (jsonDecode(res.data)['results'] == null) {
return jsonDecode(res.data)['message'];
} else {
var results = jsonDecode(res.data)['results'][0];
var address = results['address1'] + results['address2'] +
results['address3'];
return address;
}
} catch (e) {
print("エラーです");
return "郵便番号が正しくありません";
}
}
}
メソッドの説明
zipCodeクラスの中にsearchAddressFromZipCodeというメソッドを作成します。このメソッドの説明を下記に記述しておきます。
- 3行目:Dioインスタンスを作成しています。これはHTTPクライアントで、ネットワークリクエストを行うために使用されます。尚、Dioを使用するためにこのファイルの先頭に import文を書く必要があります。
- 4行目:mainUrl変数にAPIのURLを設定しています。
- 5行目:queryParametersマップにリクエストパラメータとして郵便番号(’zipcode’:code)を設定しています。
- 7行目:dio.getメソッドを使用して、APIに対してGETリクエストを非同期で行います。リクエストはqueryParametersを用いてパラメータ化されます。
- 11行目:レスポンスが返ってきたら、jsonDecodeを使用してレスポンスボディをデコードします。jsonDecodeはJSON形式の文字列をDartのオブジェクトに変換する関数です。
- 16行目:デコードされたデータからresultsキーを確認し、値がnullであるかどうかをチェックします。nullの場合、APIからのmessageを返します。
- 15行目:resultsがnullでない場合、最初の結果(results[0])から住所の部分(address1、address2、adddress3)を取り出し、これらを連結して完全な住所として返します。
- 6~11行目:try-catchブロックを使用しており、何らかのエラー(例えばネットワークエラーやデコードエラー)が発生した場合は、エラーメッセージを表示し、「郵便番号が正しくありません」という文字列を返します。
_TopPageStateクラスの修正
修正コード
先ほど作成した_TopPageStateクラスは、UI画面を表示するためだけに作成したので、実際にフォームからのデータを受け取ってAPIに渡し、APIからレスポンスを受け取ることは出来ません。
そこで、下記の様に修正します。
class _TopPageState extends State<TopPage> {
final TextEditingController _controller = TextEditingController();
String _addressResult = '';
@override
void dispose() {
_controller.dispose();
super.dispose();
}
Future<void> _search() async {
final zipCode = _controller.text;
final address = await ZipCode.searchAddressFromZipCode(zipCode);
setState(() {
_addressResult = address;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('郵便番号検索'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('郵便番号を入力してください'),
const SizedBox(height: 16),
SizedBox(
width: 200,
child: TextField(
controller: _controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: '郵便番号',
),
),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _search,
child: const Text('検索'),
),
const SizedBox(height: 16),
Text('検索結果: $_addressResult'),
],
),
),
);
}
}
コードの説明
下記にコードの説明を記述しておきます。
- 2行目:ユーザーが入力した郵便番号を追跡するためのテキスト編集コントローラーです。このコントローラーはTextFieldウィジェットに結びついており、ユーザーの入力を取得できます。
- 3行目:検索結果を保存するための変数で、初期状態は空文字列です。
- 5〜9行目:ウィジェットのライフサイクルが終了し、ステートが破棄される時にコントローラーをクリーンアップするためにオーバーライドされています。
- 11〜17行目:ユーザーが入力した郵便番号を_Controllerから取得し、ZipCodeクラスのsearchAddressFromZipCodeメソッドを非同期で呼び出して住所を検索します。結果は_addressResultに格納され、setStateを呼び出すことでUIが更新されます。
- 20行目:Widget buildは、 UIの構造を定義するメソッドです。Scaffoldウィジェットを使用して、アプリバーとボディコンテンツを持つ基本的なページレイアウトを作成しています。ボディ内では、ユーザーが郵便番号を入力するためのTextFieldと、検索を実行するためのElevatedButton、そして検索結果を表示するTextウィジェットが垂直に配置されています。
検索結果
詳しい説明は飛ばして、まず動かすことをメインにこの記事を書いてみました。
まずは動かしてFlutterが動く実感を味わってください。
全コードは下記に記載しておきます。
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: TopPage(),
);
}
}
class TopPage extends StatefulWidget {
const TopPage({Key? key}) : super(key: key);
@override
State<TopPage> createState() => _TopPageState();
}
class _TopPageState extends State<TopPage> {
final TextEditingController _controller = TextEditingController();
String _addressResult = '';
@override
void dispose() {
_controller.dispose();
super.dispose();
}
Future<void> _search() async {
final zipCode = _controller.text;
final address = await ZipCode.searchAddressFromZipCode(zipCode);
setState(() {
_addressResult = address;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('郵便番号検索'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('郵便番号を入力してください'),
const SizedBox(height: 16),
SizedBox(
width: 200,
child: TextField(
controller: _controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: '郵便番号',
),
),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _search,
child: const Text('検索'),
),
const SizedBox(height: 16),
Text('検索結果: $_addressResult'),
],
),
),
);
}
}
class ZipCode {
static Future<String> searchAddressFromZipCode(String? code) async {
Dio dio = Dio();
String mainUrl = 'https://zipcloud.ibsnet.co.jp/api/search';
Map<String, dynamic> queryParameters = {'zipcode': code};
try {
var res = await dio.get(
mainUrl,
queryParameters: queryParameters,
);
if (jsonDecode(res.data)['results'] == null) {
return jsonDecode(res.data)['message'];
} else {
var results = jsonDecode(res.data)['results'][0];
var address = results['address1'] + results['address2'] +
results['address3'];
return address;
}
} catch (e) {
print("エラーです");
return "郵便番号が正しくありません";
}
}
}
追伸
今回のアプリの詳細な考え方とAPI通信する上での基本的な情報を「Flutterで郵便番号検索アプリを作ってみよう(解説編)」でご紹介していますので、よろしければそちらのページもご覧下さい。
コメント