plugin_kit_dialog 0.1.0 copy "plugin_kit_dialog: ^0.1.0" to clipboard
plugin_kit_dialog: ^0.1.0 copied to clipboard

Flutter dialog UI for inspecting and editing a plugin_kit PluginRuntime at runtime. Browse plugins, services, capabilities, and live configuration.

plugin_kit_dialog. A live customization dialog for plugin_kit.

pub package License: BSD-3-Clause

A drop-in Flutter dialog for inspecting and editing any plugin_kit PluginRuntime at runtime. Users get a three-tab UI for toggling plugins, editing service fields, and browsing the registry, without you writing a settings screen per plugin set.

plugin_kit declares the fields in Dart. plugin_kit_dialog renders them in Flutter.

  • Plugins tab: enable/disable each plugin; locked entries can't be toggled, experimental ones are flagged.
  • Services tab: each resolved winning service registration that ships a UiConfigurableCapability becomes an editable card. Edit text, numbers, dropdowns, switches, multiline, password, grouped, or custom fields.
  • Advanced tab: registry inspector with priority chains, winners, shadowed contenders, plus a JSON view of the working settings.

Plugin Kit Dialog Plugins tab showing a grid of registered plugins with enable/disable toggles, stable/experimental tiers, and per-plugin icons and colors

Plugin Kit Dialog Services tab showing expanded service cards grouped by namespace, with capability chips and configuration fields

Plugin Kit Dialog Advanced tab showing the service registry inspector with namespaces, competing registrations, priority badges, and the current winner picked out

The dialog runtime is built from plugin_kit plugins internally. Tabs and field renderers come from fixed built-in dialog plugins, while header actions are built-in widget callbacks.

Quick start #

Future<void> openConfigDialog(
  BuildContext context,
  PluginRuntime myRuntime,
  RuntimeSettings currentSettings,
) async {
  final next = await showPluginKitDialog(
    context: context,
    runtime: myRuntime,
    initialSettings: currentSettings,
    onSave: (settings) async {
      await persistSettings(settings); // write to disk, push to runtime, etc.
    },
  );
  if (next != null) {
    // User saved. `next` is the merged RuntimeSettings.
  }
}

That's it. If your plugins already attach UiConfigurableCapability, the Services tab populates itself from global registrations, then from the most recently created session registry after a session starts.

Declaring configurable services #

Configurability is opt-in per registration. Attach a UiConfigurableCapability next to any service:

void registerConfigurableService(ScopedServiceRegistry registry) {
  const agent = Namespace('agent');

  registry.registerSingleton<MyService>(
    agent('temperature'),
    () => const MyService(),
    capabilities: const {
      UiConfigurableCapability(
        label: 'Temperature',
        description: 'Controls randomness in responses.',
        fields: [
          NumberConfigField(
            key: 'temperature',
            label: 'Temperature',
            min: 0,
            max: 2,
            step: 0.1,
            defaultValue: 1.0,
          ),
        ],
      ),
    },
  );
}

Saved values flow through RuntimeSettings.services[Pin('main_agent', ['agent', 'temperature'])].config (or the typed chain pluginId.namespace('agent').service('temperature')).

Built-in field types #

All live in plugin_kit (Dart-only):

Field Renders as
TextConfigField Single-line TextField.
MultilineConfigField Multiline editor with optional moustache-tag chips.
PasswordConfigField Obscured input with show/hide toggle.
NumberConfigField Slider when both min and max set; numeric TextField otherwise. style: NumberFieldStyle.textInput forces text mode. isInteger: true stores int and snaps to whole numbers.
DropdownConfigField<T> Typed dropdown over List<DropdownOption<T>>.
BoolConfigField Switch with label + helper.
GroupConfigField Indented sub-section grouping nested fields under a heading.
ExtensionConfigField Escape hatch for custom Flutter renderers (see below).

Each field carries key, label, helperText, and defaultValue. Dotted keys (provider.api_key) write to nested maps automatically.

Visuals (icons, colors, labels) #

Visuals are a Flutter-only concern, so the canonical attachment path is one locked GlobalPlugin (PluginKitVisualsPlugin) that the host app adds to the runtime alongside its other plugins. It carries three independent maps for the three things the dialog renders: plugin tiles, namespace section headers, and individual service cards.

