cometchat_calls_sdk 5.1.0
cometchat_calls_sdk: ^5.1.0 copied to clipboard
Integrate voice & video calling into your Flutter app with CometChat's Calls SDK. Create real-time communication for better user engagement.
example/lib/main.dart
// A minimal CometChat Calls app: initialise the SDK, log a user in and join a
// call session. Open the same session ID on a second device (or in the web
// sample) as another user to talk to each other.
//
// Credentials come from your CometChat dashboard. Pass them at build time,
// flutter run --dart-define=APP_ID=... --dart-define=REGION=us --dart-define=AUTH_KEY=...
// or type them into the first screen. The Auth Key is for development only;
// production apps log in with an auth token minted by their server.
import 'dart:async';
import 'package:cometchat_calls_sdk/cometchat_calls_sdk.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
void main() => runApp(const CallsExampleApp());
class CallsExampleApp extends StatelessWidget {
const CallsExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'CometChat Calls',
theme: ThemeData(colorSchemeSeed: const Color(0xFF6852D6)),
home: const JoinScreen(),
);
}
}
class JoinScreen extends StatefulWidget {
const JoinScreen({super.key});
@override
State<JoinScreen> createState() => _JoinScreenState();
}
class _JoinScreenState extends State<JoinScreen> {
final _appId = TextEditingController(
text: const String.fromEnvironment('APP_ID'),
);
final _region = TextEditingController(
text: const String.fromEnvironment('REGION', defaultValue: 'us'),
);
final _authKey = TextEditingController(
text: const String.fromEnvironment('AUTH_KEY'),
);
final _uid = TextEditingController(text: 'cometchat-uid-1');
final _sessionId = TextEditingController(text: 'example-session');
bool _video = true;
bool _busy = false;
String? _error;
Future<void> _join() async {
setState(() {
_busy = true;
_error = null;
});
try {
if (defaultTargetPlatform == TargetPlatform.android) {
// iOS asks for the camera and microphone itself on first use.
await [Permission.microphone, if (_video) Permission.camera].request();
}
await _initAndLogin();
final settings =
(SessionSettingsBuilder()
..setTitle(_sessionId.text.trim())
..setType(_video ? SessionType.video : SessionType.audio))
.build();
await CometChatCalls.joinSession(
sessionId: _sessionId.text.trim(),
sessionSettings: settings,
onSuccess: (callView) {
if (!mounted) return;
if (callView == null) {
_fail('Joined, but the SDK returned no call view');
return;
}
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => CallScreen(callView: callView)),
);
setState(() => _busy = false);
},
onError: (e) => _fail(e.message),
);
} catch (e) {
_fail('$e');
}
}
/// Initialises the SDK and logs the user in, unless that user already is.
Future<void> _initAndLogin() async {
final appSettings =
(CallAppSettingBuilder()
..appId = _appId.text.trim()
..region = _region.text.trim().toLowerCase())
.build();
await _await<String>(
(ok, err) =>
CometChatCalls.init(appSettings, onSuccess: ok, onError: err),
);
final uid = _uid.text.trim();
final current = await CometChatCalls.getLoggedInUser();
if (current?.uid == uid) return;
if (current != null) {
await _await<String>(
(ok, err) => CometChatCalls.logout(onSuccess: ok, onError: err),
);
}
await _await<User?>(
(ok, err) => CometChatCalls.login(
uid: uid,
authKey: _authKey.text.trim(),
onSuccess: ok,
onError: err,
),
);
}
/// Turns a callback-style SDK call into a Future.
Future<T> _await<T>(
void Function(
void Function(T) ok,
void Function(CometChatCallsException) err,
)
call,
) {
final done = Completer<T>();
call(done.complete, (e) => done.completeError(e.message ?? e.code));
return done.future;
}
void _fail(String? message) {
if (!mounted) return;
setState(() {
_busy = false;
_error = message ?? 'Unknown error';
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('CometChat Calls')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
TextField(
controller: _appId,
decoration: const InputDecoration(labelText: 'App ID'),
),
TextField(
controller: _region,
decoration: const InputDecoration(
labelText: 'Region (us, eu or in)',
),
),
TextField(
controller: _authKey,
decoration: const InputDecoration(labelText: 'Auth Key'),
obscureText: true,
),
TextField(
controller: _uid,
decoration: const InputDecoration(labelText: 'User ID'),
),
TextField(
controller: _sessionId,
decoration: const InputDecoration(labelText: 'Session ID'),
),
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: const Text('Video'),
value: _video,
onChanged: (value) => setState(() => _video = value),
),
const SizedBox(height: 8),
FilledButton(
onPressed: _busy ? null : _join,
child: Text(_busy ? 'Joining…' : 'Join session'),
),
if (_error != null) ...[
const SizedBox(height: 12),
Text(
_error!,
style: TextStyle(color: Theme.of(context).colorScheme.error),
),
],
],
),
);
}
@override
void dispose() {
for (final controller in [_appId, _region, _authKey, _uid, _sessionId]) {
controller.dispose();
}
super.dispose();
}
}
/// Shows the call. The view holds the running call, so it stays mounted until
/// the user leaves with the end-call button.
class CallScreen extends StatefulWidget {
const CallScreen({super.key, required this.callView});
final Widget callView;
@override
State<CallScreen> createState() => _CallScreenState();
}
class _CallScreenState extends State<CallScreen> {
late final _listener = _SessionLeftListener(() {
if (mounted) Navigator.of(context).pop();
});
@override
void initState() {
super.initState();
CallSession.getInstance()?.addSessionStatusListener(_listener);
}
@override
void dispose() {
CallSession.getInstance()?.removeSessionStatusListener(_listener);
super.dispose();
}
@override
Widget build(BuildContext context) {
return PopScope(
canPop: false,
child: Scaffold(
backgroundColor: Colors.black,
body: SafeArea(child: SizedBox.expand(child: widget.callView)),
),
);
}
}
class _SessionLeftListener extends SessionStatusListeners {
_SessionLeftListener(this.onLeft);
final VoidCallback onLeft;
@override
void onSessionLeft() => onLeft();
}