ex_im_flutter_call 0.0.8
ex_im_flutter_call: ^0.0.8 copied to clipboard
A Flutter plugin for audio/video calling - provides WebRTC-based 1v1 and group calling functionality for ex_im_flutter_chat.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:ex_im_flutter_call/ex_im_flutter_call.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
CallState _callState = const CallState();
MediaState _mediaState = const MediaState();
StreamSubscription<CallState>? _callStateSub;
StreamSubscription<MediaState>? _mediaStateSub;
bool _isInitialized = false;
String _statusMessage = '未初始化';
@override
void initState() {
super.initState();
_checkInitStatus();
}
void _checkInitStatus() {
// 检查是否已登录 Chat SDK
// 实际使用中需要先初始化 ExImChat 并登录
setState(() {
_statusMessage = '请先初始化 ExImChat 并登录';
});
}
void _initCallSdk() {
try {
ExImCall.instance.init();
// 监听通话状态
_callStateSub = ExImCall.instance.callStateStream.listen((state) {
setState(() {
_callState = state;
});
});
// 监听媒体状态
_mediaStateSub = ExImCall.instance.mediaStateStream.listen((state) {
setState(() {
_mediaState = state;
});
});
setState(() {
_isInitialized = true;
_statusMessage = 'Call SDK 已初始化';
});
} catch (e) {
setState(() {
_statusMessage = '初始化失败: $e';
});
}
}
Future<void> _makeCall() async {
if (!_isInitialized) {
_showSnackBar('请先初始化 SDK');
return;
}
try {
// 简化的 API:只需传用户 ID 和通话类型
// 用户信息由插件内部自动获取
await ExImCall.instance.call(
targetUserId: 'test_user_id',
callType: CallTypeEnum.video,
);
} catch (e) {
_showSnackBar('发起通话失败: $e');
}
}
Future<void> _hangup() async {
try {
await ExImCall.instance.hangup();
} catch (e) {
_showSnackBar('挂断失败: $e');
}
}
void _showSnackBar(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
@override
void dispose() {
_callStateSub?.cancel();
_mediaStateSub?.cancel();
if (_isInitialized) {
ExImCall.instance.dispose();
}
super.dispose();
}
String _getModeText(RtcServiceMode mode) {
switch (mode) {
case RtcServiceMode.idle:
return '空闲';
case RtcServiceMode.dial:
return '拨号中';
case RtcServiceMode.invite:
return '来电中';
case RtcServiceMode.talking:
return '通话中';
case RtcServiceMode.connecting:
return '连接中';
case RtcServiceMode.reconnecting:
return '重连中';
case RtcServiceMode.ended:
return '已结束';
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('ExIm Call Example'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 状态信息
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('状态: $_statusMessage'),
const SizedBox(height: 8),
Text('通话模式: ${_getModeText(_callState.mode)}'),
if (_callState.roomId.isNotEmpty)
Text('房间ID: ${_callState.roomId}'),
if (_callState.remoteNickname.isNotEmpty)
Text('对方: ${_callState.remoteNickname}'),
if (_callState.mode == RtcServiceMode.talking)
Text('通话时长: ${_callState.callDuration}秒'),
],
),
),
),
const SizedBox(height: 16),
// 媒体状态
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('媒体状态', style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Text('视频: ${_mediaState.isVideoEnabled ? "开启" : "关闭"}'),
Text('音频: ${_mediaState.isAudioEnabled ? "开启" : "关闭"}'),
Text('扬声器: ${_mediaState.isSpeakerOn ? "开启" : "关闭"}'),
Text('摄像头: ${_mediaState.isFrontCamera ? "前置" : "后置"}'),
],
),
),
),
const SizedBox(height: 24),
// 操作按钮
if (!_isInitialized)
ElevatedButton(
onPressed: _initCallSdk,
child: const Text('初始化 Call SDK'),
),
if (_isInitialized) ...[
Row(
children: [
ElevatedButton(
onPressed: _callState.mode == RtcServiceMode.idle ? _makeCall : null,
child: const Text('发起视频通话'),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: _callState.mode != RtcServiceMode.idle ? _hangup : null,
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
child: const Text('挂断', style: TextStyle(color: Colors.white)),
),
],
),
const SizedBox(height: 16),
// 媒体控制按钮
if (_callState.mode == RtcServiceMode.talking)
Row(
children: [
IconButton(
onPressed: () => ExImCall.instance.toggleVideo(),
icon: Icon(_mediaState.isVideoEnabled
? Icons.videocam
: Icons.videocam_off),
),
IconButton(
onPressed: () => ExImCall.instance.toggleAudio(),
icon: Icon(_mediaState.isAudioEnabled
? Icons.mic
: Icons.mic_off),
),
IconButton(
onPressed: () => ExImCall.instance.toggleSpeaker(),
icon: Icon(_mediaState.isSpeakerOn
? Icons.volume_up
: Icons.volume_off),
),
IconButton(
onPressed: () => ExImCall.instance.switchCamera(),
icon: const Icon(Icons.cameraswitch),
),
],
),
],
],
),
),
),
);
}
}