This guide shows you how to add Heyo to your Flutter application for iOS and Android.
Add webview_flutter to your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
webview_flutter: ^4.4.0
Then run:
flutter pub get
Create lib/widgets/heyo_chat.dart:
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class HeyoChat extends StatefulWidget {
final String projectId;
final Map<String, dynamic>? user;
const HeyoChat({
Key? key,
required this.projectId,
this.user,
}) : super(key: key);
@override
State<HeyoChat> createState() => _HeyoChatState();
}
class _HeyoChatState extends State<HeyoChat> {
late final WebViewController controller;
@override
void initState() {
super.initState();
final html = '''
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<style>
body { margin: 0; padding: 0; }
#heyo-container { width: 100vw; height: 100vh; }
</style>
</head>
<body>
<div id="heyo-container"></div>
<script src="https://heyo.so/embed/script" defer data-project-id="${widget.projectId}"></script>
<script>
window.addEventListener('load', function() {
if (window.HEYO) {
${widget.user != null ? 'window.HEYO.init({ projectId: "${widget.projectId}", user: ${_encodeUser()} });' : ''}
window.HEYO.show();
}
});
</script>
</body>
</html>
''';
controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..loadRequest(Uri.dataFromString(html, mimeType: 'text/html'));
}
String _encodeUser() {
if (widget.user == null) return '{}';
return widget.user.toString().replaceAll("'", '"');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Support Chat'),
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.of(context).pop(),
),
),
body: WebViewWidget(controller: controller),
);
}
}
import 'package:flutter/material.dart';
import 'widgets/heyo_chat.dart';
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('My App'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => HeyoChat(
projectId: 'YOUR_PROJECT_ID',
),
),
);
},
child: const Text('Open Support Chat'),
),
),
);
}
}
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => HeyoChat(
projectId: 'YOUR_PROJECT_ID',
user: {
'name': 'John Doe',
'email': '[email protected]',
},
),
),
);
Create a reusable FAB for chat:
class ChatFAB extends StatelessWidget {
final String projectId;
final Map<String, dynamic>? user;
const ChatFAB({
Key? key,
required this.projectId,
this.user,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => HeyoChat(
projectId: projectId,
user: user,
),
),
);
},
backgroundColor: const Color(0xFF6366F1),
child: const Icon(Icons.chat_bubble_outline),
);
}
}
Usage:
Scaffold(
body: // Your content
floatingActionButton: ChatFAB(
projectId: 'YOUR_PROJECT_ID',
user: {
'name': currentUser.name,
'email': currentUser.email,
},
),
);
Show chat as a bottom sheet:
void openChatSheet(BuildContext context) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => DraggableScrollableSheet(
initialChildSize: 0.9,
minChildSize: 0.5,
maxChildSize: 0.95,
expand: false,
builder: (context, scrollController) {
return HeyoChat(projectId: 'YOUR_PROJECT_ID');
},
),
);
}
Customize the widget to match your Material theme:
class ThemedHeyoChat extends StatelessWidget {
final String projectId;
const ThemedHeyoChat({Key? key, required this.projectId}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
backgroundColor: theme.scaffoldBackgroundColor,
appBar: AppBar(
title: Text('Support', style: theme.textTheme.titleLarge),
backgroundColor: theme.primaryColor,
),
body: HeyoChat(projectId: projectId),
);
}
}
class _HeyoChatState extends State<HeyoChat> {
late final WebViewController controller;
bool isLoading = true;
@override
void initState() {
super.initState();
controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
NavigationDelegate(
onPageFinished: (url) {
setState(() {
isLoading = false;
});
},
),
)
..loadRequest(Uri.dataFromString(html, mimeType: 'text/html'));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Support Chat')),
body: Stack(
children: [
WebViewWidget(controller: controller),
if (isLoading)
const Center(
child: CircularProgressIndicator(),
),
],
),
);
}
}
In ios/Runner/Info.plist, add:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
In android/app/src/main/AndroidManifest.xml, ensure internet permission:
<uses-permission android:name="android.permission.INTERNET" />
setJavaScriptMode(JavaScriptMode.unrestricted) is setwebview_flutter to latest versionFor Flutter web, use the standard JavaScript integration instead:
import 'dart:html' as html;
void initHeyoForWeb() {
final script = html.ScriptElement()
..src = 'https://heyo.so/embed/script'
..defer = true
..setAttribute('data-project-id', 'YOUR_PROJECT_ID');
html.document.head?.append(script);
}