マグネテック備忘録

Flutterアプリ開発の備忘録

【Flutter】ローカルデータベース(sqflite)



概要

準備

パッケージの追加

dependencies:
  sqflite: ^2.3.0

インポート文

import 'package:sqflite/sqflite.dart';





基本操作

データベースの作成&削除

  • 以下の手順ではここで生成したテーブルを使用します
  • ファイル名を指定してテーブルを構築します。
  • getDatabasesPathはデフォルトのデータベース保存用フォルダのパスを取得できるとのこと。
  • 生成されるテーブル(テーブル名:Test)
id(主キー) name age
// getDatabasesPathを取得
var databasesPath = await getDatabasesPath();
String path = '$databasesPath/demo.db';

// データベースを開く(pathに存在しなければ新規作成)
Database database = await openDatabase(
  path, 
  version: 1,
  onCreate: (Database db, int version) async {
    await db.execute(
        'CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)'
    );
});


ちなみに作成したテーブルは以下の操作で削除できます。

await deleteDatabase(path);



データの追加(rawInsert, insert)

  • (1) rawInsert(SQL文を使った方法)と(2) insert(Mapとかを引数に持つ関数を指定)の2つの方法がある。
  • どちらの場合でもidを未指定の場合は連番で自動的に決定される。


(1)rawInsert(SQL文を使った方法)

  • SQL文を文字列として認識する方法と、数値などの引数のみ外に出す方法の2つがあります。
// SQLを文字列として認識
await database.rawInsert(
  'INSERT INTO Test(name, age) VALUES("アリス", 22)'
);

// 引数のみ外で指定
await database.rawInsert(
  'INSERT INTO Test(name, age) VALUES(?, ?)', 
  ['マイク', 1234]
);

実行後のテーブル

id(主キー) name age
1 アリス 22
2 マイク 1234


(2)insert(Mapとかを引数に持つ関数を指定)

  • テーブル名、追加したいデータ(Map型)を指定します(ほかの引数は公式ドキュメントを参考にしてください)。
await database.insert(
  'Test', 
  {'name': 'ケビン', 'age': 48},
);

// idを明示して追加((1), (2)どちらでも可能)
await database.insert(
  'Test', 
  {'id': 13, 'name': 'ブラック', 'age': 4},
);

実行後のテーブル(赤字が追加データ)

id(主キー) name age
1 アリス 22
2 マイク 1234
3 ケビン 48
13 ブラック 4



データ更新(rawUpdate, update)

  • テーブルは先ほどの続きです。
  • 追加と同様に(1) rawUpdate(SQL文を使った方法)と(2) update(関数利用)の2つの方法がある。


(1)rawUpdate(SQL文を使った方法)

  • 追加と同様にSQL文を文字列として認識する方法と、数値などの引数のみ外に出す方法の2つがありますが、後者のみ載せています。
// アリスを更新
await database.rawUpdate(
  'UPDATE Test SET name = ?, age = ? WHERE name = ?',
  ['エミリー', 98, 'アリス']
);

実行後のテーブル(変更したデータのみ赤字)

id(主キー) name age
1 エミリー 98
2 マイク 1234
3 ケビン 48
13 ブラック 4


(2)update(関数利用)

  • 引数はテーブル名、変更後のデータ(Map型)、検索に使用するカラム名、削除したいid, nameなどの数値です。(ほかの引数は公式ドキュメントを参考にしてください)。
// 「id=3」を更新
await database.update(
  'Test', 
  {'id': 4, 'name': 'ジム'}, 
  where: 'id = ?', 
  whereArgs: [3]
);

実行後のテーブル(変更したデータのみ赤字)

id(主キー) name age
1 エミリー 98
2 マイク 1234
ジム 48
13 ブラック 4



データの削除(rawDelete, delete)

  • テーブルは先ほどの続きです。
  • 追加と同様に(1) rawDelete(SQL文を使った方法)と(2) delete(関数利用)の2つの方法がある。


(1)rawDelete(SQL文を使った方法)

  • 追加と同様にSQL文を文字列として認識する方法と、数値などの引数のみ外に出す方法の2つがあります。
// 「name = ケビン」を削除
await database.rawDelete(
  'DELETE FROM Test WHERE name = ?', ['ケビン']
);

// 「age = 4」(ブラック)を削除
await database.rawDelete(
  'DELETE FROM Test WHERE age = 4'
);

実行後のテーブル

id(主キー) name age
1 アリス 22
2 マイク 1234


(2)delete(関数利用)

  • 引数はテーブル名、削除に使うカラム、削除したいid, nameなどの数値です。(ほかの引数は公式ドキュメントを参考にしてください)。
