embed_flutter 0.0.6 copy "embed_flutter: ^0.0.6" to clipboard
embed_flutter: ^0.0.6 copied to clipboard

unlisted

A powerful Flutter SDK that provides a powerful voice-enabled AI-agent functionality with real-time widget tree monitoring for screen context fetching and realtime voice communication.

Embed Flutter SDK #

A powerful Flutter SDK that provides a powerful voice-enabled AI-agent functionality with real-time widget tree monitoring for screen context fetching and realtime voice communication.

Table of Contents #

Installation #

Add the following dependency to your pubspec.yaml:

dependencies:
  embed_flutter: ^0.0.6

Then run:

flutter pub get

Android Configuration

Add the following permissions to your android/app/src/main/AndroidManifest.xml:

 <!-- Required permissions for Embed SDK -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MICROPHONE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

<!-- Add the following permissions for embed -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MICROPHONE" />

iOS Configuration

Add the following permissions to your ios/Runner/Info.plist:

<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to microphone for voice calls.</string>

Getting Started #

Initialization #

Before using any EmbedWidget, you must initialize the configuration once in your main.dart. You can initialize multiple API keys for different flows:

void main() {
  // Initialize API keys for different flows
  embedInitialize(
    'api-key-for-flow-1',
    flowName: 'flow 1', // Optional: Flow identifier
    embedUrl: 'https://embed.revrag.ai', // Optional
  );

  embedInitialize(
    'api-key-for-flow-2',
    flowName: 'flow 2', // Optional: Flow identifier
    embedUrl: 'https://embed.revrag.ai', // Optional
  );

  runApp(const MyApp());
}

Note: Each embedInitialize() call saves the API key for that specific flow. When a user navigates to a route, the SDK automatically detects which flow the route belongs to and uses the corresponding API key.

Required: Navigator Observer

For route-based activation to work properly, you must add EmbedNavigatorObserver to your app's navigator observers. This enables the SDK to track route changes and activate/deactivate the EmbedWidget accordingly.

For GoRouter:

import 'package:go_router/go_router.dart';
import 'package:embed_flutter/embed_flutter.dart';

final GoRouter router = GoRouter(
  observers: [EmbedNavigatorObserver()], // Required for route tracking
  routes: [
    // Your routes here
  ],
);

For MaterialApp:

import 'package:embed_flutter/embed_flutter.dart';

MaterialApp(
  navigatorObservers: [EmbedNavigatorObserver()], // Required for route tracking
  // Other MaterialApp properties
)

Required: USER_DATA Event

To activate the EmbedWidget on the screen, you must call the USER_DATA embedEvent with a user ID. This is typically done after user authentication or when you have a user identifier:

import 'package:embed_flutter/embed_flutter.dart';

// Call this after user authentication or when you have a user ID
embedEvent(
  EventKeys.USER_DATA,
  UserEventPayload(
    app_user_id: 'user_12345', // Required: Unique user identifier
    data: {
      'name': 'John Doe', // Optional: Additional user data
      'email': 'john@example.com',
      'phone': '+1234567890',
    },
  ),
);

Important: The EmbedWidget will not be fully functional until the USER_DATA event is sent. This event initializes the voice communication features.

Optional: SCREEN_STATE Event

You can optionally send SCREEN_STATE events to provide additional context about screen changes or user navigation:

// Send screen state when navigating to a new screen
embedEvent(
  EventKeys.SCREEN_STATE,
  ScreenEventPayload(
    screen: 'product_selection_screen', // Required: Screen identifier
    data: {
      'category': 'banking', // Optional: Additional screen context
      'user_type': 'premium',
      'flow_step': 'selection',
    },
  ),
);

Optional: CUSTOM_EVENT

CUSTOM_EVENT lets you capture bespoke analytics or interaction events that matter to your flows (for example, plan selections, button taps, or offer views). Supply any key/value metadata you want to track:

embedEvent(
  EventKeys.CUSTOM_EVENT,
  CustomEventPayload(
    data: {
      'context': 'pricing_screen',
      'event': 'option_selected',
      'option_id': 'plan_12_months',
      'price_per_month': 249,
      'currency': 'INR',
    },
  ),
);

Tip: Combine CUSTOM_EVENT with your flow configuration to build rich analytics around user journeys.

Available Events

