eigen_flutter 0.8.0
eigen_flutter: ^0.8.0 copied to clipboard
EigenInteractive Flutter client: the whitelabel app half for turn-based multiplayer games on the EigenInteractive server.
EigenInteractive Flutter #
The Flutter half of EigenInteractive: a server-authoritative engine for turn-based multiplayer games.
eigen_client is the pure Dart protocol, domain, clock, and live-session
runtime. eigen_flutter builds provider-neutral Flutter presentation on it
and, for the moment, supplies the complete app shell. Firebase is an optional
adapter in the separate eigen_firebase package. A game supplies a small
GameModule containing its client-side rules and presentation.
Start a game #
The recommended flow creates the Cloudflare Worker and Flutter app together:
pnpm create eigen-game my-game
# or
npm create eigen-game@latest my-game
The scaffold installs published npm/pub.dev dependencies; it does not clone the engine repositories. For an existing app or separate Worker/app repositories, follow the manual setup guide.
Add to an app #
dependencies:
eigen_flutter: ^0.8.0
eigen_firebase: ^0.1.0 # only when the app chooses Firebase
firebase_core: ^4.9.0
Import the framework through its public barrel:
import 'package:eigen_flutter/eigen_flutter.dart';
import 'package:eigen_firebase/eigen_firebase.dart';
Do not depend on eigen_api directly or deep-import core/, features/, or
shared/. The barrel is the supported game-facing API.
On Android, the Firebase adapter's local-notification dependency requires
core-library desugaring in the application module. create-eigen-game
configures it automatically. For a hand-created app, add this to
android/app/build.gradle.kts:
android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
}
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
}
Boot the app with your module, branding, Worker origin, and generated Firebase configuration. The standard app targets Android and web, so the public Web Push key is required deployment configuration; it belongs to the same Firebase project used for authentication:
const apiBaseUrl = String.fromEnvironment('API_BASE_URL');
const googleWebClientId = String.fromEnvironment('GOOGLE_WEB_CLIENT_ID');
const firebaseVapidKey = String.fromEnvironment('FIREBASE_VAPID_KEY');
const appHost = String.fromEnvironment('APP_HOST');
Future<void> main() => runFirebaseEngineApp(
module: const MyGameModule(),
config: AppConfig(
branding: const Branding(appName: 'My Game', seedColor: Colors.indigo),
engine: EngineConfig(
apiBaseUrl: apiBaseUrl,
appHost: appHost.isEmpty ? null : appHost,
),
),
firebaseOptions: DefaultFirebaseOptions.currentPlatform,
firebase: FirebaseAdapterConfig(
googleWebClientId: googleWebClientId,
vapidKey: firebaseVapidKey,
),
onBackgroundMessage: onBackgroundMessage,
telemetry: FirebaseTelemetryPolicy.releaseOnly(),
);
Without Firebase, omit eigen_firebase and call runEngineApp directly. Its
auth, notification, and analytics ports default to unavailable/no-op adapters;
another integration can override those ports through
package:eigen_flutter/adapters.dart.
These are public build-time values. Scaffolded apps keep them in
app-config.json and use the same command option for Android and web:
flutter run --dart-define-from-file=app-config.json
Missing or malformed required values are reported together before their
services start. Keep actual secrets on the Worker. Telemetry collection is off
unless the app passes an explicit policy such as releaseOnly().
Connect a game app to Firebase with the package executable:
dart run eigen_firebase:configure_firebase
It runs FlutterFire for Android and web, then derives
web/firebase-config.js for the messaging service worker from the Web app
FlutterFire selected. The public VAPID key remains in app-config.json because
it is not part of Firebase's app SDK configuration.
The game boundary #
The authoritative TypeScript module declares state, observation, action,
and config once. It emits game-contract.json; the development-only
eigen_codegen package turns that artifact into immutable Dart payloads, a
typed rules base, and fixture copies:
flutter pub add --dev eigen_codegen
dart run eigen_codegen:generate_payloads \
--contract ../server/game-contract.json \
--output lib/game/generated/payloads.dart \
--fixtures-output test/fixtures
Your handwritten Dart code then:
- extends the generated
V<N>RulesBase; - implements client-side legality and optional optimistic preview;
- renders the game from
GameContentContext; - registers one rules unit per server
schemaVersion; - declares the version-independent creation and rules UI.
The server remains authoritative. Shared fixtures run against both languages so payload and behavior drift fails in tests.
Example #
example/ is a complete Rock–Paper–Scissors client. It deliberately
uses simultaneous hidden commitments to demonstrate per-seat observations and
the valid “do not predict this move” path:
cd example
flutter pub get
flutter test
flutter build web --release
The package treats Android and web as supported targets. The generated scaffold includes the browser Firebase Messaging service worker, Firebase Auth's web popup flow, cross-origin Worker setup, and a release web build in CI. See Deploy the web app.
Documentation #
- Quickstart
- The TypeScript + Dart game contract
- Payload generation
- Rendering a game
- Testing both halves
- Deploy the web app
- Dart API reference
- Versions and compatibility
Working on the framework #
- CONTRIBUTING.md: local setup, generation, validation, changelog entries, and pull requests.
- MAINTAINERS.md: pub.dev setup, releases, version tags, and failure recovery.