0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Flutter で郵便番号・住所検索を簡単に実装する — japan_post_api_client

0
Last updated at Posted at 2026-03-04

はじめに

Flutter アプリで住所入力フォームを実装する際、郵便番号から住所を自動補完する機能はよく求められます。
日本郵便が提供する郵便番号検索 API を利用すれば実現できますが、トークン管理や API の呼び出し方法を自前で実装するのは手間がかかります。

japan_post_api_client は、日本郵便 API のクライアントを Flutter 向けにラップしたパッケージです。
トークンの自動取得・管理や、シンプルなインターフェースで郵便番号・住所の検索が簡単に行えます。

デモ


インストール

# pubspec.yaml
dependencies:
  japan_post_api_client: ^1.0.5

公開 IP アドレスの取得に public_ip_address パッケージも合わせて使うと便利です(後述)。

  public_ip_address: any

事前準備:日本郵便 API の認証情報

日本郵便の API を利用するには、クライアント ID とシークレットキーが必要です。
また、トークン取得時にデバイスのパブリック IP アドレスが必要になります。


基本的な使い方

初期化

import 'package:japan_post_api_client/japan_post_api_client.dart';

final apiClient = JapanPostApiClient(
  clientId: 'YOUR_CLIENT_ID',
  secretKey: 'YOUR_SECRET_KEY',
);

トークンの取得

API を呼び出す前に、まずトークンを取得します。
public_ip_address パッケージでパブリック IP を取得する方法が簡単です。

import 'package:public_ip_address/public_ip_address.dart';

final ipResult = await IpAddress().getIpAddress();
final ipAddress = ipResult.ip;

await apiClient.getToken(ipAddress);

郵便番号で住所を検索する

searchByPostalCode() で郵便番号から住所情報を取得できます。

final result = await apiClient.searchByPostalCode('1000001');

switch (result) {
  case ApiResultSuccess(:final data):
    for (final address in data) {
      print('${address.prefecture} ${address.city} ${address.town}');
    }
  case ApiResultFailure(:final error):
    print('エラー: ${error.message}');
}

ハイフンあり・なしどちらの形式にも対応しています。

await apiClient.searchByPostalCode('100-0001'); // ハイフンあり
await apiClient.searchByPostalCode('1000001');  // ハイフンなし

住所から郵便番号を検索する

searchByAddress() で都道府県・市区町村・町域名から郵便番号を逆引き検索できます。

final result = await apiClient.searchByAddress(
  prefecture: '東京都',
  city: '千代田区',
  town: '千代田',
);

switch (result) {
  case ApiResultSuccess(:final data):
    for (final address in data) {
      print('〒${address.postalCode} ${address.prefecture}${address.city}${address.town}');
    }
  case ApiResultFailure(:final error):
    print('エラー: ${error.message}');
}

エラーハンドリング:ApiResult sealed class

戻り値は ApiResult という sealed class になっており、Dart 3 のパターンマッチングで安全に処理できます。

sealed class ApiResult<T> {}

class ApiResultSuccess<T> extends ApiResult<T> {
  final T data;
}

class ApiResultFailure<T> extends ApiResult<T> {
  final ApiError error;
}

switch 式や if-case で網羅的に処理できるため、エラーの取りこぼしがありません。

// switch 式で使う例
final message = switch (result) {
  ApiResultSuccess(:final data) => '${data.length}件見つかりました',
  ApiResultFailure(:final error) => 'エラー: ${error.message}',
};

実装例:住所入力フォームへの組み込み

郵便番号入力に連動して住所を自動補完する例です。

class AddressForm extends StatefulWidget {
  @override
  State<AddressForm> createState() => _AddressFormState();
}

class _AddressFormState extends State<AddressForm> {
  final _postalCodeController = TextEditingController();
  final _prefectureController = TextEditingController();
  final _cityController = TextEditingController();
  final _townController = TextEditingController();

  late final JapanPostApiClient _apiClient;

  @override
  void initState() {
    super.initState();
    _apiClient = JapanPostApiClient(
      clientId: 'YOUR_CLIENT_ID',
      secretKey: 'YOUR_SECRET_KEY',
    );
    _initToken();
  }

  Future<void> _initToken() async {
    final ip = (await IpAddress().getIpAddress()).ip;
    await _apiClient.getToken(ip);
  }

  Future<void> _searchAddress(String postalCode) async {
    if (postalCode.replaceAll('-', '').length != 7) return;

    final result = await _apiClient.searchByPostalCode(postalCode);

    if (result case ApiResultSuccess(:final data) when data.isNotEmpty) {
      final address = data.first;
      setState(() {
        _prefectureController.text = address.prefecture;
        _cityController.text = address.city;
        _townController.text = address.town;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(
          controller: _postalCodeController,
          decoration: const InputDecoration(labelText: '郵便番号'),
          keyboardType: TextInputType.number,
          onChanged: _searchAddress,
        ),
        TextField(
          controller: _prefectureController,
          decoration: const InputDecoration(labelText: '都道府県'),
        ),
        TextField(
          controller: _cityController,
          decoration: const InputDecoration(labelText: '市区町村'),
        ),
        TextField(
          controller: _townController,
          decoration: const InputDecoration(labelText: '町域'),
        ),
      ],
    );
  }
}

対応プラットフォーム

プラットフォーム 対応
iOS
Android
macOS
Windows
Linux
Web ❌(dart:io 依存のため)

まとめ

japan_post_api_client を使うことで、日本郵便 API のトークン管理や通信処理を気にせず、郵便番号・住所検索機能を Flutter アプリに簡単に組み込めます。

  • 郵便番号 → 住所の正引き検索
  • 都道府県・市区町村・町域名 → 郵便番号の逆引き検索
  • ApiResult sealed class による型安全なエラーハンドリング
  • pub points 160/160 の高品質パッケージ

住所入力フォームの実装にぜひ活用してみてください。


リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?