// 「id = 2」(マイク)を削除
await database.delete(
  'Test',
  where: 'id = ?', 
  whereArgs: [2]

実行後のテーブル

id(主キー) name age
1 アリス 22



テーブルの表示

List<Map> list = await database.rawQuery('SELECT * FROM Test');
print(list);





デモアプリ

  • データの追加・削除・更新ができる簡単なアプリを製作しました。
  • idが同じデータを追加するときにエラーが出るなどはありますがご容赦ください。

ソースコード

入力フォーム

import 'package:flutter/material.dart';

class DataInputForm extends StatelessWidget {

  final Function(int id) updateId;
  final Function(String name) updateName;
  final Function(int age) updateAge;

  const DataInputForm({super.key, required this.updateId, required this.updateName, required this.updateAge});

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        TextFormField(
          keyboardType: TextInputType.number,
          onChanged: (value) => updateId(int.parse(value)),
          decoration: const InputDecoration(
            labelText: 'id',
            border: OutlineInputBorder(),
          )
        ),

        const SizedBox(height: 10),

        TextFormField(
          keyboardType: TextInputType.name,
          onChanged: (value) => updateName(value),
          decoration: const InputDecoration(
            labelText: 'name',
            border: OutlineInputBorder(),
          )
        ),

        const SizedBox(height: 10),

        TextFormField(
          keyboardType: TextInputType.number,
          onChanged: (value) => updateAge(int.parse(value)),
          decoration: const InputDecoration(
            labelText: 'age',
            border: OutlineInputBorder(),
          )
        ),
      ],
    );
  }
}


メイン画面

import 'package:flutter/material.dart';
import 'data_input_form.dart';
import 'package:sqflite/sqflite.dart';

// データベースのパスとオブジェクト
late String path;
late Database database;
List<Map> listMap = [];

void main() async {

  // これがないと「 Unhandled Exception: Binding has not yet been initialized.」というエラーが出る
  WidgetsFlutterBinding.ensureInitialized();

  // パスを取得
  var databasesPath = await getDatabasesPath();
  path = '$databasesPath/demo.db';

  // データベースを開く
  database = await openDatabase(
    path, 
    version: 1,
    onCreate: (Database db, int version) async {
      await db.execute(
          'CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)'
      );
  });

  final app = MaterialApp(
    title: 'Flutter Demo',
    theme: ThemeData(
      colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      useMaterial3: true,
    ),
    home: const MyHomePage(),
  );

  runApp(app);

}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  int? id;
  String? name;
  int? age;

  void updateId(int id){
    setState(() {
      this.id = id;
    });
  }

  void updateName(String name){
    setState(() {
      this.name = name;
    });
  }

  void updateAge(int age){
    setState(() {
      this.age = age;
    });
  }

  void updateList() async {
    listMap = await database.rawQuery('SELECT * FROM Test');
    setState(() {});
  }

  @override
  void initState() {
    super.initState();

    // リストを初期化
    updateList();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            // データベースを削除
            ElevatedButton(
              onPressed: () async {
                // データベースを削除
                await deleteDatabase(path);

                // データベースを再度開く
                database = await openDatabase(
                  path, 
                  version: 1,
                  onCreate: (Database db, int version) async {
                    await db.execute(
                        'CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)'
                    );
                });

                // 表示を更新
                listMap = [];
                setState(() {});
              }, 
              child: const Text('データベースを初期化'),
            ),

            // Id, name, numを入力
            DataInputForm(
              updateId: updateId, 
              updateName: updateName, 
              updateAge: updateAge
            ),

            // id, name, numのいずれも埋まっていたらinsert, updateが可能 
            if(id!=null)...{

              // 削除
              ElevatedButton(
                onPressed: () async{
                  await database.delete(
                    'Test',
                    where: 'id = ?', 
                    whereArgs: [id]
                  );

                  // 表示を更新
                  updateList();
                }, 
                child: const Text('id指定でdelete'),
              ),

              if(name!=null && age!=null)...{

                // 更新
                ElevatedButton(
                  onPressed: () async{
                    await database.update(
                      'Test', 
                      {'name': name, 'age': age}, 
                      where: 'id = ?', 
                      whereArgs: [id]
                    );

                    // 表示を更新
                    updateList();
                  }, 
                  child: const Text('id指定でupdate'),
                ),

                // 追加
                ElevatedButton(
                  onPressed: () async{
                    await database.insert(
                      'Test', 
                      {'id': id, 'name': name, 'age': age},
                    );

                    // 表示を更新
                    listMap = await database.rawQuery('SELECT * FROM Test');
                    setState(() {});
                  }, 
                  child: const Text('insert'),
                )
              },
            },

            // データを表示
            const Text(
              'データ表示',
              style: TextStyle(
                fontSize: 20
              ),
            ),
            for (var map in listMap) ...{
              Text('id = ${map["id"]}, name = ${map["name"]}, age = ${map["age"]}'),
            }
         ],
        ),
      ),
    );
  }
}



実行画面

実行画面