void addVisualsPlugin(PluginRuntime runtime, List<Plugin> myPlugins) {
  runtime
    ..addPlugins(myPlugins)
    ..addPlugin(
      PluginKitVisualsPlugin(
        pluginVisuals: {
          const PluginId('main_agent'): const PluginKitVisual(
            label: 'Main Agent',
            description: 'The brain. Drives chat, tools, and routing.',
            icon: Icon(Icons.psychology),
            color: Color(0xFF7C5CFF),
          ),
        },
        namespaceVisuals: {
          const Namespace('agent'): const PluginKitVisual(
            label: 'Agent',
            icon: Icon(Icons.smart_toy),
            color: Color(0xFF7C5CFF),
          ),
        },
        serviceVisuals: {
          const Namespace('agent')('temperature'): const PluginKitVisual(
            label: 'Temperature',
            icon: Icon(Icons.thermostat),
            color: Color(0xFFFF9500),
          ),
        },
      ),
    );
}

The visuals plugin lives in your host app (which already depends on Flutter), so Dart-only plugins still get rich visuals without importing Flutter themselves. Decoration is keyed by PluginId, Namespace, or ServiceId, so the host owns the map and the plugin source stays portable.

  • Unknown keys are accepted silently. Keep a visual ready for a plugin you may enable later.
  • When no visual is found, cards fall back to a generic gear icon and the theme's primary color.

Custom field renderers #

Need a color picker, file selector, or any other widget? Declare an ExtensionConfigField from anywhere (no Flutter needed at the field site):

const extensionField = ExtensionConfigField(
  key: 'theme.accent',
  label: 'Accent color',
  rendererKey: 'color_picker',
  args: {'allow_alpha': false},
);

The default showPluginKitDialog and PluginKitDialogBody entry points use a private dialog runtime with fixed built-in plugins, so host-runtime renderer registrations are not used:

/// A custom field renderer for color values (Flutter-side).
class ColorPickerRenderer implements ConfigFieldRenderer<ExtensionConfigField> {
  /// Creates a [ColorPickerRenderer].
  const ColorPickerRenderer();

  @override
  Widget build(
    BuildContext context,
    ExtensionConfigField field,
    ConfigFieldHandle handle,
    FieldRenderResolver resolveRenderer,
  ) {
    final allowAlpha = field.args['allow_alpha'] as bool? ?? false;
    return Slider(
      value: ((handle.value as int?) ?? 0xFF000000).toDouble(),
      min: 0,
      max: 0xFFFFFFFF.toDouble(),
      onChanged: (next) => handle.value = next.toInt(),
      label: allowAlpha ? 'ARGB' : 'RGB',
    );
  }
}

class ColorPickerRendererPlugin extends GlobalPlugin {
  @override
  PluginId get pluginId => const PluginId('color_picker_renderer');

  @override
  void register(ScopedServiceRegistry registry) {
    registry.registerFactory<ConfigFieldRenderer>(
      FieldRenderersPlugin.namespace('color_picker'),
      ColorPickerRenderer.new,
    );
  }
}

If a renderer key is unknown when the dialog tries to resolve it, an inline placeholder card surfaces the missing key; the dialog never throws at paint time.

Theming #

Pass a PluginKitDialogTheme to override accents, surfaces, and badges:

Future<void> openConfigDialog(
  BuildContext context,
  PluginRuntime myRuntime,
  RuntimeSettings currentSettings,
) async {
  final next = await showPluginKitDialog(
    context: context,
    runtime: myRuntime,
    initialSettings: currentSettings,
    onSave: (settings) async {
      await persistSettings(settings); // write to disk, push to runtime, etc.
    },
  );
  if (next != null) {
    // User saved. `next` is the merged RuntimeSettings.
  }
}

Or wrap your app with buildPluginKitDialogDarkTheme() / buildPluginKitDialogLightTheme() to adopt the full Material 3 ThemeData.

Why dart-only declaration matters #