Event Purpose Required When to Use
USER_DATA Initialize user context and activate EmbedWidget Required After user authentication or when user ID is available
SCREEN_STATE Provide screen context and navigation info Optional When navigating between screens or when screen context changes
CUSTOM_EVENT Capture bespoke user interactions or analytics Optional Whenever you need additional tracking for specific actions

Basic Usage #

Here's a complete example showing how to use the SDK with flow-based activation:

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

void main() {
  // 1. Initialize the SDK with flow-based API keys
  embedInitialize('api-key-for-flow-1', flowName: 'flow 1');
  embedInitialize('api-key-for-flow-2', flowName: 'flow 2');

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return EmbedWidget(
      showEmbedWidget: true,
      // 4. Configure flow-based activation
      enabledRoutes: {
        'flow 1': ['home', 'product_selection_screen'],
        'flow 2': ['checkout', 'payment'],
      },
      child: MaterialApp(
        navigatorObservers: [EmbedNavigatorObserver()], // Required for route tracking
        routes: [
          'home': (context) => const MyHomePage(),
          'product_selection_screen': (context) => const ProductSelectionScreen(),
          'checkout': (context) => const CheckoutScreen(),
          'payment': (context) => const PaymentScreen(),
        ]
        home: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    super.initState();

    // 2. Send USER_DATA event to activate EmbedWidget
    // This should typically be called after user authentication
    embedEvent(
      EventKeys.USER_DATA,
      UserEventPayload(
        app_user_id: 'user_12345', // Replace with actual user ID
        data: {
          'name': 'John Doe',
          'email': 'john@example.com',
          'plan': 'premium',
        },
      ),
    );
  }

  void _navigateToProductScreen() {
    // 3. Send SCREEN_STATE event for additional context (Optional)
    embedEvent(
      EventKeys.SCREEN_STATE,
      ScreenEventPayload(
        screen: 'product_selection_screen',
        data: {
          'category': 'banking_products',
          'user_segment': 'new_customer',
        },
      ),
    );

    // Navigate to product screen
    Navigator.pushNamed(context, 'product_selection');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('My App')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('Welcome to My App!'),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _navigateToProductScreen,
              child: const Text('Go to Product Selection'),
            ),
          ],
        ),
      ),
    );
  }
}

Required Parameters #

  • child (required): The main content of your app

Flow-Based Activation #

The SDK supports intelligent flow-based activation, allowing you to organize routes into flows and assign different API keys to each flow. When a user navigates to a route, the SDK automatically detects which flow the route belongs to and uses the corresponding API key.

Basic Flow Configuration

void main() {
  // Initialize API keys for different flows
  embedInitialize('api-key-for-flow-1', flowName: 'flow 1');
  embedInitialize('api-key-for-flow-2', flowName: 'flow 2');

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return EmbedWidget(
      // Configure flows with their associated routes
      enabledRoutes: {
        'flow 1': [
          'personal_info',
          'personal_details',
          'product_selection_screen',
          'additional_info',
          'review_confirm_screen',
        ],
        'flow 2': [
          'aadhar_verification_screen',
        ],
      },
      child: MaterialApp.router(
        routerConfig: appRouter,
      ),
    );
  }
}

How Flow-Based Activation Works

  1. Flow Detection: When a user navigates to a route, the SDK automatically detects which flow the route belongs to based on your enabledRoutes configuration.

  2. API Key Selection: The SDK retrieves the API key that was saved for the detected flow during initialization.

  3. Flow Switching: When switching between flows (or navigating outside any flow), the EmbedWidget automatically:

    • Ends any active calls
    • Re-initializes with the new flow's API key when entering a new flow
  4. Route Matching: Routes are matched using the routeMatchMode setting (default: RouteMatchMode.exact).

Route Configuration Options

  • enabledRoutes: Map<String, List<String>> - Flow name to list of route names mapping
    • Key: Flow identifier (e.g., 'flow 1', 'flow 2')
    • Value: List of route names that belong to that flow
  • showOnAllRoutes: Show EmbedWidget on all routes (default: false)
  • disabledRoutes: List of route names where EmbedWidget should be disabled
  • routeMatchMode: How to match routes:
    • RouteMatchMode.exact - Exact match (default)
    • RouteMatchMode.startsWith - Route starts with pattern
    • RouteMatchMode.contains - Route contains pattern

