16. Flutter のデータベース外部パッケージSQFLiteを使ってみる
本日はFlutterでデータベース操作を学習しようと思います。 iOSとAndroidではSQLiteという軽量ライブラリがあります。SQLiteを使えばアプリ内にDB(DataBase)を作成しアプリ内にデータを保存できました。 そのSQLiteをFlutterで利用するための外部パッケージに「SQFLite」というライブラリがあります。 今回はSQFLiteを使ってデータベース操作について学習してみます。
SQFLiteのインストール
その外部パッケージのSQFLiteをインストールするためにはpubspec.yaml
ファイルを開き、
SQFLiteをインストールするコードを書く必要があります。
Flutter のプロジェクトファイルのpubspec.yaml
を開きます。
編集していない状態は次のコードになります。
name: practice_app description: A new Flutter application. # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 # followed by an optional build number separated by a +. # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. # In Android, build-name is used as versionName while build-number used as versionCode. # Read more about Android versioning at https://developer.android.com/studio/publish/versioning # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html version: 1.0.0+1 environment: sdk: ">=2.2.2 <3.0.0" dependencies: flutter: sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.2 dev_dependencies: flutter_test: sdk: flutter
ここのdependencies
に次のコードを追加します。
dependencies: flutter: sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.2 # コードを追加 sqflite: any
sqflite: any
がSQFLiteのインストールになります。
この状態でpackages upgrade
をタップします。
下のコンソールでエラーが表示されていなければSQFLiteのインストールが完了します。
Running "flutter pub upgrade" in practice_app... Process finished with exit code 0
注記:
iPhoneX でのシミュレート時にcocoapod のインストールを求められたのでiOSでのデバッグの際には エラー内容に従って
gem install cocoapods
でcocoapods をインストールする必要がありました。
SQFLite の使い方
SQFLite の使い方について解説します。
基本的には
- sqflite をインポートする
- データベースを開く
- データベースが開いたときの処理を書く
と通常のSQLの扱いと同じ手順になります。
- SQFLiteのインポート
Dartファイルに次の1行を追加します。
import 'package:sqflite/sqflite.dart';
- データベースを開く
Database db = await openDatabase( [データベースのpath], version: [バージョン], onCreate: [処理の内容] );
openDatabase
でデータベースを開くことができます。第1引数にはDBのpath、第2引数にはDBのバージョン、第3引数に処理内容という感じになります。
onCreate
メソッドの中は概ね次のような処理が書かれます。
onCreate: (Database db, int version) async { /// 処理内容 ); }
サンプルコード
では、具体的にサンプルコードを書いてみます。
今回はデータベース名やテーブル名を別クラスconstants.dart
として予め作成しています。
constants.dart
class Constants { final String dbName = "sqflite.db"; final int dbVersion = 1; final String tableName = "address"; }
main.dart
import 'package:flutter/material.dart'; import 'package:practice_app/first_screen.dart'; import 'package:practice_app/second_screen.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( debugShowCheckedModeBanner: true, title: 'BottomNavigationBar App', theme: new ThemeData( primarySwatch: Colors.blue, primaryColor: const Color(0xff2196f3), accentColor: const Color(0xff2196f3), canvasColor: const Color(0xfffafafa), ), initialRoute: '/', routes: { '/': (context) => FirstScreen(), '/list': (context) => SecondScreen(), }, ); } }
first_screen.dart
import 'package:flutter/material.dart'; import 'package:practice_app/constants.dart'; import 'package:sqflite/sqflite.dart'; import 'package:path/path.dart'; class FirstScreen extends StatefulWidget { @override _FirstScreenState createState() => _FirstScreenState(); } class _FirstScreenState extends State<FirstScreen> { final nameController = TextEditingController(); final emailController = TextEditingController(); final phoneController = TextEditingController(); final TextStyle style1 = TextStyle( fontSize: 30.0, color: Colors.black ); final TextStyle style2 = TextStyle( fontSize: 30.0, color: Colors.black ); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Input'), ), body: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ Align( alignment: Alignment.centerLeft, child: Text('名前:', style: style2,), ), TextField( controller: nameController, style: style1, ), Align( alignment: Alignment.centerLeft, child: Text('Email:', style: style2,), ), TextField( controller: emailController, style: style1, ), Align( alignment: Alignment.centerLeft, child: Text('電話番号:', style: style2,), ), TextField( controller: phoneController, style: style1, ) ], ), ), bottomNavigationBar: BottomNavigationBar( currentIndex: 0, items: <BottomNavigationBarItem>[ BottomNavigationBarItem( title: Text('追加'), icon: Icon(Icons.home), ), BottomNavigationBarItem( title: Text('一覧'), icon: Icon(Icons.list) ), ], onTap: (int index) { if (index == 1) { Navigator.pushNamed(context, '/list'); } }, ), floatingActionButton: FloatingActionButton( child: Icon(Icons.save), onPressed: () { _saveData(); showDialog( context: context, builder: (BuildContext context) => AlertDialog( title: Text("保存しました"), content: Text('データベースに保存できました'), ) ); }, ), ); } /// データを保存する void _saveData() async { /// データベースのパスを取得 String dbFilePath = await getDatabasesPath(); String path = join(dbFilePath, Constants().dbName); /// 保存するデータの用意 String name = nameController.text; String email = emailController.text; String phone = phoneController.text; /// SQL文 String query = 'INSERT INTO ${Constants().tableName}(name, mail, tel) VALUES("$name", "$email", "$phone")'; Database db = await openDatabase(path, version: Constants().dbVersion, onCreate: (Database db, int version) async { await db.execute( "CREATE TABLE IF NOT EXISTS ${Constants().tableName} (id INTEGER PRIMARY KEY, name TEXT, mail TEXT, tel TEXT)" ); }); /// SQL 実行 await db.transaction((txn) async { int id = await txn.rawInsert(query); print("保存成功 id: $id"); }); /// ウィジェットの更新 setState(() { nameController.text = ""; emailController.text = ""; phoneController.text = ""; }); } }
second_screen.dart
import 'package:flutter/material.dart'; import 'package:practice_app/constants.dart'; import 'package:sqflite/sqflite.dart'; import 'package:path/path.dart'; class SecondScreen extends StatefulWidget { @override _SecondScreenState createState() => _SecondScreenState(); } class _SecondScreenState extends State<SecondScreen> { List<Widget> _items = <Widget>[]; @override void initState() { super.initState(); getItems(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('一覧'), ), body: ListView( children: _items, ), bottomNavigationBar: BottomNavigationBar( currentIndex: 1, items: <BottomNavigationBarItem> [ BottomNavigationBarItem( title: Text("追加"), icon: Icon(Icons.home) ), BottomNavigationBarItem( title: Text('一覧'), icon: Icon(Icons.list) ) ], onTap: (int index) { if (index == 0) { Navigator.pop(context); } }, ), ); } /// 保存したデータを取り出す void getItems() async { /// データベースのパスを取得 List<Widget> list = <Widget>[]; String dbFilePath = await getDatabasesPath(); String path = join (dbFilePath, Constants().dbName); /// テーブルがなければ作成する Database db = await openDatabase( path, version: Constants().dbVersion, onCreate: (Database db, int version) async { await db.execute("CREATE TABLE IF NOT EXISTS ${Constants().tableName} (id INTEGER PRIMARY KEY, name TEXT, mail TEXT, tel TEXT)" ); }); /// SQLの実行 List<Map> result = await db.rawQuery('SELECT * FROM ${Constants().tableName}'); /// データの取り出し for (Map item in result) { list.add(ListTile( title: Text(item['name']), subtitle: Text(item['mail'] + ' ' + item['tel']), )); } /// ウィジェットの更新 setState(() { _items = list; }); } }
これでビルドすると次のような2つの画面が表示されます。 下のタブバーには「追加」と「一覧」タブを表示しています。
初期表示の画面 | 入力後 |
---|---|
データの保存 | 保存したデータの一覧画面 |
---|---|
これでちゃんとデータベースに保存して、表示できることが確認できました。
通常のアプリ開発ではあまりキャッシュを使うことがありませんので用途は限られますが 使い方を理解しておいたほうが後々便利ですね。 だいたいのアプリ開発では結局サーバー連携のためにAPIからデータを取得することが多いですけど。
これでSQFLiteの使い方からライブラリのインストールまで学習できましたのでいい勉強になったと思います。