leancode_cyberware_contract_base
Base package for Flutter web apps running inside an iframe that need bidirectional RPC with a host page. Provides Cubit-based connection state management, semver contract versioning, and type-safe Dart wrappers over a Penpal-powered postMessage bridge.
This package is the Flutter side of the @leancodepl/cyberware-contract ecosystem. For the full end-to-end setup guide (contract schema, Dart code generation, React host, and Flutter remote), see the readme.
How it fits together
A shared contract package contains a Zod schema — the single source of truth. TypeScript types are inferred for the React host; Dart extension types are generated by @leancodepl/cyberware-contract-generator-dart. The Flutter remote uses this package to manage the connection lifecycle via ConnectToHostCubit.
Features
- Bidirectional method calls between iframe (Flutter) and host page
- Semver-based contract version negotiation
- Ready-to-use
ConnectToHostCubitfor managing the connection lifecycle - Equatable states for reliable
BlocBuilderrebuilds - Type-safe Dart wrappers over the JS interop layer
- Works with generated Dart types from
@leancodepl/cyberware-contract-generator-dart
Setup
1. Install the package
flutter pub add leancode_cyberware_contract_base
2. Load the JS asset
Add the bundled script to your Flutter web app's web/index.html before the Flutter bootstrap script:
<head>
<!-- ... -->
</head>
<body>
<script src="assets/packages/leancode_cyberware_contract_base/assets/connect_to_host.js"></script>
<script src="flutter_bootstrap.js" async></script>
</body>
Usage
The typical workflow is:
- Define a Zod contract schema in a shared contract package
- Generate Dart types with
npx cyberware-contract-generator-dart - Implement
RemoteMethodsBase(the methods the host can call on the Flutter app) - Create a
ConnectToHostCubitwrapper wiring the generatedconnectToHostand contract version - Use
BlocProvider+BlocBuilderto react to connection states
The generated code provides HostMethods, RemoteMethodsBase, JSRemoteMethods, JSHostMethods, RemoteUrlParams, and a typed connectToHost function. You write a thin ConnectToHostCubit subclass and a RemoteMethodsBase implementation on top.
Reacting to connection states
BlocBuilder<ConnectToHostCubit, ConnectToHostState>(
builder: (context, state) {
return switch (state) {
ConnectToHostStateIdle() =>
const Center(child: Text('Connecting...')),
ConnectToHostStateConnected(:final host) => Column(
children: [
Text('Connected!'),
ElevatedButton(
onPressed: () => host.showNotification(
HostShowNotificationParams(
message: 'Hello from Flutter!',
),
),
child: const Text('Show notification'),
),
],
),
ConnectToHostStateError(:final error) =>
Center(child: Text('Error: $error')),
ConnectToHostStateIncompatible(
:final hostVersion,
:final remoteVersion,
) =>
Center(
child: Text(
'Incompatible: host $hostVersion, remote $remoteVersion',
),
),
};
},
)
Using the raw API
For more control, use connectToHostRaw directly instead of through the generated wrapper:
import 'package:web/web.dart' as web;
if (!identical(web.window.parent, web.window)) {
final result = await connectToHostRaw<JSHostMethods>(myMethods);
switch (result) {
case RawConnectToHostResultConnected(:final host):
// use host methods
case RawConnectToHostResultError(:final error):
// handle error
}
}
Note
connectToHostRaw must only be called once. Subsequent calls throw a StateError. Call disconnectHost() to tear down the connection.
Contract versioning
The host page passes a contractVersion query parameter in the iframe URL. ConnectToHostCubit reads it and checks it against the contractVersionRange you provide (parsed as a semver VersionConstraint). If the versions are incompatible, the cubit emits ConnectToHostStateIncompatible instead of attempting to connect.
API overview
| Symbol | Description |
|---|---|
connectToHostRaw() |
Establishes a raw JS-level connection to the host |
disconnectHost() |
Tears down the active connection |
parseConnectToHostResult() |
Parses the raw JS result into a typed result |
ConnectToHostCubit |
Cubit managing the full connection lifecycle |
ConnectToHostCubitOptions |
Configuration for ConnectToHostCubit |
ConnectToHostState |
Sealed class with Idle, Connected, Incompatible, Error states |
UrlParamsBase |
Abstract class with static accessors for URL query parameters (e.g. contractVersion) |
Related packages
| Package | Description |
|---|---|
| @leancodepl/cyberware-contract | TypeScript contract definition, React hooks, and Penpal connection management. See its readme for the full end-to-end setup guide. |
| @leancodepl/cyberware-contract-generator-dart | Generates Dart extension types from the Zod contract schema |
Building the JS asset
The TypeScript source lives in js/. To rebuild the bundled JS asset:
Note
Node.js >= 22.0.0 is required.
cd js
npm install
npx vite build
The output is written to assets/connect_to_host.js.
🛠️ Maintained by LeanCode
This package is built with 💙 by LeanCode. We are top-tier experts focused on Flutter Enterprise solutions.
Why LeanCode?
-
Creators of Patrol – the next-gen testing framework for Flutter.
-
Production-Ready – We use this package in apps with millions of users.
-
Full-Cycle Product Development – We take your product from scratch to long-term maintenance.