【Flutter】ローカルデータベース | SQLite使い方を解説!【コピペOK】

Flutterでローカルデータベースの使い方法を知りたい
FlutterでSQLiteの使い方を知りたい。

そこで、今回はFlutterSQLiteにおける基本的な使い方サンプルコードデモアプリで解説します。

不明点などありましたら、お気軽にお問い合わせ下さい。

この記事で分かること!
  • SQLiteって何?
  • SQLiteの使い方
  • SQLiteの基本プロパティ

ちなみに、プログラミング初心者は疑問やエラーの解消に時間が掛かり、学習が前に進まなくて挫折することはよくあります。独学でFlutterを学習する場合は、次のことも考えないといけません。

  • 学習の計画
  • スケジュール管理
  • ポートフォリオ作成時に技術選定 など

初心者にとっては大変です。そこで、自走できるまでの手段にスクールを利用するのは1つの手です!Flutterが学べるおすすめスクールはこちらで詳しく解説してます。月2万ほど〜学べるスクールもあるのでご参考にどうぞ。

目次

【Flutter】SQLiteとは?ローカルデータベースが使えるDB

SQLiteはDBサーバーへのアクセスなしで管理できるローカルDB。SQLiteは全てのスマホで使われて、世界で1番利用されるDBです。

SQLiteの特徴
  • リレーショナルデータベース管理システム (RDBMS)
  • C言語で書かれてる
  • 軽量なため、さまざまなプラットフォームで動作する
  • オープンソース、誰でも無料で使用可能

SQLiteとは

サーバとしてではなくアプリケーションに組み込んで利用されるデータベース。 一般的なRDBMSと違い、APIは単純にライブラリを呼び出すだけであり、データの保存に単一のファイルのみを使用することが特徴である。

引用:SQLite公式サイト

【Flutter】SQLiteの使い方とデモアプリのサンプルコードを解説

上のデモ動画では、各ボタンをクリックするとデータの登録、照会、更新、削除がされ、コンソールに処理結果を表示するシンプルな作りです。

SQLiteの実装手順は以下の通りです。

  1. SQLiteを操作するためのパッケージをインストール・インポート
  2. データベース作成・テーブル作成
  3. データ登録 | insertメソッドを使用
  4. データ照会 | queryメソッドを使用
  5. データ更新 | updateメソッドを使用
  6. データ削除 | deleteメソッドを使用

デモアプリで利用するライブラリ/パッケージ(SQLite以外)

スクロールできます
ライブラリ内容
dart:ioDart言語で使用できる I/O (Input/Output) ライブラリ。このライブラリを使用すると、Dart のアプリケーションからファイルやネットワークを操作することができます。
pathDart のコアライブラリに含まれているもので、ファイルやディレクトリのパスを扱うためのユーティリティ関数を提供。
例)ファイルやディレクトリのパスを操作(パスの結合・分解・拡張子を取得など)
  ファイルやディレクトリの名前を操作(ファイル名取得、ディレクトリ名取得など)
path_providerアプリがデータを保存するためのパスや、デバイスの一般的なパスを取得することができる。

デモアプリのファイル構成

main.dartファイルと、database_helperファイルに実装します。database_helperファイルにDB接続やデータ更新処理などを記述します。

SQLiteを操作するためのパッケージをインストール・インポート

SQLiteのプラグインsqfliteをpubspec.yamlファイルに定義します。

dependencies:
  flutter:
    sdk: flutter
  sqflite: ^2.2.0+3
  path_provider: ^2.0.11

sqfliteをインポートする。

import 'package:sqflite/sqflite.dart';

データベース作成・テーブル作成

データベースのデータを保存するディレクトリパスの取得から、データベース接続・初期化、テーブル作成をします。

各処理にコメントをつけておりますので、処理内容の詳細はそちらからご確認ください。

import 'dart:io';

import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';

class DatabaseHelper {

