Extensions

Extension Authoring

Extension Authoring

Ruflet extensions follow Flet's user-extension architecture: a typed Ruby DSL control and a Flutter package share one exact wire type. The Flutter package exports Extension, subclasses FletExtension, and returns the widget from createWidget. Ruflet does not require a separate client adapter.

Package shape

The Ruby side defines a Ruflet::Control subclass with an explicit TYPE, accepted KEYWORDS, event properties, and any mounted-control methods. Register it with Ruflet::Extensions.register_control so the helper is available in the bare DSL, Ruflet, Ruflet::UI, and widget builders:

module MyExtension
  class RatingControl < Ruflet::Control
    TYPE = "rating"
    KEYWORDS = %i[value maximum on_change width height].freeze

    def initialize(id: nil, **props)
      super(type: TYPE, id: id, **props)
    end
  end
end

Ruflet::Extensions.register_control(
  :rating,
  control_class: MyExtension::RatingControl,
  flutter: {
    package: "ruflet_rating",
    import: "package:ruflet_rating/ruflet_rating.dart",
    alias: "ruflet_rating",
    constructor: "Extension"
  }
)

The Flutter package exposes the normal Flet extension entrypoint:

library;

import "package:flet/flet.dart";
import "package:flutter/widgets.dart";

class Extension extends FletExtension {
  @override
  Widget? createWidget(Key? key, Control control) {
    if (control.type != "rating") return null;
    return LayoutControl(
      control: control,
      child: RatingWidget(key: key),
    );
  }
}

Keep Ruby and Dart property names and defaults identical. Flutter events check the matching on_<event> flag and call control.triggerEvent with JSON-safe data. Mounted Ruby methods call runtime_page.invoke and should accept timeout: and on_result:.

Use the extension

Add the extension under extensions: in ruflet.yaml, then rebuild your app.

For an extension Ruflet does not include, follow Use an extension Ruflet does not bundle.