[基礎知識]Androidプラットフォーム環境向け/sqfliteパッケージによるSQLiteの利用手順

flutter開発でsqflite系パッケージでAndroidプラットフォーム環境向けにSQLite操作を実装する場合の手順です。

今回は前回記事「[基礎知識]Windowsプラットフォーム環境向け/sqflite_common_ffiパッケージによるSQLiteの利用手順」の続きになっています。

1.Flutterプロジェクト作成

(1) ターミナルを開き、以下のコマンドを実行して新しいプロジェクトを作成します。

flutter create sqlite_example

(2) プロジェクトフォルダに移動します。

cd sqlite_example

2 必要な パッケージの追加

Androidプラットフォーム環境で、SQLiteを使用するために必要なパッケージ(次の2つ)を追加します。

  • sqflite
  • path

(1) プロジェクトフォルダで次のコマンドによりsqfliteパッケージとpathパッケージ(の依存関係)を追加します。

flutter pub add sqflite
flutter pub add path

(2) pubspec.yamlファイルを開き、上記パッケージ(の依存関係)が追加されていることを確認します。

(修正後)pubspec.yaml:
dependencies:
  flutter:
    sdk: flutter

  sqflite: ^2.3.3+1
  path: ^1.9.0
  sqflite_common_ffi: ^2.3.3

(3) ターミナルで以下のコマンドを実行してパッケージをインストール(反映)します。

flutter pub get

(4) ※但しSQLite操作と関係なく元々必要なパッケージ(これは処理内容によりますが)は上記説明に含めていません。
 (例)material.dart、foundation.dart、等

3.SQLiteデータベースの設定と使用

以下は、SQLiteデータベースを設定し、使用するためのサンプルコードです。

3.1 データベースヘルパークラスの作成

まず、データベース操作を行うためのヘルパークラスを作成(修正)します。
(実際には、冒頭にも書きましたが前回記事の続きなので、修正前のコードの説明は、そちらでご参照下さい)

(1) lib\database_helper.dartファイルを作成(修正)します。

(修正後)database_helper.dart:
// SQLite操作(Windows環境,android環境共通(条件分岐済))データヘルパー:
// Web環境未対応

import 'package:path/path.dart';  // ファイルパスを操作するためのパッケージ
import 'package:sqflite/sqflite.dart';  // (androidで必要)
import 'package:sqflite_common_ffi/sqflite_ffi.dart'; // (Windows環境用)FFI

// DB_HELPERクラス
class DatabaseHelper {
  // シングルトンインスタンス保持変数
  static final DatabaseHelper _instance = DatabaseHelper._internal();
  // ファクトリコンストラクタ : クラスの新しいインスタンスを返すのではなく、既存のインスタンスを返すことができます。
  factory DatabaseHelper() => _instance;
  // プライベートコンストラクタ : このクラスの外部からインスタンスを作成できないようにするためのものです。
  DatabaseHelper._internal();
  // DBインスタンス : データベースの接続を保持する変数です。
  static Database? _database;

  // データベースのインスタンスを取得(存在しない場合は初期化)
  // ※database: getter定義プロパティ
  //   get: これを定義する為のgetterメソッド
  Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDatabase();
    return _database!;
  }

  // データベースを初期化するメソッド
  Future<Database> _initDatabase() async {
    String path = '';

    var databasePath = await getDatabasesPath();
    path = join(databasePath, 'SqliteExampleForWindowsAndroid.db');
    // データベースを開く(存在しない場合は作成)
    return await openDatabase(
      path,
      version: 1,
      onCreate: _onCreate, // データベースが作成された時に実行されるメソッド
    );
  }

  // データベーステーブルを作成するメソッド
  Future _onCreate(Database db, int version) async {
    await db.execute(
      '''
      CREATE TABLE items (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT
      )
      '''
    );
  }

  // アイテムを取得するメソッド
  Future<List<Map<String, dynamic>>> getItems() async {
    final db = await database;  // データベースのインスタンスを取得
    return await db.query('items'); // 'items' テーブルからすべてのデータを取得
  }

  // アイテムを追加するメソッド
  Future<void> insertItem(Map<String, dynamic> item) async {
    final db = await database;  // データベースのインスタンスを取得
    await db.insert('items', item, conflictAlgorithm: ConflictAlgorithm.replace);
  }

  // アイテムを削除するメソッド
  Future<void> deleteItem(int id) async {
    final db = await database;
    await db.delete('items', where: 'id = ?', whereArgs: [id]);
  }

  // アイテムを更新するメソッド
  Future<void> updateItem(Map<String, dynamic> item) async {
    final db = await database;
    await db.update('items', item, where: 'id = ?', whereArgs: [item['id']]);
  }

}

3.2 本処理の作成

次に本処理を作成してSQLiteデータベースを利用します。

(1) lib\main.dartファイルを開き、以下の様に書き換えます。