  static final _databaseName = "MyDatabase.db"; // DB名
  static final _databaseVersion = 1; // スキーマのバージョン指定

  static final table = 'my_table'; // テーブル名

  static final columnId = '_id'; // カラム名:ID
  static final columnName = 'name'; // カラム名:Name
  static final columnAge = 'age'; // カラム名:age

  // DatabaseHelper クラスを定義
  DatabaseHelper._privateConstructor();
  // DatabaseHelper._privateConstructor() コンストラクタを使用して生成されたインスタンスを返すように定義
  // DatabaseHelper クラスのインスタンスは、常に同じものであるという保証
  static final DatabaseHelper instance = DatabaseHelper._privateConstructor();

  // Databaseクラス型のstatic変数_databaseを宣言
  // クラスはインスタンス化しない
  static Database? _database;

  // databaseメソッド定義
  // 非同期処理
  Future<Database?> get database async {
    // _databaseがNULLか判定
    // NULLの場合、_initDatabaseを呼び出しデータベースの初期化し、_databaseに返す
    // NULLでない場合、そのまま_database変数を返す
    // これにより、データベースを初期化する処理は、最初にデータベースを参照するときにのみ実行されるようになります。
    // このような実装を「遅延初期化 (lazy initialization)」と呼びます。
    if (_database != null) return _database;
    _database = await _initDatabase();
    return _database;
  }

  // データベース接続
  _initDatabase() async {
    // アプリケーションのドキュメントディレクトリのパスを取得
    Directory documentsDirectory = await getApplicationDocumentsDirectory();
    // 取得パスを基に、データベースのパスを生成
    String path = join(documentsDirectory.path, _databaseName);
    // データベース接続
    return await openDatabase(path,
        version: _databaseVersion,
        // テーブル作成メソッドの呼び出し
        onCreate: _onCreate);
  }

  // テーブル作成
  // 引数:dbの名前
  // 引数:スキーマーのversion
  // スキーマーのバージョンはテーブル変更時にバージョンを上げる(テーブル・カラム追加・変更・削除など)
  Future _onCreate(Database db, int version) async {
    await db.execute('''
          CREATE TABLE $table (
            $columnId INTEGER PRIMARY KEY,
            $columnName TEXT NOT NULL,
            $columnAge INTEGER NOT NULL
          )
          ''');
  }

データ登録 | insertメソッドを使用

データ登録処理のサンプルコードは以下の通りです。main.dartファイルの処理からdatabase_helper.dartファイルの処理を呼び出してデータの登録を行います。

insert メソッドは、非同期で実行され、データベースにレコード追加処理をして、追加レコードの ID を返します。

 void _insert() async {
    Map<String, dynamic> row = {
      DatabaseHelper.columnName : '山田 太郎',
      DatabaseHelper.columnAge  : 35
    };
    final id = await dbHelper.insert(row);
    print('登録しました。id: $id');
  }

insert メソッドは、非同期で実行され、データベースにレコード追加処理をして、追加レコードの ID を返します。

  Future<int> insert(Map<String, dynamic> row) async {
    Database? db = await instance.database;
    return await db!.insert(table, row);
  }

データ照会 | queryメソッドを使用

データ照会処理のサンプルコードは以下の通りです。main.dartファイルの処理からdatabase_helper.dartファイルの処理を呼び出してデータの照会を行います。

  // 照会ボタンクリック
  void _query() async {
    final allRows = await dbHelper.queryAllRows();
    print('全てのデータを照会しました。');
    allRows.forEach(print);
  }
  Future<List<Map<String, dynamic>>> queryAllRows() async {
    Database? db = await instance.database;
    return await db!.query(table);
  }

データ更新 | updateメソッドを使用

データ更新処理のサンプルコードは以下の通りです。main.dartファイルの処理からdatabase_helper.dartファイルの処理を呼び出してデータの更新を行います。

