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

Ringg AI widget for Flutter — drop-in widget and headless controller for voice/text AI agents.

example/lib/main.dart

// Example host page — the Flutter cut of the web example's App.tsx.
//
// The page itself stays bare (the web example's empty index.html body): a
// plain white Scaffold whose only tenants are the RinggWidget under test and
// the floating dev config panel. The panel's config edits recreate the
// harness (controller + transport), exactly like the web example — the
// conversation resets with it.
//
// Offline by default (mock transport + mock backend, no secrets). Real mode:
//   flutter run -d macos --dart-define-from-file=dart_defines.json

import 'package:flutter/material.dart';
import 'package:ringg_flutter/ringg_flutter.dart';

import 'dev_panel/dev_config_panel.dart';
import 'dev_panel/dev_overrides.dart';
import 'dev_panel/event_log.dart';
import 'harness.dart';
import 'probe.dart';

/// Flip to true to boot the headless-core dev probe (probe.dart) instead of
/// the styled widget — core debugging without the UI in the way.
const bool kUseProbe = false;

void main() => runApp(kUseProbe ? const ProbeApp() : const ExampleApp());

final class ExampleApp extends StatelessWidget {
  const ExampleApp({super.key});

  @override
  Widget build(BuildContext context) => MaterialApp(
        title: 'Ringg Widget Example',
        debugShowCheckedModeBanner: false,
        theme: ThemeData(colorSchemeSeed: const Color(0xFF0A0A0B)),
        home: const HostPage(),
      );
}

final class HostPage extends StatefulWidget {
  const HostPage({super.key});

  @override
  State<HostPage> createState() => _HostPageState();
}

final class _HostPageState extends State<HostPage> {
  // The "window listener" of this harness — host events and dom-action
  // dispatches land here; the panel's Events tab renders it.
  final DevEventLog _eventLog = DevEventLog();

  DevOverrides _overrides = DevOverrides();
  late Harness _harness = _createHarness();

  Harness _createHarness() => Harness.create(
        config: _overrides.apply(baseConfig()),
        eventBus: createLoggingEventBus(_eventLog),
        onDomAction: (action, log) =>
            executeDevDomAction(action, _eventLog, log: log),
      );

  /// Web parity (App.tsx onConfigChange): swap in a fresh harness for the new
  /// config; the old one is disposed after the frame so the outgoing widget
  /// subtree never touches a destroyed controller.
  void _applyOverrides(DevOverrides next) {
    final old = _harness;
    setState(() {
      _overrides = next;
      _harness = _createHarness();
    });
    WidgetsBinding.instance.addPostFrameCallback((_) => old.dispose());
  }

  @override
  void dispose() {
    _harness.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final controller = _harness.controller;
    return Scaffold(
      backgroundColor: Colors.white,
      body: Stack(
        children: [
          // The widget under test fills the page — it positions its trigger
          // and panel within this box (the web's fixed positioning).
          // ObjectKey remounts the subtree per harness so no view carries
          // state across a config rebuild.
          Positioned.fill(
            child: RinggWidget(
              key: ObjectKey(controller),
              controller: controller,
              transport: _harness.livekit,
            ),
          ),
          // Dev chrome on top. isCalling mirrors the web example's
          // `session.connectionState === "connected"` gate. SafeArea keeps
          // the gear/panel clear of the home indicator + status bar on mobile.
          Positioned.fill(
            child: SafeArea(
              child: StoreBuilder<ShellSnapshot>(
                store: controller.shell,
                builder: (context, shell, _) => StoreBuilder<SessionSnapshot>(
                  store: controller.session,
                  builder: (context, session, _) => DevConfigPanel(
                    widgetOpen: shell.viewState != WidgetViewState.closed,
                    overrides: _overrides,
                    baseConfig: baseConfig(),
                    onOverridesChanged: _applyOverrides,
                    // The exact call the web dev panel makes: raw wire payload →
                    // passthrough component → the full typed-parse + buffering
                    // path, no backend needed.
                    onInjectComponent: (payload) => controller.components.add(
                      PassthroughComponentPayload(payload),
                      controller.shell.snapshot.callMode,
                    ),
                    onOpenWidget: controller.openWidget,
                    onFireDomAction: (action) =>
                        executeDevDomAction(action, _eventLog),
                    eventLog: _eventLog,
                    isCalling: session.connectionState ==
                        TransportConnectionState.connected,
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
0
likes
0
points
113
downloads

Publisher

verified publisherringg.ai

Weekly Downloads

Ringg AI widget for Flutter — drop-in widget and headless controller for voice/text AI agents.

Homepage
Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

audioplayers, flutter, flutter_markdown_plus, flutter_svg, http, livekit_client, markdown, url_launcher

More

Packages that depend on ringg_flutter