flutter sqflite 初歩歩の歩


自分用に書いたメモです。
間違っている部分やおかしな部分があったらコメントください！

この記事を書くにあたってこちらの２つのサイトを参考にさせていただきました。
・[FlutterのシンプルなSQFliteデータベースの例 ](https://ichi.pro/flutter-no-shinpuruna-sqflite-de-tabe-su-no-rei-252244950400913)
・[Flutterでsqliteを使ったTodoアプリを作る](https://qiita.com/popy1017/items/7ada79b07281f4a8e544)

パッケージ
・[sqflite](https://pub.dev/packages/sqflite)
・[path_provider](https://pub.dev/packages/path_provider)
・[uuid](https://pub.dev/packages/uuid)

```pubspec.yaml
  sqflite: ^2.0.0
  path_provider: ^2.0.2
  uuid: ^3.0.4
```

モデル

```lib/db/memo.dart
import 'package:uuid/uuid.dart';

class Memo {
  late int id;
  late DateTime date;
  late String note;

  Memo({
    required this.id,
    required this.date,
    required this.note,
  });

  Memo.newMemo() {
    id = Uuid().v4();    
    date = DateTime.now();
    note = "";
  }

  // SQLiteではDateTime型を保存できないためString型に変換する
  Map<String, dynamic> toMap() => {
        "id": id,
        // DateTime型を、文字列で保存する。
        "date": date.toUtc().toIso8601String(),
        "note": note,
      };

  factory Memo.fromMap(Map<String, dynamic> json) => Memo(
        id: json["id"],
        //　文字列で保存されているため、DateTime型に変換する。
        date: DateTime.parse(json["date"]).toLocal(),
        note: json["note"],
      );
}
```

DatabaseProvider

```lib/db/dbProvider.dart
import 'dart:io';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';

import 'package:article_project/db/memo.dart';

class DBProvider {
  final _databaseName = 'my_database.db';
  final _databaseVersion = 1;

  final _tableName = 'memo';
  final _columnId = 'id';
  final _columnDate = 'date';
  final _columnNote = 'note';

  DBProvider._();

  static final DBProvider instance = DBProvider._();
  Database? _database;

  // Databaseが存在するかどうか確認して、あったら返す。
  Future<Database> get database async {
    if (_database != null) return _database!;
    // Databaseがない場合に作成する。
    _database = await _initDatabase();
    return _database!;
  }

  Future<Database> _initDatabase() async {
    Directory documentDirectory = await getApplicationDocumentsDirectory();
    String path = join(documentDirectory.path, _databaseName);
    return await openDatabase(
      path,
      version: _databaseVersion,
      onCreate: _onCreate,
    );
  }

  Future<void> _onCreate(Database db, int version) async {
    await db.execute('''
    CREATE TABLE $_tableName (
    $_columnId TEXT PRIMARY KEY,
    $_columnDate TEXT NOT NULL,
    $_columnNote TEXT NOT NULL
    )''');
  }
  
  Future<int> insert(Memo row) async {
    Database db = await instance.database;
    return await db.insert(_tableName, row.toMap());
  }
  
  Future<List<Memo>> getAllRows() async {
    Database db = await instance.database;
    var res = await db.query(_tableName);
    // Databaseの中身を確認して、あった場合Listにして無い場合は空のListを返す。
    List<Memo> list = 
        res.isNotEmpty ? res.map((m) => Memo.fromMap(m)).toList() : [];
    return list;
  }

  Future<Memo?> getSelectRow(String id) async {
    Database db = await instance.database;
    List<Map> maps = await db.query(_tableName,
        columns: [_columnId, _columnDate, _columnNote],
        where: '$_columnId = ?',
        whereArgs: [id]);
    if (maps.isNotEmpty) {
      return Memo.fromMap(Map<String, dynamic>.from(maps.first));
    }
    return null;
  }

  Future<int> update(Memo row) async {
    Database db = await instance.database;
    return await db.update(
      _tableName,
      row.toMap(),
      where: '$_columnId = ?',
      whereArgs: [row.id],
    );
  }
  
  Future<int> delete(String id) async {
    Database db = await instance.database;
    return await db.delete(
      _tableName,
      where: '$_columnId = ?',
      whereArgs: [id],
    );
  }
}
```

Main

```main.dart
import 'package:flutter/material.dart';
import 'package:uuid/uuid.dart';                 // ++

import 'package:my_project/db/memo.dart';        // ++
import 'package:my_project/db/dbProvider.dart';  // ++

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
//  int _counter = 0;

//  void _incrementCounter() {
//    setState(() {
//      _counter++;
//    });
//  }

  final dbProvider = DBProvider.instance;        // ++

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      /** --- 追加 ここから --- **/
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              child: Text(
                'insert',
                style: TextStyle(fontSize: 20),
              ),
              onPressed: _insert,
            ),
            ElevatedButton(
              child: Text(
                'query',
                style: TextStyle(fontSize: 20),
              ),
              onPressed: _query,
            ),
            ElevatedButton(
              child: Text(
                'update',
                style: TextStyle(fontSize: 20),
              ),
              onPressed: _update,
            ),
            ElevatedButton(
              child: Text(
                'delete',
                style: TextStyle(fontSize: 20),
              ),
              onPressed: _delete,
            ),
          ],
        ),
      ),
      /** --- 追加 ここまで -- **/
//      body: Center(
//        child: Column(
//          mainAxisAlignment: MainAxisAlignment.center,
//          children: <Widget>[
//            Text(
//              'You have pushed the button this many times:',
//            ),
//            Text(
//              '$_counter',
//              style: Theme.of(context).textTheme.headline4,
//            ),
//          ],
//        ),
//      ),
//      floatingActionButton: FloatingActionButton(
//        onPressed: _incrementCounter,
//        tooltip: 'Increment',
//        child: Icon(Icons.add),
//      ),
    );
  }
  
  /** --- 追加 ここから --- **/
  void _insert() async {
    Memo row = Memo.newMemo();
    row.note = 'test Text 000';
    final id = await dbProvider.insert(row);
    print('inserted row id: $id');
  }

  void _query() async {
    final allRows = await dbProvider.getAllRows();
    print('query all rows:');
    allRows.forEach(print);
  }

  void _update() async {
    final rowsAffected = await dbProvider.update(Memo.newMemo());
    print('updated $rowsAffected row(s)');
  }

  void _delete() async {
    String uuid = Uuid().v4();
    final rowsDeleted = await dbProvider.delete(uuid);
    print('deleted $rowsDeleted row(s): row $uuid');
  }
  /** --- 追加 ここまで --- **/
}

```