なにこれ
Flutter で Supabase の接続までをまとめたもの。
公式ページや Stackflow とか Zenn とかであがっている記事を見たりしたけど完全補完できなかったので個人的なメモとしてまとめました。
(そのため、興味があったらこの記事も「ふーん、そうなんだ」くらいで読んでみてください)
あと最後に自分で設定していた時にひっかかったところがあったので、そのメモも書いておきました。
Supabase 初期セットアップ
まずは supabase で初期セットアップをしましょう!
https://supabase.com/
ユーザー認証部分は割愛しますが、ダッシュボードまで辿り着ければOK
https://supabase.com/dashboard/projects
Supabase で プロジェクト作成
先ほどのダッシュボード画面から Start your project
でプロジェクト作成フローを開いて進めていきましょう。
プロジェクト作成ボタンをクリックしたら完成。あっという間。
プロジェクトトップの Project API keys > anon と Project Configration > URL は後ほど使用します。(このプロジェクト画面にくれば確認できるのでメモしなくてもOK)
テーブル作成
SQL editor で作成
こちらの URL から SQL で作成可能です。
URL にプロジェクト指定していないので、まずは直下の画面が出てきますが、そのままプロジェクトを選択して SQL Editor を開いてください。
https://supabase.com/dashboard/project/_/sql/new
参考までに以下の SQL を実行してみました。
-- Create the table
CREATE TABLE countries (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
-- Insert some sample data into the table
INSERT INTO countries (name) VALUES ('United States');
INSERT INTO countries (name) VALUES ('Canada');
INSERT INTO countries (name) VALUES ('Mexico');
GUI で作成
同様にプロジェクトを指定していないURLを書きました。
画面からだとメニューから Table Editor や Database を開けば同様の画面に到達できます。
https://supabase.com/dashboard/project/_/database/tables
+ New table
をクリックして GUI Editor を開きましょう。
テーブル名、説明文などを入力していきます。
カラムも直感的に設定できますね。
Enable Row Level Security (RLS)
はセキュリティ面から ON にしておきましょう。
できたら Save で保存しましょう。
出来上がったテーブルは Table Editor から直接みることができます。
Flutter 側の Supabase 接続設定
ここから Flutter 側での接続設定。
package インストール
supabase_flutter
を入れましょう。
flutter_dotenv
は必須ではないですが、環境変数に入れたいのでついでに追加。
flutter pub add supabase_flutter
flutter pub add flutter_dotenv
pubspec.yaml で .env のパスを通す
flutter:
assets:
- .env
プロジェクト直下に .env ファイルを作成する
SUPABASE_ANON_KEY="先ほどの supabase で確認した anon の値をここにセット、ダブルクオーテーションはいらないですよ"
SUPABASE_URL="先ほどの supabase で確認した URL の値をここにセット"
.gitignore に .env を追加
アプリ起動時に初期化処理を追加
main()
を以下のように書き換えました。
await は基本的に読み込み処理を待つために必要なので、ちゃんと入れましょう。
(なぜそれを書いたかというと、僕は最初に理由もなく「要らんでしょ!」って言って無視してたので)
Future<void> main() async {
// Ensure flutter binding has been initialized
WidgetsFlutterBinding.ensureInitialized();
// Load .env file
await dotenv.load(fileName: '.env');
// Initialize Supabase
await Supabase.initialize(
url: dotenv.env['SUPABASE_URL']!,
anonKey: dotenv.env['SUPABASE_ANON_KEY']!,
);
// Run the app
runApp(MainApp());
}
接続設定完了!
設定はできたのであとは supabase とやりとりするだけ!
Flutter で Supabase のテーブルに CRUD 処理を行う
インスタンス初期化処理
まずは最初に初期化をします。
final supabase = Supabase.instance.client;
select を実行してみよう
これは自分で作ったテーブルなので、
from にはテーブル名、select には取得したいカラム名を指定します。
final res = await supabase.from('user_info').select('id, username, email');
ちなみにアスタリスクも使えます。
final res = await supabase.from('user_info').select('*');
debug で取得してみました、良い感じ。
debugPrint('*** res: $res');
I/flutter (18662): *** res: [{id: ecfd1a1e-3644-49cc-82fb-68e0d55f240d, username: hoge2, email: hoge2@gmail.com}]
insert 処理
こんな感じで行けます。
ちなみに res には登録結果を入れています。
try {
final res = await supabase.from('user_info').insert({
'username': username,
'email': email,
'created_at': DateTime.now().toIso8601String(),
'updated_at': DateTime.now().toIso8601String(),
'deleted_at': null,
});
} catch (e) {
// エラー処理を書く
}
update 処理
この辺から説明割愛。
とりあえず match
は where ですね。
try {
final res = await supabase.from('user_info').update({
'username': _usernameController.text,
'email': _emailController.text,
'updated_at': DateTime.now().toIso8601String(),
}).match({'id': widget.user_info.id});
} catch (e) {
...
}
upsert 処理
レコードがあったら update, なかったら insert するもの。
rails の find_or_create_by
っぽい。(初心者らしい感想)
try {
final res = await supabase.from('user_info').upsert({
'id': widget.user_info?.id, // Primary key
'username': _usernameController.text,
'email': _emailController.text,
});
} catch (e) {
...
}
delete 処理
id 一致させて削除、カンタン。
try {
await supabase.from('user_info').delete().match({'id': user_info.id});
} catch (e) {
...
}
where や NOT NULL のようなものの tips
カンタンなもの以外にも色々 filter したくなるので、その辺をメモ
Filter
eq
where A = B 的なもの
final data = await supabase
.from('countries')
.select('name, country_id')
.eq('name', 'Japan');
neq
where A != B 的なもの
final data = await supabase
.from('countries')
.select('name, country_id')
.neq('name', 'Japan');
match
where A = B 的なもの、複数条件指定可能。
final data = await supabase
.from('countries')
.select('name, country_id')
.match('name', 'Japan', country_id: 81);
textSearch
where A = '' 的なもの
&
で複数条件設定できる模様
final data = await supabase
.from('countries')
.select('name, country_id')
.textSearch('name', "'Japan' & 'Korea'");
gt
where A > B
final data = await supabase
.from('countries')
.select('name, country_id')
.gt('country_id', 10);
gte
where A >= B
final data = await supabase
.from('countries')
.select('name, country_id')
.gte('country_id', 10);
lt
where A < B
final data = await supabase
.from('countries')
.select('name, country_id')
.lt('country_id', 10);
lte
where A <= B
final data = await supabase
.from('countries')
.select('name, country_id')
.lte('country_id', 10);
Modifier
order
order by
final data = await supabase
.from('countries')
.select('name, country_id')
.order('country_id', ascending: true);
limit
final data = await supabase
.from('countries')
.select('name, country_id')
.limit(10);
Appendix
Supabase から new row violates row-level security policy
が返ってきて「なんで?」と思ったのですが、それはポリシー設定が原因でした。
上記の設定ではこのエラー対応については特に記載していませんので、詳しくはこちらの記事で。
https://qiita.com/su3-hokkaido/items/ccd989050ecb3ba303fd