flutterflow_cloud 0.2.0
flutterflow_cloud: ^0.2.0 copied to clipboard
The FlutterFlow Cloud SDK for Flutter — typed database records and queries, end-user auth with self-contained persistent sessions, file storage with progress and URL caching, and a fast built-in proje [...]
flutterflow_cloud #
The FlutterFlow Cloud SDK for Flutter — typed database records and queries, end-user auth with self-contained persistent sessions, file storage with progress and URL caching, and a built-in project code generator.
One initialize call connects the app; the SDK owns everything an app
usually hand-rolls:
- Sessions persist and restore on every Flutter platform with no
plugins. Tokens and a user snapshot are stored in the app's private data
directory (or
localStorageon web), soauth.currentUseris already hydrated wheninitializereturns — no boot-timeme()handshake, no token-storage class to write. - Tokens refresh themselves (single-flight, transparent 401 replay); transient network failures never destroy a session.
- Retries, idempotency keys, and UTF-8 decoding are handled at the transport.
- Every error carries
userMessage— stable, human-safe copy an app can put straight in a snackbar, withdetail/fixreserved for logs. - Query caching is built in (memory + disk) and strictly opt-in per read
via
FFXFetchPolicy; cached pages report honest metadata (isFromCache, originalreceivedAt). - Uploads are tasks — awaitable, with a progress stream, cancellation,
and in-place retry that never orphans file records. Signed download URLs
are cached and single-flighted; the
FFXImagewidget renders a private file from its id.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterFlowCloud.initialize(options: FFXDefaultOptions.current);
runApp(const MyApp());
}
final cloud = FlutterFlowCloud.instance;
await cloud.auth.signInWithEmail(email: email, password: password);
Generated bindings #
The package ships a generator that turns the project's .ffx/project.json
(the FFX config document) into typed Dart bindings — models, enums,
collections with named-parameter writes, query methods, and file
collections:
dart run flutterflow_cloud:generate
It emits lib/ffx/ in well under a second (no build_runner, no analyzer;
one in-process pass plus a dart format polish) and is deterministic, with
a --check drift mode for CI. With .ffx/options.json present (written by
provisioning) it also emits FFXDefaultOptions, so app code never types an
API key. After generation, app code looks like this:
import 'ffx/ffx.dart';
final journal = await cloud.journals.create(
name: 'Alpine hikes',
emoji: '🌲',
color: JournalColor.meadow,
isArchived: false,
);
final page = await cloud.queries.entriesInJournal(journalId: journal.id)
.execute(fetchPolicy: FFXFetchPolicy.cacheFirst);
final count = await cloud.queries.entryCountForJournal(journalId: journal.id);
Generated code follows the platform contract: enums decode unknown future
values to a terminal unknown member (and refuse to encode it), timestamps
are UTC DateTimes, references are plain id strings, and server-assigned
fields (owner bindings, readOnly/serverOnly markers, read-time
defaults) are readable but never appear in write signatures.
Sessions and security #
Persisted sessions live in app-private storage (OS-sandboxed directories on
mobile/desktop, localStorage on web) — the same protection class as
SharedPreferences, with zero native dependencies. Apps with a
hardware-keystore requirement can implement FFXSessionStorage over their
own secure storage and pass it to initialize.
Local development with the emulator #
The FlutterFlow Cloud emulator serves the full data plane (records,
queries, batch, auth, files) offline from the project's .ffx/ config.
Start it in the project directory:
ffx-emulator # or: dart run flutterflow_cloud_emulator:ffx_emulator
and point the app at it with one flag:
await FlutterFlowCloud.initialize(
options: FFXDefaultOptions.current,
useEmulator: true,
);
useEmulator: true overrides the base URL with http://<host>:8790,
where the host defaults per platform: Android emulators use 10.0.2.2
(the alias for the host machine's loopback), web uses localhost, and
everything else 127.0.0.1. A physical Android device can't reach
10.0.2.2 — pass emulatorHost: with your machine's LAN IP (and likewise
emulatorPort: for a non-default port). Compile-time defines
FFX_EMULATOR_HOST/FFX_EMULATOR_PORT (--dart-define) override the
defaults without code changes. The emulator needs no provisioned project,
so under useEmulator any pk_… key is accepted.
Scope notes #
- Reads can be cached; writes always require the network (there is no offline write queue — yet).
- Realtime subscriptions (
subscribable/channelsin the config) are not yet served by the platform's client plane, so the SDK deliberately ships nowatch()API rather than a fake one.
See examples/flutter-fieldnotes for a complete app built on this package,
and its FRICTION.md for the running dogfood log.
In the platform #
| Workspace | backend/packages/dart_flutterflow_cloud — member of the pub workspace rooted at backend/pubspec.yaml |
| Package | flutterflow_cloud (Flutter package, Dart ^3.11 / Flutter ≥3.35) |
| Ships to | pub.dev — this is a published artifact, released through melos publish |
| Talks to | The data plane: platform-dev.flutterflow.cloud via pk_dev_…, or the local gateway on http://127.0.0.1:8787, or the offline emulator on :8790 |
| Dependencies | package:http only — pure Dart, no platform channels, no plugins |
| TS twin | @flutterflow/cloud |
Deploys to nothing — it is the client #
This package runs inside the customer's app. It holds no platform credential beyond the project's publishable key (which is public by design; end-user auth rides in bearer headers), and it reaches exactly one host per environment.
Structure #
| Path | Role |
|---|---|
lib/flutterflow_cloud.dart |
The Flutter entry point |
lib/core.dart |
The pure-Dart entry point for CLIs and servers — no Flutter binding |
lib/src/http_core.dart |
Transport: retries, idempotency keys, UTF-8 decoding, single-flight refresh, transparent 401 replay |
lib/src/database.dart |
String-named collections and named queries only, with optional converter typing |
lib/src/auth.dart, lib/src/session/ |
Sessions, persisted per platform |
lib/src/persistence/ |
Blob store: blob_store_io.dart (app data dir), blob_store_web.dart (localStorage), blob_store_stub.dart |
lib/src/cache/query_cache.dart |
Memory + disk query cache, strictly opt-in per read via FFXFetchPolicy |
lib/src/storage.dart, lib/src/widgets/ffx_image.dart |
Upload tasks with progress/cancel/retry; signed-URL caching; the FFXImage widget |
lib/src/generator/, bin/generate.dart |
The project code generator (dart run flutterflow_cloud:generate) |
lib/src/wire_models.g.dart |
Generated by @flutterflow/codegen from @flutterflow/schema. Never hand-edit — bun run codegen:check gates it in CI |
lib/src/emulator_host_*.dart |
Per-platform emulator host resolution |
Verification #
From the repo root: bun run dart:analyze (dart analyze --fatal-infos),
bun run dart:test, and bun run codegen:check. The pinned Flutter/Dart SDK
comes from the root .fvmrc.
Specs #
- 14 — Dart SDK — the normative surface, including §1 (no build_runner, no model runtimes)
- 04 — Database API · 06 — Auth · 07 — Storage
- 26 — Emulator —
useEmulatorintegration - 18 — API Conventions — problems, headers, idempotency
- 17 — Monorepo & Code Sharing §7 and §9 — codegen and the pub workspace