concierge_sdk 0.1.2
concierge_sdk: ^0.1.2 copied to clipboard
Concierge in-app support chat for Flutter. Wraps the native iOS and Android SDKs: native chat UI, AI replies, push, offers and client tools.
example/lib/main.dart
import 'dart:async';
import 'package:concierge_sdk/concierge_sdk.dart';
import 'package:flutter/material.dart';
/// The app key and API come from the build, so the same example talks to production, staging or
/// a local server:
///
/// flutter run --dart-define=CONCIERGE_APP_KEY=app_... --dart-define=CONCIERGE_BASE_URL=http://10.0.2.2:3000
const appKey = String.fromEnvironment(
'CONCIERGE_APP_KEY',
defaultValue: 'app_demo',
);
const baseUrl = String.fromEnvironment(
'CONCIERGE_BASE_URL',
defaultValue: Concierge.defaultBaseUrl,
);
void main() {
WidgetsFlutterBinding.ensureInitialized();
Concierge.configure(appKey: appKey, baseUrl: baseUrl, debug: true);
Concierge.registerTool(
'resync_library',
'Resync the music library',
{'type': 'object', 'properties': {}},
(args) async {
await Future<void>.delayed(const Duration(seconds: 1));
return {'tracks': 42};
},
title: 'Resync your library?',
);
Concierge.registerScreen(
'settings',
'Settings',
'conciergeexample://settings',
);
runApp(const ExampleApp());
}
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Concierge example',
theme: ThemeData(
colorSchemeSeed: const Color(0xFF3B5BDB),
useMaterial3: true,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final _userId = TextEditingController(text: 'flutter-demo-user');
final _log = <String>[];
late final StreamSubscription<ConciergeEvent> _events;
@override
void initState() {
super.initState();
_events = Concierge.events.listen(
(event) => setState(() => _log.insert(0, event.type)),
);
Concierge.onPaywallCard((paywallId) => _snack('Show paywall "$paywallId"'));
}
@override
void dispose() {
_events.cancel();
_userId.dispose();
super.dispose();
}
void _snack(String text) =>
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(text)));
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Concierge'),
actions: [
StreamBuilder<int>(
stream: Concierge.unreadCountStream,
initialData: Concierge.getUnreadCount(),
builder: (context, snapshot) => IconButton(
tooltip: 'Support',
onPressed: Concierge.open,
icon: Badge(
isLabelVisible: (snapshot.data ?? 0) > 0,
label: Text('${snapshot.data ?? 0}'),
child: const Icon(Icons.support_agent),
),
),
),
],
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
Text('App key: $appKey', key: const Key('appKey')),
Text('API: $baseUrl'),
const SizedBox(height: 16),
FilledButton.icon(
key: const Key('openChat'),
onPressed: Concierge.open,
icon: const Icon(Icons.chat_bubble_outline),
label: const Text('Help and support'),
),
const SizedBox(height: 16),
TextField(
controller: _userId,
decoration: const InputDecoration(
labelText: 'User id',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 8),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
OutlinedButton(
onPressed: () =>
Concierge.identify(_userId.text, name: 'Flutter Demo'),
child: const Text('Identify'),
),
OutlinedButton(
onPressed: Concierge.logout,
child: const Text('Log out'),
),
OutlinedButton(
onPressed: () => Concierge.setMetadata({
'plan': 'pro',
'platform': 'flutter',
}),
child: const Text('Set metadata'),
),
OutlinedButton(
onPressed: () =>
Concierge.presentCancelFlow(productId: 'pro_yearly'),
child: const Text('Cancel flow'),
),
OutlinedButton(
onPressed: () => Concierge.paywallHelp(
const PaywallContext(
paywallId: 'main',
products: [
PaywallProduct(
id: 'pro_yearly',
price: r'$59.99',
period: 'year',
),
],
),
),
child: const Text('Paywall help'),
),
OutlinedButton(
onPressed: () async {
final result = await Concierge.maybeRequestReview(
ReviewMoment.milestone,
);
_snack('Review prompt: ${result.reason}');
},
child: const Text('Review prompt'),
),
OutlinedButton(
onPressed: () async => _snack(
'Attestation: ${(await Concierge.getAttestationStatus()).name}',
),
child: const Text('Attestation'),
),
],
),
const SizedBox(height: 16),
Text('Events', style: Theme.of(context).textTheme.titleMedium),
for (final line in _log.take(20)) Text(line),
],
),
);
}
}