Python ↔ Dart Bridge Prototype
This sandbox shows how a Flutter/Dart application can call Python functions without HTTP servers.
Communication happens over a length-prefixed stdin/stdout channel: Python exposes decorated callables and adapters, while Dart uses PythonBridge.init()
, PythonBridge.call()
, and PythonBridge.callWithMeta()
for ergonomic cross-language calls with rich metadata.
Layout
pyproject.toml
– Python package metadata (build with Hatch; publishes the CLI + runtime).python_bridge/runtime.py
– Core event loop, handshake, codec negotiation, metadata, and adapter registry.python_bridge/example_worker.py
– Minimal worker exposing sample functions and adapters.python_bridge/cli.py
– Stub generator (python-bridge-stubs
) that emits Dart helpers from decorated Python functions.lib/python_bridge.dart
– Dart bridge implementation with codec negotiation, metadata, and adapter APIs.lib/python_bridge_client.dart
– Public export used by consumers (pub.flutter-io.cn ready).example/main.dart
– Console demo that prints python call metadata.flutter_bridge_demo/
– Windows desktop Flutter UI bundling the worker for release builds.
Quick Start
# Dart console demo
dart pub get
dart run example/main.dart
You should see the Python handshake payload, followed by call results and timing metadata.
Windows Desktop Demo
cd flutter_bridge_demo
flutter pub get
flutter run -d windows
The app starts the Python worker (from local sources in debug or bundled assets in release), displays handshake details, and lets you trigger the sample functions. Metadata shows round-trip timings in the UI tooltips.
Registering Python Functions
from python_bridge import python_bridge
@python_bridge()
def heavy_lift(a: int, b: int) -> int:
return a * b
When the worker starts (python_bridge.runtime.serve()
), all decorated functions are exposed to Dart.
Custom Type Adapters
Python:
from python_bridge import register_encoder, register_decoder
register_encoder(set, lambda value: ("set", list(value)))
register_decoder("set", lambda payload: set(payload))
Dart:
PythonBridge.registerOutboundAdapter<Set>(
'set',
(value) => value.toList(),
);
PythonBridge.registerInboundAdapter(
'set',
(payload) => (payload as List).toSet(),
);
Adapters wrap values in a {"__bridge__": ...}
envelope so they flow seamlessly over JSON or MessagePack.
Calling From Dart
await PythonBridge.init(scriptPath: 'python_bridge/example_worker.py');
final response = await PythonBridge.callWithMeta(
'heavy_lift',
positional: [6, 7],
);
print('Result: ${response.result}, took ${response.meta['duration_ms']} ms');
Errors propagate as BridgeException
, which packages the Python type, stack trace, and metadata (start/completion timestamps, latency, duration).
Packaging
Python (PyPI)
hatch build
# twine upload dist/*
Install extras when needed:
pip install python-bridge[msgpack]
pip install python-bridge[numpy]
Available CLI entry points:
python-bridge-serve
– start the stdin/stdout worker loop.python-bridge-stubs
– emit Dart stubs by introspecting decorated functions.
Dart (pub.flutter-io.cn)
Update metadata in pubspec.yaml
, then verify before publishing:
dart pub publish --dry-run
The public API is exported through package:python_bridge_client/python_bridge_client.dart
.
Stub Generation
Create strongly-typed Dart wrappers directly from your Python module:
python -m python_bridge.cli python_bridge.example_worker \
--output lib/python_example_stubs.g.dart \
--library python_example_stubs
Include --with-meta
to generate wrappers that return BridgeResult
via callWithMeta()
.
Next Steps
- Surface streaming/async support so long-running Python tasks can push progress updates.
- Add worker health checks, automatic restarts, and optional worker pools for concurrency.
- Wire up automation (CI + publishing) so PyPI and pub.flutter-io.cn releases stay in sync.