The capability + field types live in plugin_kit, not in this package. That means a non-Flutter package (server-side, CLI, shared common/ library) can declare configurable services without taking a Flutter dependency. The Flutter UI layers on top through:

  • PluginKitVisualsPlugin (Flutter, host-app side) for icons, labels, and colors across the plugin, namespace, and service axes,
  • ExtensionConfigField + a registered Flutter renderer for custom widgets.

That keeps your shared plugin packages portable. The host app owns the Flutter-only glue.

Saving and dirty state #

The dialog is non-destructive:

  • Edits accumulate in a working draft. Nothing reaches the runtime until the user hits Save.
  • onSave receives the merged RuntimeSettings. Persist it however you like (disk, network, runtime push).
  • Cancel discards the draft, with a confirm prompt if there are unsaved changes.
  • Overrides that match the active baseline are pruned automatically, so RuntimeSettings stays minimal.

Example app #

Run example/plugin_kit_dialog_demo or open the live web build to see priorities, tiers, and visuals in a full app.

The demo runs 20 competing plugins (priority towers on agent.model, agent.system_message, retry.policy, search.provider, plus locked and experimental tiers) and a PluginKitVisualsPlugin decorating every plugin, namespace, and service.

Public API #

The canonical surface lives in dartdoc on pub.dev. What follows is a curated index by concern.

import 'package:plugin_kit_dialog/plugin_kit_dialog.dart';

// Entry points
showPluginKitDialog(...);            // Future<RuntimeSettings?>
PluginKitDialog(...);                // raw widget (custom hosting)
PluginKitDialogBody(...);            // tab body (custom chrome)
PluginKitDialogController(...);      // ChangeNotifier-backed draft

// Visuals
PluginKitVisualsPlugin({pluginVisuals, namespaceVisuals, serviceVisuals});
PluginKitVisual({label, description, icon, color});

// Field renderers (custom widgets via ExtensionConfigField)
FieldRenderersPlugin;                // namespace under which renderers register
ConfigFieldRenderer<F extends ConfigField>;
FieldRenderResolver;                 // (ConfigField field) -> ConfigFieldRenderer

// Theme
buildPluginKitDialogDarkTheme();
buildPluginKitDialogLightTheme();
PluginKitDialogTheme;                // ThemeExtension

Declarative types come from plugin_kit:

import 'package:plugin_kit/plugin_kit.dart';

UiConfigurableCapability({label, fields, description});
TextConfigField, MultilineConfigField, PasswordConfigField,
NumberConfigField (NumberFieldStyle, isInteger),
DropdownConfigField<T>, DropdownOption<T>,
BoolConfigField, GroupConfigField,
ExtensionConfigField (rendererKey, args),
ConfigField                          // sealed base
ConfigFieldHandle                    // value/reset handle for renderers
void registerWithNumberField(ScopedServiceRegistry registry) {
  const agent = Namespace('agent');

  registry.registerSingleton<TemperatureService>(
    agent('temperature'), // ServiceId('agent.temperature')
    () => TemperatureService(),
    capabilities: const {
      UiConfigurableCapability(
        label: 'Temperature',
        description: 'Controls randomness in responses.',
        fields: [
          NumberConfigField(
            key: 'temperature',
            label: 'Temperature',
            min: 0,
            max: 2,
            step: 0.1,
            defaultValue: 1.0,
          ),
        ],
      ),
    },
  );
}
  • plugin_kit: the dart-only runtime this dialog inspects. Required.
  • flutter_plugin_kit: Flutter ergonomics (scope widgets, State mixin) for plumbing the runtime through your widget tree. The dialog composes naturally with its PluginRuntimeScope.

Documentation #

License #

BSD 3-Clause. See LICENSE.

0
likes
160
points
52
downloads

Documentation

API reference

Publisher

verified publishersaad-ardati.dev

Weekly Downloads

Flutter dialog UI for inspecting and editing a plugin_kit PluginRuntime at runtime. Browse plugins, services, capabilities, and live configuration.

Homepage
Repository (GitHub)
View/report issues

Topics

#plugins #flutter #dialog #developer-tools #inspector

License

BSD-3-Clause (license)

Dependencies

collection, flutter, plugin_kit

More

Packages that depend on plugin_kit_dialog