  // 更新ボタンクリック
  void _update() async {
     Map<String, dynamic> row = {
      DatabaseHelper.columnId   : 1,
      DatabaseHelper.columnName : '鈴木 一郎',
      DatabaseHelper.columnAge  : 48
    };
    final rowsAffected = await dbHelper.update(row);
    print('更新しました。 ID:$rowsAffected ');
  }
  Future<int> update(Map<String, dynamic> row) async {
    Database? db = await instance.database;
    int id = row[columnId];
    return await db!.update(table, row, where: '$columnId = ?', whereArgs: [id]);
  }

データ削除 | deleteメソッドを使用

データ削除処理のサンプルコードは以下の通りです。main.dartファイルの処理からdatabase_helper.dartファイルの処理を呼び出してデータの削除を行います。

  // 削除ボタンクリック
  void _delete() async {
    final id = await dbHelper.queryRowCount();
    final rowsDeleted = await dbHelper.delete(id!);
    print('削除しました。 $rowsDeleted ID: $id');
  }
 // レコード数を確認
 Future<int?> queryRowCount() async {
    Database? db = await instance.database;
    return Sqflite.firstIntValue(await db!.rawQuery('SELECT COUNT(*) FROM $table'));
 }  
 Future<int> delete(int id) async {
    Database? db = await instance.database;
    return await db!.delete(table, where: '$columnId = ?', whereArgs: [id]);
 }

【Flutter】SQLiteのデモアプリのサンプルコード

デモ動画画面のソースコードは以下の通りです。

クリックするとサンプルコードが表示(main.dart)
import 'package:flutter/material.dart';
import 'database_helper.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
       home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {

  // DatabaseHelper クラスのインスタンス取得
  final dbHelper = DatabaseHelper.instance;

  // homepage layout
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('SQLiteデモ'),
      ),
      body: Center(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            ElevatedButton(
              child: Text('登録', style: TextStyle(fontSize: 35),),
              onPressed: _insert,
            ),
            ElevatedButton(
              child: Text('照会', style: TextStyle(fontSize: 35),),
              onPressed: _query,
            ),
            ElevatedButton(
              child: Text('更新', style: TextStyle(fontSize: 35),),
              onPressed: _update,
            ),
            ElevatedButton(
              child: Text('削除', style: TextStyle(fontSize: 35),),
              onPressed: _delete,
            ),
          ],
        ),
      ),
    );
  }

  // 登録ボタンクリック
  void _insert() async {
    // row to insert
    Map<String, dynamic> row = {
      DatabaseHelper.columnName : '山田 太郎',
      DatabaseHelper.columnAge  : 35
    };
    final id = await dbHelper.insert(row);
    print('登録しました。id: $id');
  }

  // 照会ボタンクリック
  void _query() async {
    final allRows = await dbHelper.queryAllRows();
    print('全てのデータを照会しました。');
    allRows.forEach(print);
  }

  // 更新ボタンクリック
  void _update() async {
     Map<String, dynamic> row = {
      DatabaseHelper.columnId   : 1,
      DatabaseHelper.columnName : '鈴木 一郎',
      DatabaseHelper.columnAge  : 48
    };
    final rowsAffected = await dbHelper.update(row);
    print('更新しました。 ID:$rowsAffected ');
  }

  // 削除ボタンクリック
  void _delete() async {
    final id = await dbHelper.queryRowCount();
    final rowsDeleted = await dbHelper.delete(id!);
    print('削除しました。 $rowsDeleted ID: $id');
  }
}

クリックするとサンプルコードが表示(database_helper.dart)
import 'dart:io';

import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';

class DatabaseHelper {

  static final _databaseName = "MyDatabase.db"; // DB名
  static final _databaseVersion = 1; // スキーマのバージョン指定

  static final table = 'my_table'; // テーブル名

  static final columnId = '_id'; // カラム名:ID
  static final columnName = 'name'; // カラム名:Name
  static final columnAge = 'age'; // カラム名:age

