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.