ガイド

Flutter ではじめてのアプリ

iPhone と Android 向けの ToDo リストです。ユーザーはサインアップして、自分の ToDo だけを見られます。Dart 用の Espobase パッケージを使い、データベースとキーはダッシュボードから用意します。

← すべてのガイド

1. プロジェクトとテーブルを作成する

ダッシュボードでプロジェクトを作成し、SQL エディタを開いて次を実行します:

create table todos (
  id bigint generated by default as identity primary key,
  owner_id uuid not null default espobase.user_id(),
  title text not null,
  done boolean not null default false,
  created_at timestamptz not null default now()
);

-- Row level security is already on. Each signed-in user sees and changes only their own todos:
create policy "Users manage their own todos" on todos
  for all
  using (owner_id = espobase.user_id())
  with check (owner_id = espobase.user_id());

2. 設定とキー

  • API: 公開可能キーを作成し、今すぐコピーします。キーは作成時にしか表示されません。シークレットキー(eb_sk_…)は絶対にアプリに入れないでください。アプリは誰でも展開して中身を読めます。
  • 認証 › 設定: 許可するリダイレクト URL にアプリのリンクを追加します。例:myapp://auth/callback。確認メールとソーシャルログインはここに戻ってきます。同じスキームを Android と iOS のプロジェクトにも登録してください。

3. インストール

Espobase パッケージは Flutter のすべてのプラットフォームで動きます。ほかのパッケージは、セッションを安全に保ち、サインインのページを開くために使います:

flutter pub add espobase flutter_secure_storage flutter_web_auth_2 sign_in_with_apple

4. クライアント

既定ではセッションはメモリ上にあります。クライアントに安全なストレージを渡すと、アプリを再起動してもサインインしたままになります。セッションは自動で更新されます。

// lib/espobase.dart
import 'package:espobase/espobase.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

class SecureSessionStorage implements SessionStorage {
  final _store = const FlutterSecureStorage();
  @override Future<String?> get(String key) => _store.read(key: key);
  @override Future<void> set(String key, String value) => _store.write(key: key, value: value);
  @override Future<void> remove(String key) => _store.delete(key: key);
}

final espobase = EspobaseClient(
  const String.fromEnvironment('ESPOBASE_URL'), // https://api.espobase.com/project/<your project ref>
  const String.fromEnvironment('ESPOBASE_KEY'), // the PUBLISHABLE key (eb_pk_…): safe in an app
  options: ClientOptions(sessionStorage: SecureSessionStorage()),
);

5. サインアップとサインイン

// myapp://auth/callback (the scheme is in your Android and iOS projects). Add it under Authentication › Settings.
const redirectTo = 'myapp://auth/callback';

await espobase.auth.signUp(email: email, password: password, redirectTo: redirectTo);
await espobase.auth.signInWithPassword(email: email, password: password);

// The confirmation email opens the app at redirectTo#type=signup&token=…
await espobase.auth.verifyEmail(token: token);

// Re-render when the user signs in or out.
espobase.auth.onAuthStateChange.listen((change) => setState(() => session = change.session));

メール確認が有効な場合、ユーザーには、redirectTo に渡したアドレスでアプリを開くリンクが届き、トークンは # の後ろに付きます。espobase.auth.verifyEmail(token: …) で確認します。

6. Appleでサインイン

ご自身の Apple Developer アカウントで 認証 › プロバイダー › Apple をオンにし、アプリのバンドル ID を追加します。Apple にはハッシュ化した nonce を、Espobase には元の nonce を渡します:

import 'package:sign_in_with_apple/sign_in_with_apple.dart';

Future<void> signInWithApple() async {
  final nonce = AppleNonce();
  final credential = await SignInWithApple.getAppleIDCredential(
    scopes: [AppleIDAuthorizationScopes.email],
    nonce: nonce.hashed, // Apple gets the hash …
  );
  await espobase.auth.signInWithIdToken(
    token: credential.identityToken!,
    nonce: nonce.raw, // … Espobase gets the raw value
  );
}

7. Google または GitHub でサインイン

import 'package:flutter_web_auth_2/flutter_web_auth_2.dart';

Future<void> signInWithGoogle() async {
  final url = await espobase.auth.signInWithOAuth(
    provider: OAuthProvider.google,
    redirectTo: 'myapp://auth/callback',
  );
  final result = await FlutterWebAuth2.authenticate(url: url.toString(), callbackUrlScheme: 'myapp');
  final code = Uri.parse(result).queryParameters['code'];
  if (code != null) await espobase.auth.exchangeCodeForSession(code);
}

先に 認証 › プロバイダー でプロバイダーを有効にします。アプリはシステムのブラウザでプロバイダーを開き、アプリのリンクでユーザーを戻します。exchangeCodeForSession がサインインを完了します。完了できるのはこのアプリだけです。使い捨ての秘密情報はスマートフォンの中に残ります。

8. データの読み書き

final todos = await espobase.from('todos').select().order('created_at'); // List<Map<String, dynamic>>

// owner_id fills itself in (its default is espobase.user_id()).
await espobase.from('todos').insert({'title': 'Buy milk'});
await espobase.from('todos').update({'done': true}).eq('id', todo['id']);
await espobase.from('todos').delete().eq('id', todo['id']);

すべてのリクエストにサインイン中のユーザーが付き、データベースがルールを適用します。サインインしていないユーザーには空のリストが返り、API を直接呼んでも他の人の ToDo を読んだり変更したりすることはできません。

9. 写真をアップロードする

ストレージでバケットを作成し、各ユーザーが自分のフォルダに書き込めるポリシーを追加します(ファイルアップロードのガイドを参照)。ファイルのバイト列を upload に渡します: ファイルのアップロード

final bytes = await pickedFile.readAsBytes();
final userId = (await espobase.auth.currentUser())!.id;
await espobase.storage.from('photos').upload('$userId/photo.jpg', bytes, contentType: 'image/jpeg');

知っておくと便利なこと

  • エラーは EspobaseException で、code(例:invalid_credentials)と、表示できる message を持ちます。
  • リアルタイムはまだこのパッケージにありません。今のところは REST API を使ってください。
  • 同じパッケージはサーバー側の Dart でも動きます。