kotememo

【Flutter】ローカルネット内のPostgreSQLに接続する方法

はじめに

クロスプラットフォーム開発でFlutterを見つけたのでPostgreSQLへの接続をテストしました。

AndroidスマホのFlutterアプリからローカルネット内のPostgreSQLにinsert/selectしてみました。

Flutterのサンプルアプリの+ボタンに紐づけてデバッグコンソールでinsert/select出来ることを確認しました。

概要

試した時点ではpostgresql2パッケージでのPostgreSQLパスワード方式がmd5のみ対応だったため、PostgreSQL側も設定変更します。

  1. PostgreSQL設定
  2. Flutterソース
  3. 実行結果

環境

Windows 11

  • PostgreSQL 14.6-1

Androidアプリ

  • Flutter 3.10.0-6.0.pre.19
  • postgresql2 1.0.4 (dart package)

1. PostgreSQL設定

PostgreSQLのバージョンによってはパスワードの暗号方式がscram-sha-256になっています。

しかし、postgresql2パッケージではmd5のみ対応のようですので設定変更をします。

同時に外部アクセスできる設定も行います。

1.1. postgresql.conf

localhost以外からもPostgreSQLに接続できる設定、パスワードの暗号化方式の設定を変更します。

# - Connection Settings -
listen_addresses = '*'

# - Authentication -
password_encryption = md5

1.2. pg_hba.conf

パスワード認証方式の変更、接続元IPアドレスにローカルネットを追加します。

# IPv4 local connections:
host    all             all             127.0.0.1/32            md5
host    all             all             192.168.0.0/16            md5
# IPv6 local connections:
host    all             all             ::1/128                 md5

1.3. ファイアウォール設定

[コントロールパネル>Windows ツール>セキュリティが強化されたWindows Defender ファイアウォール]のWindows設定で、PostgreSQLの使用ポートを開放します。

  1. 受信の規則>新しい規則

PostgreSQLの使用ポートを開放します。

  • 規則の種類:ポート
  • プロトコルおよびポート:TPC、特定のローカルポート「5432」※PostgreSQL起動時のポート番号
  • 操作:接続を許可する
  • プロファイル:テストなので全部にチェック「ドメイン、プライベート、パブリック」
  • 名前:PostgreSQL※好きな名前で良いため分かりやすくPostgreSQLにしています。
  1. 送信の規則>新しい規則

受信と同じ設定を送信側にも設定します。

  • 規則の種類:ポート
  • プロトコルおよびポート:TPC、特定のローカルポート「5432」※PostgreSQL起動時のポート番号
  • 操作:接続を許可する
  • プロファイル:テストなので全部にチェック「ドメイン、プライベート、パブリック」
  • 名前:PostgreSQL※好きな名前で良いため分かりやすくPostgreSQLにしています。

1.4. テーブル情報

+ボタンをタップした際に繰り上がる数字と、その時の時刻を保存するテーブルを作成します。

今回きりのtestdbデータベースにテーブル作成しています。

create table testtable (count numeric, updatetime timestamp);

2. Flutterソース

2.1. postgresql2パッケージ

pubspec.yamlにpostgresql2を追加し、Pub getします。

dependencies:
  flutter:
    sdk: flutter

  # 追加
  postgresql2:

2.2. testdao.dart

PostgreSQLとの通信をするdartファイル(lib/testdao.dart)を新規作成します。

デバッグコンソール上でinsert/select出来ていることを確認するため、サンプルからinsert/select文を引っ張ってきています。

import 'package:postgresql2/postgresql.dart';

class TestDao {
  static const String username = 'postgres';
  static const String password = 'postgres';
  static const String host = '192.168.0.19';
  static const String port = '5432';
  static const String database = 'testdb';
  static const String uri = 'postgres://$username:$password@$host:$port/$database';

  // select文
  Future<void> selectSql() async {
    var sql = "select * from testtable;";

    connect(uri).then((conn) {
      conn.query(sql).toList()
          .then((result) {
            print('result: $result');
          })
          .whenComplete(() {
            conn.close();
          });
    });
  }

  //insert文
  Future<void> insertSql(int count, DateTime updatetime) async {
    var sql = "insert into testtable values (@count, @updatetime);";

    connect(uri).then((conn) {
      conn.query(sql, {'count': count, 'updatetime': updatetime}).toList()
          .then((_) {
            print('done.');
          })
          .whenComplete(() {
            conn.close();
          });
    });
  }
}

2.2. main.dart

+ボタンに紐づいている_incrementCounter()にinsert/select文を追加します。

非同期関数でそこまで考えていないためinsert/selectが順番に処理されるとは限りません。

import 'package:flutter/material.dart';
import './testdao.dart';  // 追加

//省略......

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  TestDao dao = TestDao();  // 追加

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
      dao.insertSql(_counter, DateTime.now());  // 追加
      dao.selectSql();  // 追加
    });
  }

  //省略......
}

3. 実行結果

+ボタンを5回タップし、その時のコンソール出力を確認しました。

doneがinsert完了で、resultがselect結果となります。

insert/selectの順番はバラバラですが、select結果にカウント数字と時刻がセットされていることが確認できます。

ただし、表示されているタイムスタンプが実際よりもずれています。

Syncing files to device SOV40...
I/flutter (23040): done.
I/flutter (23040): result: [[1.0, 2023-07-03 02:29:15.849426]]
I/flutter (23040): result: [[1.0, 2023-07-03 02:29:15.849426]]
I/flutter (23040): done.
I/flutter (23040): result: [[1.0, 2023-07-03 02:29:15.849426], [2.0, 2023-07-03 02:29:19.368834]]
I/flutter (23040): done.
I/flutter (23040): result: [[1.0, 2023-07-03 02:29:15.849426], [2.0, 2023-07-03 02:29:19.368834], [3.0, 2023-07-03 02:29:21.894974]]
I/flutter (23040): done.
I/flutter (23040): result: [[1.0, 2023-07-03 02:29:15.849426], [2.0, 2023-07-03 02:29:19.368834], [3.0, 2023-07-03 02:29:21.894974], [4.0, 2023-07-03 02:29:27.428673]]
I/flutter (23040): done.

psqlでselectしてみても値がセットされていることを確認できます。

psql側のほうが日本時刻としては正しいです。

testdb=# select * from testtable;
 count |         updatetime
-------+----------------------------
     1 | 2023-07-02 17:29:15.849426
     2 | 2023-07-02 17:29:19.368834
     3 | 2023-07-02 17:29:21.894974
     4 | 2023-07-02 17:29:27.428673
     5 | 2023-07-02 17:29:29.721692
(5 行)