Optional Parameters #

  • apiKey (optional): Your Revrag AI API key for authentication (can be set globally via embedInitialize() with flowName)
  • embedUrl (optional): Custom base URL for API endpoints (can be set globally via embedInitialize())
  • showEmbedWidget (default: true): Controls whether the embedded widget is visible
  • enabledRoutes (optional): Map<String, List<String>> - Flow name to route names mapping for flow-based activation
  • showOnAllRoutes (default: false): Show EmbedWidget on all routes
  • disabledRoutes (optional): List of route names where EmbedWidget should be disabled
  • routeMatchMode (default: RouteMatchMode.exact): How to match routes

Examples #

Basic Integration #

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

void main() {
  // Initialize the SDK
  embedInitialize('your-api-key-here');
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return EmbedWidget(
      showEmbedWidget: true,
      child: MaterialApp(
        navigatorObservers: [EmbedNavigatorObserver()], // Required for route tracking
        home: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    super.initState();
    // Send USER_DATA event to activate EmbedWidget
    // This should typically be called after user authentication
    embedEvent(
      EventKeys.USER_DATA,
      UserEventPayload(
        app_user_id: 'user_12345', // Replace with actual user ID
        data: {
          'name': 'John Doe',
          'email': 'john@example.com',
        },
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('My App')),
      body: const Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Welcome to My App!'),
            SizedBox(height: 20),
            Text('The embedded agent is monitoring this screen.'),
          ],
        ),
      ),
    );
  }
}

Form with Monitoring #

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

void main() {
  // Initialize the SDK
  embedInitialize('your-api-key-here');
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return EmbedWidget(
      showEmbedWidget: true,
      child: MaterialApp(
        navigatorObservers: [EmbedNavigatorObserver()], // Required for route tracking
        home: const MyFormPage(),
      ),
    );
  }
}

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

  @override
  State<MyFormPage> createState() => _MyFormPageState();
}

class _MyFormPageState extends State<MyFormPage> {
  final _formKey = GlobalKey<FormState>();
  final _nameController = TextEditingController();
  final _emailController = TextEditingController();

  @override
  void initState() {
    super.initState();
    // Send USER_DATA event to activate EmbedWidget
    // This should typically be called after user authentication
    embedEvent(
      EventKeys.USER_DATA,
      UserEventPayload(
        app_user_id: 'user_12345', // Replace with actual user ID
        data: {
          'name': 'John Doe',
          'email': 'john@example.com',
        },
      ),
    );

    // Send SCREEN_STATE event for additional context
    embedEvent(
      EventKeys.SCREEN_STATE,
      ScreenEventPayload(
        screen: 'form_page',
        data: {
          'form_type': 'user_registration',
          'step': 'personal_info',
        },
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('My Form')),
      body: Form(
        key: _formKey,
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            children: [
              TextFormField(
                controller: _nameController,
                decoration: const InputDecoration(labelText: 'Name'),
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Please enter your name';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 16),
              TextFormField(
                controller: _emailController,
                decoration: const InputDecoration(labelText: 'Email'),
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Please enter your email';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {
                  if (_formKey.currentState!.validate()) {
                    // Form is valid - EmbedWidget will track the submission
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(content: Text('Form submitted successfully!')),
                    );
                  }
                },
                child: const Text('Submit'),
              ),
            ],
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    _nameController.dispose();
    _emailController.dispose();
    super.dispose();
  }
}

Flow-Based Configuration #

Configure the EmbedWidget with flow-based activation to organize routes into flows and use different API keys:

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

void main() {
  // Initialize API keys for different flows
  embedInitialize('api-key-for-onboarding-flow', flowName: 'onboarding');
  embedInitialize('api-key-for-checkout-flow', flowName: 'checkout');

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return EmbedWidget(
      // Configure flows with their associated routes
      enabledRoutes: {
        'onboarding': [
          'welcome',
          'personal-info',
          'personal-details',
          'additional-info'
        ],
        'checkout': [
          'product-selection',
          'payment',
          'review-confirm'
        ],
      },
      child: MaterialApp(
        navigatorObservers: [EmbedNavigatorObserver()], // Required for route tracking
        initialRoute: '/',
        routes: {
          '/': (context) => const SplashScreen(),
          'welcome': (context) => const WelcomeScreen(),
          'personal-info': (context) => const PersonalInfoScreen(),
          'personal-details': (context) => const PersonalDetailsScreen(),
          'product-selection': (context) => const ProductSelectionScreen(),
          'additional-info': (context) => const AdditionalInfoScreen(),
        },
      ),
    );
  }
}

