// pubspec.yaml dependencies:
// flutter: sdk: flutter
// embed_flutter: ^0.0.17
// go_router: ^14.0.0
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:embed_flutter/embed_flutter.dart';
// ════════════════════════════════════════════════════════════════════════════════
// main.dart
// ════════════════════════════════════════════════════════════════════════════════
void main() {
embedInitialize('your-api-key', flowName: 'main');
embedSetAppVersion('1.0.0'); // optional
runApp(const MyApp());
}
// ════════════════════════════════════════════════════════════════════════════════
// Router
// ════════════════════════════════════════════════════════════════════════════════
final _router = GoRouter(
initialLocation: '/login',
observers: [EmbedNavigatorObserver()],
routes: [
GoRoute(
path: '/login',
name: 'login',
builder: (_, __) => const LoginScreen(),
),
// Persistent tab shell
ShellRoute(
builder: (_, __, child) => MainShell(child: child),
routes: [
GoRoute(
path: '/home',
name: 'home',
builder: (_, __) => const HomeTab(),
),
GoRoute(
path: '/plans',
name: 'plans',
builder: (_, __) => const PlansTab(),
routes: [
GoRoute(
path: 'detail/:id',
name: 'plan_detail',
builder: (_, s) => PlanDetailScreen(id: s.pathParameters['id']!),
),
],
),
GoRoute(
path: '/profile',
name: 'profile',
builder: (_, __) => const ProfileTab(),
),
],
),
// Full-screen checkout screens outside the tab shell
GoRoute(
path: '/cart',
name: 'cart',
builder: (_, __) => const CartScreen(),
),
GoRoute(
path: '/payment',
name: 'payment',
builder: (_, __) => const PaymentScreen(),
),
GoRoute(
path: '/confirmation',
name: 'confirmation',
builder: (_, __) => const ConfirmationScreen(),
),
],
);
// ════════════════════════════════════════════════════════════════════════════════
// Root app widget
// ════════════════════════════════════════════════════════════════════════════════
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return EmbedWidget(
showEmbedWidget: true,
enabledRoutes: const {
// Agent appears on these screens; hidden on login and profile
'main': ['home', 'plans', 'plan_detail', 'cart', 'payment', 'confirmation'],
},
rightPadding: 16,
bottomPadding: 100,
onPermissionStatusChanged: (granted) {
if (!granted) debugPrint('Mic permission denied');
},
child: MaterialApp.router(
routerConfig: _router,
theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.blue),
),
);
}
}
// ════════════════════════════════════════════════════════════════════════════════
// Shell scaffold
// ════════════════════════════════════════════════════════════════════════════════
class MainShell extends StatelessWidget {
final Widget child;
const MainShell({super.key, required this.child});
@override
Widget build(BuildContext context) {
final location = GoRouterState.of(context).uri.toString();
int idx = 0;
if (location.startsWith('/plans')) idx = 1;
if (location.startsWith('/profile')) idx = 2;
return Scaffold(
body: child,
bottomNavigationBar: NavigationBar(
selectedIndex: idx,
onDestinationSelected: (i) {
switch (i) {
case 0: context.go('/home'); break;
case 1: context.go('/plans'); break;
case 2: context.go('/profile'); break;
}
},
destinations: const [
NavigationDestination(icon: Icon(Icons.home), label: 'Home'),
NavigationDestination(icon: Icon(Icons.description), label: 'Plans'),
NavigationDestination(icon: Icon(Icons.person), label: 'Profile'),
],
),
);
}
}
// ════════════════════════════════════════════════════════════════════════════════
// Login screen — identify the user after sign-in
// ════════════════════════════════════════════════════════════════════════════════
class LoginScreen extends StatelessWidget {
const LoginScreen({super.key});
void _login(BuildContext context) {
// Your auth logic here ...
// Identify the user to the SDK after successful authentication
embedEvent(
EventKeys.USER_DATA,
UserEventPayload(
app_user_id: 'user_42',
data: {
'name': 'Rahul Verma',
'email': 'rahul@example.com',
'plan': 'free',
},
),
);
context.go('/home');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Login')),
body: Center(
child: ElevatedButton(
onPressed: () => _login(context),
child: const Text('Sign In'),
),
),
);
}
}
// ════════════════════════════════════════════════════════════════════════════════
// Tab screens — each wrapped with EmbedRouteListener
// ════════════════════════════════════════════════════════════════════════════════
class HomeTab extends StatelessWidget {
const HomeTab({super.key});
@override
Widget build(BuildContext context) {
return EmbedRouteListener(
routeName: 'home', // ✅ in enabledRoutes — agent visible
child: Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Center(
child: ElevatedButton(
onPressed: () => context.go('/cart'),
child: const Text('Go to Cart'),
),
),
),
);
}
}
class PlansTab extends StatelessWidget {
const PlansTab({super.key});
@override
Widget build(BuildContext context) {
return EmbedRouteListener(
routeName: 'plans', // ✅ in enabledRoutes — agent visible
child: Scaffold(
appBar: AppBar(title: const Text('Plans')),
body: ListView(
children: [
ListTile(
title: const Text('Gold Plan — ₹999/mo'),
onTap: () {
// Send context so the agent knows which plan is being viewed
embedEvent(
EventKeys.SCREEN_STATE,
ScreenEventPayload(
screen: 'plan_detail',
data: {'plan_id': 'gold', 'price': 999},
),
);
context.pushNamed('plan_detail', pathParameters: {'id': 'gold'});
},
),
ListTile(
title: const Text('Platinum Plan — ₹1999/mo'),
onTap: () {
embedEvent(
EventKeys.SCREEN_STATE,
ScreenEventPayload(
screen: 'plan_detail',
data: {'plan_id': 'platinum', 'price': 1999},
),
);
context.pushNamed('plan_detail', pathParameters: {'id': 'platinum'});
},
),
],
),
),
);
}
}
class ProfileTab extends StatelessWidget {
const ProfileTab({super.key});
@override
Widget build(BuildContext context) {
return EmbedRouteListener(
routeName: 'profile', // not in enabledRoutes — agent hidden
child: Scaffold(
appBar: AppBar(title: const Text('Profile')),
body: const Center(child: Text('Your profile')),
),
);
}
}
// ════════════════════════════════════════════════════════════════════════════════
// Plan detail screen
// ════════════════════════════════════════════════════════════════════════════════
class PlanDetailScreen extends StatelessWidget {
final String id;
const PlanDetailScreen({super.key, required this.id});
@override
Widget build(BuildContext context) {
// plan_detail is a top-level GoRoute so EmbedNavigatorObserver catches the push
return Scaffold(
appBar: AppBar(title: Text('Plan: $id')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Plan ID: $id', style: const TextStyle(fontSize: 18)),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () {
// Inform the agent the user selected a plan
embedEvent(
EventKeys.CUSTOM_EVENT,
CustomEventPayload(
data: {'action': 'plan_selected', 'plan_id': id},
),
);
context.go('/cart');
},
child: const Text('Add to Cart'),
),
],
),
),
);
}
}
// ════════════════════════════════════════════════════════════════════════════════
// Checkout screens
// ════════════════════════════════════════════════════════════════════════════════
class CartScreen extends StatelessWidget {
const CartScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Cart')),
body: Center(
child: ElevatedButton(
onPressed: () => context.go('/payment'),
child: const Text('Proceed to Payment'),
),
),
);
}
}
class PaymentScreen extends StatelessWidget {
const PaymentScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Payment')),
body: Center(
child: ElevatedButton(
onPressed: () {
// Track the conversion event
embedEvent(
EventKeys.ANALYTICS_DATA,
AnalyticsDataEventPayload(
event_name: 'payment_completed',
data: {'amount': 999, 'currency': 'INR', 'method': 'upi'},
),
);
context.go('/confirmation');
},
child: const Text('Pay ₹999'),
),
),
);
}
}
class ConfirmationScreen extends StatelessWidget {
const ConfirmationScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Confirmed!')),
body: const Center(child: Text('Your order is placed.')),
);
}
}