flutter_chat_io 2.2.0
flutter_chat_io: ^2.2.0 copied to clipboard
Live customer support for Flutter apps with Slack integration. Users chat in-app, your team responds in Slack.
example/lib/main.dart
// ignore_for_file: public_member_api_docs
import 'package:flutter/material.dart';
import 'package:flutter_chat_io/flutter_chat_io.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
/// Example application demonstrating FlutterChatIO integration.
///
/// This example shows:
/// - SDK initialization
/// - User identification
/// - Theme customization
/// - Navigation to chat widget
void main() {
WidgetsFlutterBinding.ensureInitialized();
// Initialize FlutterChatIO with your credentials
// Get your client ID and token from https://flutterchat.io/dashboard
FlutterChatIO.initialize(
clientId: 'your-client-id',
clientToken: 'your-client-token',
);
runApp(const FlutterChatIOExampleApp());
}
/// Root application widget.
class FlutterChatIOExampleApp extends StatelessWidget {
const FlutterChatIOExampleApp({super.key});
@override
Widget build(BuildContext context) => MaterialApp(
title: 'FlutterChatIO Example',
// Add localization support for RTL languages (Arabic, Hebrew, etc.)
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('en'), // English
Locale('zh'), // Chinese
Locale('es'), // Spanish
Locale('ar'), // Arabic (RTL)
Locale('hi'), // Hindi
Locale('ru'), // Russian
],
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
useMaterial3: true,
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.indigo,
brightness: Brightness.dark,
),
useMaterial3: true,
),
home: const HomePage(),
);
}
/// Home page with options to open different themed chats.
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// User Info controllers
final _userIdController = TextEditingController(text: 'demo-user-123');
final _emailController = TextEditingController(text: 'demo@example.com');
final _nameController = TextEditingController(text: 'Demo User');
// Custom User Data controllers (key-value pairs)
final _subscriptionController = TextEditingController(text: 'premium');
final _accountTypeController = TextEditingController(text: 'business');
final _signupDateController = TextEditingController(text: '2024-01-15');
@override
void dispose() {
_userIdController.dispose();
_emailController.dispose();
_nameController.dispose();
_subscriptionController.dispose();
_accountTypeController.dispose();
_signupDateController.dispose();
super.dispose();
}
/// Builds custom user data map from controllers, excluding empty values.
Map<String, String> _buildUserData() {
final data = <String, String>{};
if (_subscriptionController.text.isNotEmpty) {
data['subscription'] = _subscriptionController.text;
}
if (_accountTypeController.text.isNotEmpty) {
data['accountType'] = _accountTypeController.text;
}
if (_signupDateController.text.isNotEmpty) {
data['signupDate'] = _signupDateController.text;
}
return data;
}
void _updateUserInfo() {
FlutterChatIO.setUserId(userId: _userIdController.text);
// Set user info and custom data in a single call
// Note: Device info is automatically detected by the SDK
final customData = _buildUserData();
FlutterChatIO.setUserInfo(
email: _emailController.text,
userName: _nameController.text,
customData: customData.isNotEmpty ? customData : null,
);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('User info updated'),
behavior: SnackBarBehavior.floating,
),
);
}
void _openChat(FlutterChatIOTheme theme, [Locale? locale]) {
// Ensure user info is set before opening chat
FlutterChatIO.setUserId(userId: _userIdController.text);
// Set user info and custom data in a single call
// Note: Device info is automatically detected by the SDK
final customData = _buildUserData();
FlutterChatIO.setUserInfo(
email: _emailController.text,
userName: _nameController.text,
customData: customData.isNotEmpty ? customData : null,
);
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (_) {
final chat = FlutterChatIOChat(theme: theme);
// Wrap with locale override for RTL support
if (locale != null) {
return Localizations.override(
context: context,
locale: locale,
child: Directionality(
textDirection: _isRtlLocale(locale)
? TextDirection.rtl
: TextDirection.ltr,
child: chat,
),
);
}
return chat;
},
),
);
}
/// Returns true if the locale uses Right-to-Left text direction.
bool _isRtlLocale(Locale locale) {
const rtlLanguages = ['ar', 'he', 'fa', 'ur'];
return rtlLanguages.contains(locale.languageCode);
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('FlutterChatIO Example'),
centerTitle: true,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// User Configuration Section
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'User Configuration',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 16),
TextField(
controller: _userIdController,
decoration: const InputDecoration(
labelText: 'User ID',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person),
),
),
const SizedBox(height: 12),
TextField(
controller: _emailController,
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.email),
),
),
const SizedBox(height: 12),
TextField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Display Name',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.badge),
),
),
const SizedBox(height: 20),
// Custom User Data Section
Row(
children: [
Text(
'Custom User Data',
style: Theme.of(context).textTheme.titleSmall,
),
const SizedBox(width: 8),
Tooltip(
message:
'Key-value pairs sent to your support team',
child: Icon(
Icons.info_outline,
size: 16,
color: Theme.of(context).colorScheme.outline,
),
),
],
),
const SizedBox(height: 12),
TextField(
controller: _subscriptionController,
decoration: const InputDecoration(
labelText: 'Subscription',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.card_membership),
hintText: 'e.g., premium, free, enterprise',
),
),
const SizedBox(height: 12),
TextField(
controller: _accountTypeController,
decoration: const InputDecoration(
labelText: 'Account Type',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.business),
hintText: 'e.g., business, personal',
),
),
const SizedBox(height: 12),
TextField(
controller: _signupDateController,
decoration: const InputDecoration(
labelText: 'Signup Date',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.calendar_today),
hintText: 'e.g., 2024-01-15',
),
),
const SizedBox(height: 16),
FilledButton.icon(
onPressed: _updateUserInfo,
icon: const Icon(Icons.save),
label: const Text('Save User Info'),
),
],
),
),
),
const SizedBox(height: 24),
// Theme Examples Section
Text(
'Theme Examples',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 12),
// Default Theme
_ThemeCard(
title: 'Default Theme',
subtitle: 'Material Blue styling',
color: const Color(0xFF1976D2),
onTap: () => _openChat(const FlutterChatIOTheme()),
),
// Indigo Theme
_ThemeCard(
title: 'Indigo Theme',
subtitle: 'Purple/indigo accent colors',
color: Colors.indigo,
onTap: () => _openChat(
FlutterChatIOTheme(
appBarColor: Colors.indigo,
appBarTitle: 'Help Center',
outgoingMessageColor: Colors.indigo,
sendButtonColor: Colors.indigo,
emptyStateTitle: 'How can we help?',
emptyStateSubtitle: 'Our team is here to assist you',
),
),
),
// Teal Theme
_ThemeCard(
title: 'Teal Theme',
subtitle: 'Fresh teal accent colors',
color: Colors.teal,
onTap: () => _openChat(
FlutterChatIOTheme(
appBarColor: Colors.teal,
appBarTitle: 'Support Chat',
outgoingMessageColor: Colors.teal,
sendButtonColor: Colors.teal,
onlineColor: Colors.teal,
emptyStateTitle: 'Welcome!',
emptyStateSubtitle: 'Start a conversation with us',
),
),
),
// Dark Theme
_ThemeCard(
title: 'Dark Theme',
subtitle: 'Dark mode styling',
color: const Color(0xFF2D2D2D),
onTap: () => _openChat(
FlutterChatIOTheme(
// AppBar
appBarColor: const Color(0xFF1E1E1E),
appBarTitle: 'Night Support',
// Background
backgroundColor: const Color(0xFF121212),
// Messages
incomingMessageColor: const Color(0xFF2D2D2D),
outgoingMessageColor: Colors.indigo,
incomingMessageTextStyle: const TextStyle(
fontSize: 15,
color: Colors.white,
),
incomingTimeTextStyle: const TextStyle(
fontSize: 11,
color: Colors.white54,
),
// Input
inputBackgroundColor: const Color(0xFF2D2D2D),
inputTextColor: Colors.white,
inputHintColor: Colors.white54,
keyboardAppearance: Brightness.dark,
sendButtonColor: Colors.indigo,
// Empty state
emptyStateTitle: 'Night Owl Support',
emptyStateSubtitle: 'We are here 24/7',
emptyStateTitleStyle: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: Colors.white,
),
emptyStateSubtitleStyle: const TextStyle(
fontSize: 14,
color: Colors.white70,
),
),
),
),
const SizedBox(height: 24),
// Auto-Localization Section
Text(
'Auto-Localized Themes',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
Text(
'Uses FlutterChatIOTheme.localized() for automatic translation',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 12),
// Device Locale (Auto-detect)
_ThemeCard(
title: 'Device Locale',
subtitle: 'Auto-detect from device settings',
color: Colors.deepPurple,
onTap: () => _openChat(
FlutterChatIOTheme.localized(
Localizations.localeOf(context),
appBarColor: Colors.deepPurple,
outgoingMessageColor: Colors.deepPurple,
sendButtonColor: Colors.deepPurple,
),
),
),
// Chinese
_ThemeCard(
title: 'Chinese',
subtitle: '中文本地化',
color: Colors.red,
onTap: () => _openChat(
FlutterChatIOTheme.localized(
const Locale('zh'),
appBarColor: Colors.red,
outgoingMessageColor: Colors.red,
sendButtonColor: Colors.red,
),
),
),
// Spanish
_ThemeCard(
title: 'Spanish',
subtitle: 'Localización en español',
color: Colors.orange,
onTap: () => _openChat(
FlutterChatIOTheme.localized(
const Locale('es'),
appBarColor: Colors.orange,
outgoingMessageColor: Colors.orange,
sendButtonColor: Colors.orange,
),
),
),
// Arabic (RTL)
_ThemeCard(
title: 'Arabic (RTL)',
subtitle: 'التوطين العربي',
color: Colors.green,
onTap: () => _openChat(
FlutterChatIOTheme.localized(
const Locale('ar'),
appBarColor: Colors.green,
outgoingMessageColor: Colors.green,
sendButtonColor: Colors.green,
),
const Locale('ar'), // Enable RTL
),
),
// Hindi
_ThemeCard(
title: 'Hindi',
subtitle: 'हिंदी स्थानीयकरण',
color: Colors.amber.shade800,
onTap: () => _openChat(
FlutterChatIOTheme.localized(
const Locale('hi'),
appBarColor: Colors.amber.shade800,
outgoingMessageColor: Colors.amber.shade800,
sendButtonColor: Colors.amber.shade800,
),
),
),
const SizedBox(height: 24),
// SDK Info
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'SDK Information',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 12),
_InfoRow(
label: 'Initialized',
value: FlutterChatIO.isInitialized ? 'Yes' : 'No',
),
_InfoRow(
label: 'WebSocket Endpoint',
value: FlutterChatIO.apiWss,
),
_InfoRow(
label: 'HTTP Endpoint',
value: FlutterChatIO.apiHttpHistory,
),
],
),
),
),
],
),
),
);
}
/// A card displaying a theme option.
class _ThemeCard extends StatelessWidget {
const _ThemeCard({
required this.title,
required this.subtitle,
required this.color,
required this.onTap,
});
final String title;
final String subtitle;
final Color color;
final VoidCallback onTap;
@override
Widget build(BuildContext context) => Card(
margin: const EdgeInsets.only(bottom: 8),
child: ListTile(
leading: Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(8),
),
child: const Icon(Icons.chat, color: Colors.white),
),
title: Text(title),
subtitle: Text(subtitle),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
onTap: onTap,
),
);
}
/// A row displaying label-value information.
class _InfoRow extends StatelessWidget {
const _InfoRow({
required this.label,
required this.value,
});
final String label;
final String value;
@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 140,
child: Text(
label,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
),
Expanded(
child: Text(
value,
style: Theme.of(context).textTheme.bodyMedium,
),
),
],
),
);
}