Flow Behavior

  • Within a Flow: When navigating between routes in the same flow, the EmbedWidget remains active and uses the same API key.
  • Between Flows: When switching from one flow to another, the EmbedWidget:
    • Automatically ends any active calls
    • Cleans up resources
    • Re-initializes with the new flow's API key
  • Outside Flows: When navigating to routes not in any configured flow, the EmbedWidget is hidden and cleaned up.

Custom Router Integration #

An Example to show Full integration with Custom Routers we can take example of GoRouter for declarative navigation:

import 'package:go_router/go_router.dart';
import 'package:embed_flutter/embed_flutter.dart';

final appRouter = GoRouter(
  initialLocation: '/splash',
  routes: [
    GoRoute(
      path: '/splash',
      name: 'splash_screen',
      builder: (context, state) => const SplashScreen(),
    ),
    GoRoute(
      path: '/welcome',
      name: 'welcome_screen',
      builder: (context, state) => const WelcomeScreen(),
    ),
    GoRoute(
      path: '/personal-info',
      name: 'personal_info',
      builder: (context, state) => const PersonalInfoScreen(),
    ),
    // ... more routes
  ],
  // Add EmbedNavigatorObserver to the list of observers
  observers: [
    EmbedNavigatorObserver(),
    // ... other observers
  ],
);

void main() {
  WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
  widgetsBinding = EmbedWidgetsBinding();

  // Initialize API keys for different flows
  embedInitialize('api-key-for-flow-1', flowName: 'flow 1');
  embedInitialize('api-key-for-flow-2', flowName: 'flow 2');

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return EmbedWidget(
      enabledRoutes: {
        'flow 1': ['welcome_screen', 'personal_info', 'product_selection_screen'],
        'flow 2': ['checkout', 'payment'],
      },
      child: MaterialApp.router(
        routerConfig: appRouter,
      ),
    );
  }
}

Configuration #

Custom Base URL #

You can set a custom base URL globally during initialization:

void main() {
  embedInitialize(
    'your-api-key',
    embedUrl: 'https://your-custom-domain.com',
  );
  runApp(const MyApp());
}

Troubleshooting #

Common Issues #

  1. Widget Not Visible

    • Check showEmbedWidget parameter
    • Verify API key is valid
    • Check network connectivity
  2. Voice Call Issues

    • Ensure microphone permission is granted
    • Check device audio settings
  3. Widget Tree Not Updating

    • Check widget tree handler initialization
    • Verify context is available
    • Check for widget disposal issues
  4. Flow-Based Activation Not Working

    • Verify route names match exactly (case-sensitive)
    • Check EmbedNavigatorObserver is added to router observers
    • Ensure GoRouter is properly configured with named routes
    • Verify enabledRoutes map contains correct flow names and route names
    • Ensure each flow has been initialized with embedInitialize() and flowName parameter
    • Check that API keys are saved correctly for each flow (check console logs)
    • Verify the current route belongs to a configured flow
  5. Button Click Detection Issues

    • Ensure buttons have proper keys for identification
    • Check if buttons are properly tracked in widget tree
    • Verify hit testing permissions and context
    • Test with different button types (ElevatedButton, TextButton, etc.)
  6. EmbedWidget Not Appearing on Expected Screens

    • Check enabledRoutes configuration (flow-based map structure)
    • Check disabledRoutes configuration
    • Verify route matching mode (exact vs startsWith vs contains)
    • Check if showEmbedWidget is set to true
    • Verify the route belongs to a configured flow
    • Check console logs for flow detection messages
  7. Wrong API Key Being Used for a Flow

    • Verify embedInitialize() was called with the correct flowName for each flow
    • Check that API keys are saved correctly (check console logs for LocalStorageHandler.saveAPIKey)
    • Ensure flow names in enabledRoutes match the flowName used in embedInitialize()
    • Check console logs for EmbedConfigService.apiKey to see which API key is being retrieved

License #

This project is licensed under the Non-Commercial License License - see the LICENSE file for details.

Support #

Changelog #

See CHANGELOG.md for a complete list of changes and version history.


0
likes
0
points
131
downloads

Documentation

Documentation

Publisher

verified publisherrevrag.ai

Weekly Downloads

A powerful Flutter SDK that provides a powerful voice-enabled AI-agent functionality with real-time widget tree monitoring for screen context fetching and realtime voice communication.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, flutter_webrtc, livekit_client, lottie, permission_handler, shared_preferences

More

Packages that depend on embed_flutter