.md

Wrapping pub.dev packages

The point of rendering with Flutter isn't just pixels — it's pub.dev: tens of thousands of maintained native plugins. Skal's codegen turns any Flutter widget into a JSX component.

One line of config

List the package in your app's skal_codegen.yaml:

# flutter-host/lib/skal_codegen.yaml
packages:
  - qr_flutter
  - shimmer
  - camera

Then run the generator:

$ bun run codegen

The builder introspects each package's public widgets with Dart's analyzer — constructors, named constructors, parameter types — and emits adapters that register them with Skal's widget registry. On the JS side, typed components appear under skal-flutter:

import { QrImageView, ShimmerFromColors, Camera } from 'skal-flutter';

<QrImageView data="https://skal.dev" size={220} />

<ShimmerFromColors baseColor="#FF1F2937" highlightColor="#FF374151">
  <Box width="fill" height={96} cornerRadius={12} />
</ShimmerFromColors>
Named constructors surface as their own components — shimmer's Shimmer.fromColors becomes <ShimmerFromColors>. Prop types round-trip: enums, colors, durations, edge insets.

Calling native APIs (not just widgets)

Plugins with imperative APIs — geolocation, biometrics, file pickers — ship as shim packages with one async JS surface:

import { getCurrentPosition } from 'skal-plugin-geolocator';

const pos = await getCurrentPosition();
// { lat, lon, accuracy, altitude, speed, timestamp }

On web this routes through the hidden Flutter plugin host today; the native routing for the same calls is in progress. Imperative native work is done today through host-widget RPC: bind a ref, then await ref.method(args) — one RPC op out, the reply back through the reply heap, and Dart Streams subscribe via ref.name$(cb). The Camera and Ticker demos in kitchen-sink use exactly this.

Custom Dart widgets

Your own Flutter widgets register the same way — a small adapter reads props off the node and returns your widget:

// flutter-host/lib/adapters/gauge.dart
Widget _buildGauge(NodeState n, SkalBridge bridge) =>
    Gauge(value: n.getCustomPropF32('value', 0));

SkalRegistry.registerWidget('gauge', _buildGauge);
// <Gauge value={0.7} /> is now available in JSX

For full control (custom prop decoding, children handling, RPC methods), the generated adapters in lib/adapters/generated/ are the documentation for the shape.

When codegen isn't enough

  • Function-valued parameters (builders, custom painters) can't cross the bridge declaratively — wrap those widgets manually with a constrained prop surface.
  • Web target: plugin calls route through a headless Flutter Web instance, so the same shim API works in the browser for plugins with web implementations.