(修正後)main.dart:
// SQLite操作(Windows環境,android環境共通(条件分岐済))メインプログラム:
// Web環境未対応

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart'; // (Windows環境用)FFI

// データベースヘルパーのインポート
import 'database_helper.dart'; 

// DB環境初期化関数(Platform別)
void initDatabase() {
  // Windows対象時(ffi初期化が必要)
  if (defaultTargetPlatform == TargetPlatform.windows || defaultTargetPlatform == TargetPlatform.linux) {
    sqfliteFfiInit();
    databaseFactory = databaseFactoryFfi;
  }
  // Android対象時(ffi初期化は不要)
  // ※Android対象時、sqflite パッケージが内部で自動的にネイティブのSQLiteライブラリを使用します。
}

Future<void> main() async {
  // DB環境初期化関数(Platform別)呼出
  initDatabase();

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SQLite Example for Windows・Android',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  MyHomePageState createState() => MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  // DB_HELPERのインスタンス
  final DatabaseHelper _databaseHelper = DatabaseHelper();
  // テキストフィールドのコントローラ
  final TextEditingController _controller = TextEditingController();
  // 取得したアイテムを格納するリスト
  List<Map<String, dynamic>> _items = [];

  @override
  void initState() {
    super.initState();
    _loadItems(); // アイテムをロード
  }

  // データベースからアイテムを読み込むメソッド
  Future<void> _loadItems() async {
    final items = await _databaseHelper.getItems();
    setState(() {
      _items = items;
    });
  }

  // アイテムを追加するメソッド
  Future<void> _addItem(String name) async {
    await _databaseHelper.insertItem({'name': name});
    _controller.clear(); // テキストフィールドをクリア
    _loadItems(); // アイテムを再読み込み
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SQLite Example for Windows・Android'),
      ),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: TextField(
              controller: _controller,
              decoration: InputDecoration(
                labelText: 'Item Name',
                suffixIcon: IconButton(
                  icon: const Icon(Icons.add),
                  onPressed: () {
                    if (_controller.text.isNotEmpty) {
                      _addItem(_controller.text); // アイテムを追加
                    }
                  },
                ),
              ),
            ),
          ),
          Expanded(
            child: ListView.builder(
              itemCount: _items.length, // アイテムの数
              itemBuilder: (context, index) {
                return ListTile(
                  title: Text(_items[index]['name']), // アイテムの名前を表示
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

4. 起動・動作確認

4.1 Windows向けデバッグモード実行

これについては前回記事(「[基礎知識]Windowsプラットフォーム環境向け/sqflite_common_ffiパッケージによるSQLiteの利用手順」の『(1) Windows向けデバッグモード実行』)を参照願います。

4.2 Android向けデバッグモード実行

Android向けアプリの動作確認にはUSB接続やEmulator利用の方法がありますが、ここではEmulatorを利用します。
Emulator起動方法もGUI(Android Studio)利用やCUI利用の方法がありますが、ここではCUIを利用します。
(但し仮想デバイス(AVD)は既に作成済みであることとします)

(1) ビルド実行

まだ動作確認段階ではありますが一旦ビルドします。

> flutter build apk

(2) 利用可能なAVD確認

> emulator-list-avds

・・・(ここに利用可能なAVDが表示されます)・・・

(3) AVD起動

> emulator -avd <上記で表示されたAVD名>

(4) Flutterアプリの実行

> flutter run

Launching lib\main.dart on sdk gphone64 x86 64 in debug mode...
Running Gradle task 'assembleDebug'...                             13.7s
√ Built build\app\outputs\flutter-apk\app-debug.apk
Syncing files to device sdk gphone64 x86 64...                      83ms

Flutter run key commands.
r Hot reload.
R Hot restart.
h List all available interactive commands.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).

A Dart VM Service on sdk gphone64 x86 64 is available at: http://127.0.0.1:60181/qC4EnS4Hbig=/
The Flutter DevTools debugger and profiler on sdk gphone64 x86 64 is available at:
http://127.0.0.1:9102?uri=http://127.0.0.1:60181/qC4EnS4Hbig=/
D/EGL_emulation(14225): app_time_stats: avg=6318.89ms min=4973.32ms max=7664.46ms count=2
…(ここまで表示されてからアプリが起動開始します)…
…(ちなみに、ここの画面で、キーボードから、CTRL + C を入力するとアプリが停止します)…

①起動確認

②データ入力

③再起動

次の方法で一旦アプリを終了し、
(アプリ終了方法)
 ・上記emulator画面でアプリを終了させる、
 ・上記(4)のコマンドラインを終了させる、
 ・上記(4)の画面でCTRL + Zにより終了させる、等

その後、再度、flutter run を実行して、アプリを再起動します。

このように再起動前に登録したデータが再表示されていれば成功です。

コメントを残す