GitHub - formbricks/flutter: Formbricks Flutter SDK
Monorepo for the first-party Flutter SDK for Formbricks
and its demo app. Initialize a workspace, identify users, track actions, and
render targeted in-app surveys inside a WebView backed by
{appUrl}/js/surveys.umd.cjs.
SDK usage docs live in
packages/formbricks/README.md.
Quick Start
Run commands from the repo root unless a step says otherwise.
1. Install platform prerequisites
FVM installs the Flutter SDK for this project, but you still need the native tooling for the platform you want to run:
- iOS simulator: macOS with Xcode installed. Also run
xcode-select --installonce so command-line tools are available. - Android emulator: Android Studio with the Android SDK, Platform Tools, and at least one Android Virtual Device (AVD) created in Device Manager.
If you only plan to run one platform, you only need that platform's tooling.
2. Install FVM
FVM reads .fvmrc and downloads the exact Flutter version used by this repo.
On macOS, Homebrew is the easiest install path:
brew tap leoafarias/fvm brew install fvm
If you already have Dart on your machine, this also works:
dart pub global activate fvm
3. Install the pinned Flutter SDK
fvm install downloads the Flutter version from .fvmrc (currently 3.44.0).
make doctor checks the local iOS/Android tooling with the pinned Flutter SDK.
Fix the red ✗ items for the platform you want to run, then run the doctor
command again.
4. Fetch dependencies
This resolves the whole Dart pub workspace from the root pubspec.yaml and
uses the shared pubspec.lock.
5. Run the playground app
make run # iOS simulator, default make run android # Android emulator
The Makefile delegates to tool/run.sh, boots a simulator/emulator when it can,
then runs apps/playground. Once the app is running, press r for hot reload,
R for hot restart, and q to quit.
You should see a "Welcome to Formbricks" header, a connection form (App URL + Workspace ID), a "Trigger Code Action" button, the identity buttons (setUserId / setAttributes / setAttribute / setLanguage / logout), and local-storage inspect/clear buttons. Connect to a real workspace to exercise them end to end.
Repository layout
flutter/
├── pubspec.yaml # pub workspace root + Melos script config (never published)
├── Makefile # daily dev + CI command entry point
├── analysis_options.yaml # shared analyzer + lint rules for every package
├── sonar-project.properties # SonarCloud config (finalised in a follow-up)
├── LICENSE # MIT
├── packages/
│ └── formbricks/ # the SDK package (the thing we publish)
│ ├── lib/
│ │ ├── formbricks.dart # public exports
│ │ └── src/ # private implementation
│ ├── test/ # one test file per source file
│ ├── pubspec.yaml
│ ├── CHANGELOG.md
│ ├── LICENSE
│ └── README.md
└── apps/
└── playground/ # demo / manual-QA app
├── lib/main.dart # SDK test buttons: track, setUserId, setAttributes, …
├── android/ ios/ # platform projects (iOS + Android only)
├── test/
└── pubspec.yaml
A standard packages/* + apps/* monorepo split.
Why these locations
| Path | Holds | Rationale |
|---|---|---|
packages/formbricks |
The publishable SDK | Single source of the pub.dev package. src/ is private; only lib/formbricks.dart re-exports the public API. |
apps/playground |
Demo app | Real Flutter app on iOS + Android for manual QA of WebView / keyboard / modal behaviour. Excluded from SonarCloud + pub scoring. A real demo app is what catches keyboard/touch regressions before customers do, so it ships from day one. |
Monorepo tooling
Uses Dart pub workspaces (Dart ≥ 3.6) + Melos 7.
- The root
pubspec.yamldeclaresworkspace:members. Each member setsresolution: workspace, so the whole repo shares one lockfile and one resolved dependency graph — no version drift between SDK and demo app. - Melos 7 sits on top of native workspaces and adds cross-package scripts
(analyze / test / format across everything at once). Its config lives under
the
melos:key in the rootpubspec.yaml(Melos 7 droppedmelos.yaml).
Common commands
All run from the repo root. Use the Makefile for normal development; it uses
fvm flutter / fvm dart when FVM is installed and falls back to flutter /
dart from PATH otherwise.
make help # list available targets make deps # resolve the whole workspace (one lockfile) make format # format Dart code make format-check # CI-style formatting check make analyze # analyze all packages make test # run tests in packages that have test/ make coverage # run tests with coverage make check # format-check + analyze + test
Important daily commands:
make deps
make run
make run android
make format
make analyze
make test
make checkCI/parity targets are also available when you need to reproduce workflow steps:
make deps-lockfile make analyze-ci make test-sdk-machine make test-sdk-coverage make test-playground make build-android make build-ios-no-codesign make pana-install make pana make pub-publish-dry-run
Conventions
Conventions locked in across the SDK:
- Naming. Internal types are
FormbricksConfig,Logger, etc. — no redundantFlutterprefix inside a Flutter package. Storage key isformbricks-flutter, namespaced so multiple Formbricks SDKs on one device never collide. - Static public API, hidden singleton.
Formbricks.track(...)/Formbricks.setup(...)are static facades over a private singleton — notFormbricks.instance.track(...). The host widget and static API share the oneFormbricksclass. - No
environmentId. The SDK acceptsworkspaceIdonly — no legacy alias to ever deprecate. - Dates cross one boundary.
DateTimein memory, ISO-8601Stringon the wire and on disk; convert only infromJson/toJson. No strayDateTime.parseelsewhere. - Testing is mandatory. Every source file gets a dedicated test file (happy
path + errors + edge cases). PRs aren't mergeable without it; aim for ≥ 80 %
coverage on touched files. Unit tests use
flutter_test+mocktail/http'sMockClient— no real network. - Targets. iOS + Android only; Flutter Web and desktop are not supported.
Toolchain details
The exact Flutter version is pinned in .fvmrc and managed with
fvm. Pinning means every contributor and CI run uses the same
SDK version.
- FVM downloads Flutter into its own cache under
~/fvm/versions/; you do not clone Flutter by hand. .fvm/flutter_sdkis a local symlink to the active SDK and is git-ignored. Only.fvmrcis committed.- Editors should use
.fvm/flutter_sdkas the Flutter/Dart SDK path so analysis and code completion use the pinned SDK. - Bump the repo's Flutter version with
fvm use <version> --force, then commit the changed.fvmrc. - Floors: published SDK = Flutter ≥ 3.27 / Dart ≥ 3.6; dev tooling pins a newer SDK via
.fvmrc.
Running the demo app
The playground targets iOS + Android only — there is no macos//web/
project. Note that flutter run cannot boot a simulator on its own: with no
device running it falls back to the macOS desktop target (which this app does
not support), so a simulator/emulator must be started first.
One-command run
make run # iOS simulator (default) make run android # Android emulator
Or call the underlying script directly after make deps:
./tool/run.sh ./tool/run.sh android
Device checks
Use these commands when the run script cannot find a device:
make emulators # configured simulators/emulators Flutter can launch make devices # currently running simulators/emulators/devices
For Android, if no emulator appears in make emulators, create an AVD in
Android Studio's Device Manager first.
Manual CLI run
A simulator/emulator must be booted first — flutter run never boots one
itself. Start a device, then target it by id or name substring:
fvm flutter emulators --launch apple_ios_simulator # iOS sim # or: fvm flutter emulators --launch Pixel_9a # Android emulator cd apps/playground fvm flutter run -d iphone # iOS, if the simulator name contains "iphone" fvm flutter run -d emulator # Android, if the emulator id contains "emulator"
-d matches a device id or name substring, not a platform name. For example,
-d ios does not mean "run on iOS".
First Android build is slow because Gradle downloads the NDK and CMake (approximately 3 GB, one-time). Subsequent builds reuse them.