  // DatabaseHelper クラスを定義
  DatabaseHelper._privateConstructor();
  // DatabaseHelper._privateConstructor() コンストラクタを使用して生成されたインスタンスを返すように定義
  // DatabaseHelper クラスのインスタンスは、常に同じものであるという保証
  static final DatabaseHelper instance = DatabaseHelper._privateConstructor();

  // Databaseクラス型のstatic変数_databaseを宣言
  // クラスはインスタンス化しない
  static Database? _database;

  // databaseメソッド定義
  // 非同期処理
  Future<Database?> get database async {
    // _databaseがNULLか判定
    // NULLの場合、_initDatabaseを呼び出しデータベースの初期化し、_databaseに返す
    // NULLでない場合、そのまま_database変数を返す
    // これにより、データベースを初期化する処理は、最初にデータベースを参照するときにのみ実行されるようになります。
    // このような実装を「遅延初期化 (lazy initialization)」と呼びます。
    if (_database != null) return _database;
    _database = await _initDatabase();
    return _database;
  }

  // データベース接続
  _initDatabase() async {
    // アプリケーションのドキュメントディレクトリのパスを取得
    Directory documentsDirectory = await getApplicationDocumentsDirectory();
    // 取得パスを基に、データベースのパスを生成
    String path = join(documentsDirectory.path, _databaseName);
    // データベース接続
    return await openDatabase(path,
        version: _databaseVersion,
        // テーブル作成メソッドの呼び出し
        onCreate: _onCreate);
  }

  // テーブル作成
  // 引数:dbの名前
  // 引数:スキーマーのversion
  // スキーマーのバージョンはテーブル変更時にバージョンを上げる(テーブル・カラム追加・変更・削除など)
  Future _onCreate(Database db, int version) async {
    await db.execute('''
          CREATE TABLE $table (
            $columnId INTEGER PRIMARY KEY,
            $columnName TEXT NOT NULL,
            $columnAge INTEGER NOT NULL
          )
          ''');
  }

  // 登録処理
  Future<int> insert(Map<String, dynamic> row) async {
    Database? db = await instance.database;
    return await db!.insert(table, row);
  }

  // 照会処理
  Future<List<Map<String, dynamic>>> queryAllRows() async {
    Database? db = await instance.database;
    return await db!.query(table);
  }

  // レコード数を確認
   Future<int?> queryRowCount() async {
    Database? db = await instance.database;
    return Sqflite.firstIntValue(await db!.rawQuery('SELECT COUNT(*) FROM $table'));
  }

  // 更新処理
   Future<int> update(Map<String, dynamic> row) async {
    Database? db = await instance.database;
    int id = row[columnId];
    return await db!.update(table, row, where: '$columnId = ?', whereArgs: [id]);
  }

  // 削除処理
   Future<int> delete(int id) async {
    Database? db = await instance.database;
    return await db!.delete(table, where: '$columnId = ?', whereArgs: [id]);
  }
}

まとめ:FlutterのSQLiteはローカルDBを利用したい方は学習しときましょう!

今回はFlutterSQLiteにおける基本的な使い方サンプルコードデモ画面で解説しました。

オリジナルアプリ開発で、ローカルデータベースを利用したい方は是非押さえておきましょう!

焼き芋

より実践的なSQLiteを使ったアプリは、Udemyの
Flutter & Dart – The Complete Guide [2023 Edition]で学習できます。

Flutterエンジニアになるには?

初心者が中級者レベルのFlutterエンジニアなるまでの進め方をまとめました。

Flutterの学習方法を知る

Flutter を動画で学ぶ(Udemy)

Flutter をスクールで学ぶ

Flutterの仕事を探す

おまけFlutter入門の完全ガイド

Flutter/Dartの基礎一覧

Flutter/Dartの入門知識として押さえておきたい内容をまとめました。学習のご参考にどうぞ。

Widget一覧

画面レイアウト

ボタン

入力・出力

ページ遷移

状態管理

非同期処理

Dartの基本文法

ライブラリ使い方

目次