Tu primera app con Flutter
Una lista de tareas para iPhone y Android donde las personas se registran y solo ven sus propias tareas. Usa el paquete de Espobase para Dart; la base de datos y las claves salen del panel.
1. Crea un proyecto y una tabla
En el panel, crea un proyecto. Abre Editor SQL y ejecuta:
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. Ajustes y claves
- API: crea una clave publicable y cópiala ahora; las claves solo se muestran al crearlas. Nunca pongas una clave secreta (
eb_sk_…) en una app: cualquiera puede abrir una app y leerla. - Autenticación › Ajustes: añade el enlace de tu app en URLs de redirección permitidas, por ejemplo
myapp://auth/callback. Los correos de confirmación y el inicio de sesión social vuelven a él. Registra el mismo esquema en tus proyectos de Android e iOS.
3. Instala
El paquete de Espobase funciona en todas las plataformas de Flutter. Los otros paquetes protegen la sesión y abren las páginas de inicio de sesión:
flutter pub add espobase flutter_secure_storage flutter_web_auth_2 sign_in_with_apple
4. El cliente
Por defecto la sesión vive en memoria. Dale al cliente un almacenamiento seguro y la persona seguirá con la sesión iniciada al reiniciar la app. La sesión se renueva sola.
// 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. Registro e inicio de sesión
// 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));
Con la confirmación por correo activada, la persona recibe un enlace que abre tu app en la dirección que pasaste como redirectTo, con el token después de un #. Confírmalo con espobase.auth.verifyEmail(token: …).
6. Inicia sesión con Apple
Actívalo en Autenticación › Proveedores › Apple con tu propia cuenta de Apple Developer, y añade allí el bundle ID de tu app. Dale a Apple el nonce con hash y a Espobase el original:
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
);
}8. Lee y escribe tus datos
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']);Cada solicitud lleva a la persona que inició sesión, y la base de datos aplica tu regla. Quien no ha iniciado sesión recibe una lista vacía, y nadie puede leer ni cambiar las tareas de otra persona, ni siquiera llamando a la API directamente.
9. Sube una foto
Crea un bucket en Storage y añade una política para que cada persona pueda escribir en su propia carpeta (mira la guía de subida de archivos). Pasa los bytes del archivo a upload: Subida de archivos
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');Conviene saber
- Los errores son
EspobaseExceptioncon uncode(por ejemploinvalid_credentials) y unmessageque puedes mostrar. - El tiempo real aún no está en este paquete: por ahora usa la API REST.
- El mismo paquete funciona en Dart del lado del servidor.
7. Inicia sesión con Google o 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); }Primero activa el proveedor en Autenticación › Proveedores. La app abre el proveedor en el navegador del sistema y recupera a la persona a través del enlace de tu app;
exchangeCodeForSessiontermina el inicio de sesión. Solo esta app puede terminarlo: el secreto de un solo uso se queda en el teléfono.