telnyx-webrtc-client-flutter
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTelnyx WebRTC - Flutter SDK
Telnyx WebRTC - Flutter SDK
Build real-time voice communication into Flutter applications (Android, iOS, Web).
Prerequisites: Create WebRTC credentials and generate a login token using the Telnyx server-side SDK. See theskill in your server language plugin (e.g.,telnyx-webrtc-*,telnyx-python).telnyx-javascript
将实时语音通信能力集成到Flutter应用(Android、iOS、Web)中。
前提条件: 创建WebRTC凭证并使用Telnyx服务端SDK生成登录令牌。请参考对应服务端语言插件中的skill(例如telnyx-webrtc-*、telnyx-python)。telnyx-javascript
Quick Start Option
快速启动选项
For faster implementation, consider Telnyx Common - a higher-level abstraction that simplifies WebRTC integration with minimal setup.
如需更快实现,可以考虑使用Telnyx Common——这是一个更高层级的抽象层,可通过最少的配置简化WebRTC集成。
Installation
安装
Add to :
pubspec.yamlyaml
dependencies:
telnyx_webrtc: ^latest_versionThen run:
bash
flutter pub get添加到:
pubspec.yamlyaml
dependencies:
telnyx_webrtc: ^latest_version然后运行:
bash
flutter pub getPlatform Configuration
平台配置
Android
Android
Add to :
AndroidManifest.xmlxml
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />添加到:
AndroidManifest.xmlxml
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />iOS
iOS
Add to :
Info.plistxml
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) needs microphone access for calls</string>添加到:
Info.plistxml
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) needs microphone access for calls</string>Authentication
身份验证
Option 1: Credential-Based Login
选项1: 基于凭证的登录
dart
final telnyxClient = TelnyxClient();
final credentialConfig = CredentialConfig(
sipUser: 'your_sip_username',
sipPassword: 'your_sip_password',
sipCallerIDName: 'Display Name',
sipCallerIDNumber: '+15551234567',
notificationToken: fcmOrApnsToken, // Optional: for push
autoReconnect: true,
debug: true,
logLevel: LogLevel.debug,
);
telnyxClient.connectWithCredential(credentialConfig);dart
final telnyxClient = TelnyxClient();
final credentialConfig = CredentialConfig(
sipUser: 'your_sip_username',
sipPassword: 'your_sip_password',
sipCallerIDName: 'Display Name',
sipCallerIDNumber: '+15551234567',
notificationToken: fcmOrApnsToken, // Optional: for push
autoReconnect: true,
debug: true,
logLevel: LogLevel.debug,
);
telnyxClient.connectWithCredential(credentialConfig);Option 2: Token-Based Login (JWT)
选项2: 基于Token的登录(JWT)
dart
final tokenConfig = TokenConfig(
sipToken: 'your_jwt_token',
sipCallerIDName: 'Display Name',
sipCallerIDNumber: '+15551234567',
notificationToken: fcmOrApnsToken,
autoReconnect: true,
debug: true,
);
telnyxClient.connectWithToken(tokenConfig);dart
final tokenConfig = TokenConfig(
sipToken: 'your_jwt_token',
sipCallerIDName: 'Display Name',
sipCallerIDNumber: '+15551234567',
notificationToken: fcmOrApnsToken,
autoReconnect: true,
debug: true,
);
telnyxClient.connectWithToken(tokenConfig);Configuration Options
配置选项
| Parameter | Type | Description |
|---|---|---|
| String | Credentials from Telnyx Portal |
| String | Caller ID name displayed to recipients |
| String | Caller ID number |
| String? | FCM (Android) or APNS (iOS) token |
| bool | Auto-retry login on failure |
| bool | Enable call quality metrics |
| LogLevel | none, error, warning, debug, info, all |
| String? | Custom ringtone asset path |
| String? | Custom ringback tone asset path |
| 参数 | 类型 | 描述 |
|---|---|---|
| String | 从Telnyx门户获取的凭证 |
| String | 展示给接收方的来电显示名称 |
| String | 来电显示号码 |
| String? | FCM (Android) 或 APNS (iOS) 令牌 |
| bool | 登录失败时自动重试 |
| bool | 启用通话质量指标统计 |
| LogLevel | 可选值: none, error, warning, debug, info, all |
| String? | 自定义铃声资源路径 |
| String? | 自定义回铃音资源路径 |
Making Outbound Calls
拨打外呼
dart
telnyxClient.call.newInvite(
'John Doe', // callerName
'+15551234567', // callerNumber
'+15559876543', // destinationNumber
'my-custom-state', // clientState
);dart
telnyxClient.call.newInvite(
'John Doe', // callerName
'+15551234567', // callerNumber
'+15559876543', // destinationNumber
'my-custom-state', // clientState
);Receiving Inbound Calls
接听来电
Listen for socket events:
dart
InviteParams? _incomingInvite;
Call? _currentCall;
telnyxClient.onSocketMessageReceived = (TelnyxMessage message) {
switch (message.socketMethod) {
case SocketMethod.CLIENT_READY:
// Ready to make/receive calls
break;
case SocketMethod.LOGIN:
// Successfully logged in
break;
case SocketMethod.INVITE:
// Incoming call!
_incomingInvite = message.message.inviteParams;
// Show incoming call UI...
break;
case SocketMethod.ANSWER:
// Call was answered
break;
case SocketMethod.BYE:
// Call ended
break;
}
};
// Accept the incoming call
void acceptCall() {
if (_incomingInvite != null) {
_currentCall = telnyxClient.acceptCall(
_incomingInvite!,
'My Name',
'+15551234567',
'state',
);
}
}监听Socket事件:
dart
InviteParams? _incomingInvite;
Call? _currentCall;
telnyxClient.onSocketMessageReceived = (TelnyxMessage message) {
switch (message.socketMethod) {
case SocketMethod.CLIENT_READY:
// 已准备好拨打/接听通话
break;
case SocketMethod.LOGIN:
// 登录成功
break;
case SocketMethod.INVITE:
// 有来电!
_incomingInvite = message.message.inviteParams;
// 展示来电界面...
break;
case SocketMethod.ANSWER:
// 通话已被接听
break;
case SocketMethod.BYE:
// 通话已结束
break;
}
};
// 接听来电
void acceptCall() {
if (_incomingInvite != null) {
_currentCall = telnyxClient.acceptCall(
_incomingInvite!,
'My Name',
'+15551234567',
'state',
);
}
}Call Controls
通话控制
dart
// End call
telnyxClient.call.endCall(telnyxClient.call.callId);
// Decline incoming call
telnyxClient.createCall().endCall(_incomingInvite?.callID);
// Mute/Unmute
telnyxClient.call.onMuteUnmutePressed();
// Hold/Unhold
telnyxClient.call.onHoldUnholdPressed();
// Toggle speaker
telnyxClient.call.enableSpeakerPhone(true);
// Send DTMF tone
telnyxClient.call.dtmf(telnyxClient.call.callId, '1');dart
// 结束通话
telnyxClient.call.endCall(telnyxClient.call.callId);
// 拒接来电
telnyxClient.createCall().endCall(_incomingInvite?.callID);
// 静音/取消静音
telnyxClient.call.onMuteUnmutePressed();
// 保持/取消保持
telnyxClient.call.onHoldUnholdPressed();
// 切换扬声器
telnyxClient.call.enableSpeakerPhone(true);
// 发送DTMF音
telnyxClient.call.dtmf(telnyxClient.call.callId, '1');Push Notifications - Android (FCM)
推送通知 - Android (FCM)
1. Setup Firebase
1. 配置Firebase
dart
// main.dart
('vm:entry-point')
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
if (defaultTargetPlatform == TargetPlatform.android) {
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseBackgroundHandler);
}
runApp(const MyApp());
}dart
// main.dart
('vm:entry-point')
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
if (defaultTargetPlatform == TargetPlatform.android) {
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseBackgroundHandler);
}
runApp(const MyApp());
}2. Background Handler
2. 后台处理器
dart
Future<void> _firebaseBackgroundHandler(RemoteMessage message) async {
// Show notification (e.g., using flutter_callkit_incoming)
showIncomingCallNotification(message);
// Listen for user action
FlutterCallkitIncoming.onEvent.listen((CallEvent? event) {
switch (event!.event) {
case Event.actionCallAccept:
TelnyxClient.setPushMetaData(
message.data,
isAnswer: true,
isDecline: false,
);
break;
case Event.actionCallDecline:
TelnyxClient.setPushMetaData(
message.data,
isAnswer: false,
isDecline: true, // SDK handles decline automatically
);
break;
}
});
}dart
Future<void> _firebaseBackgroundHandler(RemoteMessage message) async {
// 展示通知(例如使用flutter_callkit_incoming)
showIncomingCallNotification(message);
// 监听用户操作
FlutterCallkitIncoming.onEvent.listen((CallEvent? event) {
switch (event!.event) {
case Event.actionCallAccept:
TelnyxClient.setPushMetaData(
message.data,
isAnswer: true,
isDecline: false,
);
break;
case Event.actionCallDecline:
TelnyxClient.setPushMetaData(
message.data,
isAnswer: false,
isDecline: true, // SDK自动处理拒接逻辑
);
break;
}
});
}3. Handle Push When App Opens
3. 应用打开时处理推送
dart
Future<void> _handlePushNotification() async {
final data = await TelnyxClient.getPushMetaData();
if (data != null) {
PushMetaData pushMetaData = PushMetaData.fromJson(data);
telnyxClient.handlePushNotification(
pushMetaData,
credentialConfig,
tokenConfig,
);
}
}dart
Future<void> _handlePushNotification() async {
final data = await TelnyxClient.getPushMetaData();
if (data != null) {
PushMetaData pushMetaData = PushMetaData.fromJson(data);
telnyxClient.handlePushNotification(
pushMetaData,
credentialConfig,
tokenConfig,
);
}
}Early Accept/Decline Handling
提前接听/拒接处理
dart
bool _waitingForInvite = false;
void acceptCall() {
if (_incomingInvite != null) {
_currentCall = telnyxClient.acceptCall(...);
} else {
// Set flag if invite hasn't arrived yet
_waitingForInvite = true;
}
}
// In socket message handler:
case SocketMethod.INVITE:
_incomingInvite = message.message.inviteParams;
if (_waitingForInvite) {
acceptCall(); // Accept now that invite arrived
_waitingForInvite = false;
}
break;dart
bool _waitingForInvite = false;
void acceptCall() {
if (_incomingInvite != null) {
_currentCall = telnyxClient.acceptCall(...);
} else {
// 如果邀请还未到达则设置标记
_waitingForInvite = true;
}
}
// 在Socket消息处理器中:
case SocketMethod.INVITE:
_incomingInvite = message.message.inviteParams;
if (_waitingForInvite) {
acceptCall(); // 邀请到达后自动接听
_waitingForInvite = false;
}
break;Push Notifications - iOS (APNS + PushKit)
推送通知 - iOS (APNS + PushKit)
1. AppDelegate Setup
1. AppDelegate配置
swift
// AppDelegate.swift
func pushRegistry(_ registry: PKPushRegistry,
didUpdate credentials: PKPushCredentials,
for type: PKPushType) {
let deviceToken = credentials.token.map {
String(format: "%02x", $0)
}.joined()
SwiftFlutterCallkitIncomingPlugin.sharedInstance?
.setDevicePushTokenVoIP(deviceToken)
}
func pushRegistry(_ registry: PKPushRegistry,
didReceiveIncomingPushWith payload: PKPushPayload,
for type: PKPushType,
completion: @escaping () -> Void) {
guard type == .voIP else { return }
if let metadata = payload.dictionaryPayload["metadata"] as? [String: Any] {
let callerName = (metadata["caller_name"] as? String) ?? ""
let callerNumber = (metadata["caller_number"] as? String) ?? ""
let callId = (metadata["call_id"] as? String) ?? UUID().uuidString
let data = flutter_callkit_incoming.Data(
id: callId,
nameCaller: callerName,
handle: callerNumber,
type: 0
)
data.extra = payload.dictionaryPayload as NSDictionary
SwiftFlutterCallkitIncomingPlugin.sharedInstance?
.showCallkitIncoming(data, fromPushKit: true)
}
}swift
// AppDelegate.swift
func pushRegistry(_ registry: PKPushRegistry,
didUpdate credentials: PKPushCredentials,
for type: PKPushType) {
let deviceToken = credentials.token.map {
String(format: "%02x", $0)
}.joined()
SwiftFlutterCallkitIncomingPlugin.sharedInstance?
.setDevicePushTokenVoIP(deviceToken)
}
func pushRegistry(_ registry: PKPushRegistry,
didReceiveIncomingPushWith payload: PKPushPayload,
for type: PKPushType,
completion: @escaping () -> Void) {
guard type == .voIP else { return }
if let metadata = payload.dictionaryPayload["metadata"] as? [String: Any] {
let callerName = (metadata["caller_name"] as? String) ?? ""
let callerNumber = (metadata["caller_number"] as? String) ?? ""
let callId = (metadata["call_id"] as? String) ?? UUID().uuidString
let data = flutter_callkit_incoming.Data(
id: callId,
nameCaller: callerName,
handle: callerNumber,
type: 0
)
data.extra = payload.dictionaryPayload as NSDictionary
SwiftFlutterCallkitIncomingPlugin.sharedInstance?
.showCallkitIncoming(data, fromPushKit: true)
}
}2. Handle in Flutter
2. 在Flutter中处理
dart
FlutterCallkitIncoming.onEvent.listen((CallEvent? event) {
switch (event!.event) {
case Event.actionCallIncoming:
PushMetaData? pushMetaData = PushMetaData.fromJson(
event.body['extra']['metadata']
);
telnyxClient.handlePushNotification(
pushMetaData,
credentialConfig,
tokenConfig,
);
break;
case Event.actionCallAccept:
// Handle accept
break;
}
});dart
FlutterCallkitIncoming.onEvent.listen((CallEvent? event) {
switch (event!.event) {
case Event.actionCallIncoming:
PushMetaData? pushMetaData = PushMetaData.fromJson(
event.body['extra']['metadata']
);
telnyxClient.handlePushNotification(
pushMetaData,
credentialConfig,
tokenConfig,
);
break;
case Event.actionCallAccept:
// 处理接听逻辑
break;
}
});Handling Late Notifications
延迟通知处理
dart
const CALL_MISSED_TIMEOUT = 60; // seconds
void handlePushMessage(RemoteMessage message) {
DateTime now = DateTime.now();
Duration? diff = now.difference(message.sentTime!);
if (diff.inSeconds > CALL_MISSED_TIMEOUT) {
showMissedCallNotification(message);
return;
}
// Handle normal incoming call...
}dart
const CALL_MISSED_TIMEOUT = 60; // 单位: 秒
void handlePushMessage(RemoteMessage message) {
DateTime now = DateTime.now();
Duration? diff = now.difference(message.sentTime!);
if (diff.inSeconds > CALL_MISSED_TIMEOUT) {
showMissedCallNotification(message);
return;
}
// 处理正常来电...
}Call Quality Metrics
通话质量指标
Enable with in config:
debug: truedart
// When making a call
call.newInvite(
callerName: 'John',
callerNumber: '+15551234567',
destinationNumber: '+15559876543',
clientState: 'state',
debug: true,
);
// Listen for quality updates
call.onCallQualityChange = (CallQualityMetrics metrics) {
print('MOS: ${metrics.mos}');
print('Jitter: ${metrics.jitter * 1000} ms');
print('RTT: ${metrics.rtt * 1000} ms');
print('Quality: ${metrics.quality}'); // excellent, good, fair, poor, bad
};| Quality Level | MOS Range |
|---|---|
| excellent | > 4.2 |
| good | 4.1 - 4.2 |
| fair | 3.7 - 4.0 |
| poor | 3.1 - 3.6 |
| bad | ≤ 3.0 |
在配置中开启即可启用:
debug: truedart
// 拨打通话时
call.newInvite(
callerName: 'John',
callerNumber: '+15551234567',
destinationNumber: '+15559876543',
clientState: 'state',
debug: true,
);
// 监听质量更新
call.onCallQualityChange = (CallQualityMetrics metrics) {
print('MOS: ${metrics.mos}');
print('Jitter: ${metrics.jitter * 1000} ms');
print('RTT: ${metrics.rtt * 1000} ms');
print('Quality: ${metrics.quality}'); // 可选值: excellent, good, fair, poor, bad
};| 质量等级 | MOS值范围 |
|---|---|
| excellent | > 4.2 |
| good | 4.1 - 4.2 |
| fair | 3.7 - 4.0 |
| poor | 3.1 - 3.6 |
| bad | ≤ 3.0 |
AI Agent Integration
AI Agent集成
Connect to a Telnyx Voice AI Agent:
连接到Telnyx语音AI Agent:
1. Anonymous Login
1. 匿名登录
dart
try {
await telnyxClient.anonymousLogin(
targetId: 'your_ai_assistant_id',
targetType: 'ai_assistant', // Default
targetVersionId: 'optional_version_id', // Optional
);
} catch (e) {
print('Login failed: $e');
}dart
try {
await telnyxClient.anonymousLogin(
targetId: 'your_ai_assistant_id',
targetType: 'ai_assistant', // 默认值
targetVersionId: 'optional_version_id', // 可选
);
} catch (e) {
print('Login failed: $e');
}2. Start Conversation
2. 开启对话
dart
telnyxClient.newInvite(
'User Name',
'+15551234567',
'', // Destination ignored for AI Agent
'state',
customHeaders: {
'X-Account-Number': '123', // Maps to {{account_number}}
'X-User-Tier': 'premium', // Maps to {{user_tier}}
},
);dart
telnyxClient.newInvite(
'User Name',
'+15551234567',
'', // AI Agent场景下忽略目标号码
'state',
customHeaders: {
'X-Account-Number': '123', // 映射到 {{account_number}}
'X-User-Tier': 'premium', // 映射到 {{user_tier}}
},
);3. Receive Transcripts
3. 接收对话转录
dart
telnyxClient.onTranscriptUpdate = (List<TranscriptItem> transcript) {
for (var item in transcript) {
print('${item.role}: ${item.content}');
// role: 'user' or 'assistant'
// content: transcribed text
// timestamp: when received
}
};
// Get current transcript anytime
List<TranscriptItem> current = telnyxClient.transcript;
// Clear transcript
telnyxClient.clearTranscript();dart
telnyxClient.onTranscriptUpdate = (List<TranscriptItem> transcript) {
for (var item in transcript) {
print('${item.role}: ${item.content}');
// role: 'user' 或 'assistant'
// content: 转录的文本内容
// timestamp: 接收时间
}
};
// 随时获取当前转录内容
List<TranscriptItem> current = telnyxClient.transcript;
// 清空转录内容
telnyxClient.clearTranscript();4. Send Text to AI Agent
4. 向AI Agent发送文本
dart
Call? activeCall = telnyxClient.calls.values.firstOrNull;
if (activeCall != null) {
activeCall.sendConversationMessage(
'Hello, I need help with my account'
);
}dart
Call? activeCall = telnyxClient.calls.values.firstOrNull;
if (activeCall != null) {
activeCall.sendConversationMessage(
'Hello, I need help with my account'
);
}Custom Logging
自定义日志
dart
class MyCustomLogger extends CustomLogger {
log(LogLevel level, String message) {
print('[$level] $message');
// Send to analytics, file, server, etc.
}
}
final config = CredentialConfig(
// ... other config
logLevel: LogLevel.debug,
customLogger: MyCustomLogger(),
);dart
class MyCustomLogger extends CustomLogger {
log(LogLevel level, String message) {
print('[$level] $message');
// 可发送到分析平台、文件、服务器等
}
}
final config = CredentialConfig(
// ... 其他配置
logLevel: LogLevel.debug,
customLogger: MyCustomLogger(),
);Troubleshooting
故障排查
| Issue | Solution |
|---|---|
| No audio on Android | Check RECORD_AUDIO permission |
| No audio on iOS | Check NSMicrophoneUsageDescription in Info.plist |
| Push not working (debug) | Push only works in release mode |
| Login fails | Verify SIP credentials in Telnyx Portal |
| 10-second timeout | INVITE didn't arrive - check network/push setup |
| sender_id_mismatch | FCM project mismatch between app and server |
references/webrtc-server-api.md has the server-side WebRTC API — credential creation, token generation, and push notification setup. You MUST read it when setting up authentication or push notifications.
| 问题 | 解决方案 |
|---|---|
| Android端无音频 | 检查RECORD_AUDIO权限 |
| iOS端无音频 | 检查Info.plist中的NSMicrophoneUsageDescription配置 |
| 调试模式下推送不工作 | 推送仅在发布模式下生效 |
| 登录失败 | 验证Telnyx门户中的SIP凭证是否正确 |
| 10秒超时 | INVITE请求未到达 - 检查网络/推送配置 |
| sender_id_mismatch | 应用和服务端使用的FCM项目不匹配 |
references/webrtc-server-api.md包含服务端WebRTC API相关内容——凭证创建、令牌生成和推送通知配置。在配置身份验证或推送通知时必须阅读该文档。
API Reference
API参考
TxClient
TxClient
Telnyx Client
Telnyx客户端
TelnyxClient() is the core class of the SDK, and can be used to connect to our backend socket connection, create calls, check state and disconnect, etc.
dart
TelnyxClient _telnyxClient = TelnyxClient();TelnyxClient()是该SDK的核心类,可用于连接后端Socket连接、创建通话、检查状态和断开连接等。
dart
TelnyxClient _telnyxClient = TelnyxClient();Logging into Telnyx Client
登录Telnyx客户端
To log into the Telnyx WebRTC client, you'll need to authenticate using a Telnyx SIP Connection. Follow our quickstart guide to create JWTs (JSON Web Tokens) to authenticate. To log in with a token we use the connectWithToken() method. You can also authenticate directly with the SIP Connection and with the connectWithCredential() method:
usernamepassworddart
_telnyxClient.connectWithToken(tokenConfig)
//OR
_telnyxClient.connectWithCredential(credentialConfig) 要登录Telnyx WebRTC客户端,你需要使用Telnyx SIP连接进行身份验证。按照我们的快速入门指南创建JWT(JSON Web Tokens)完成身份验证。使用connectWithToken()方法可以通过token登录,你也可以使用connectWithCredential()方法通过SIP连接的和直接进行身份验证:
usernamepassworddart
_telnyxClient.connectWithToken(tokenConfig)
// 或
_telnyxClient.connectWithCredential(credentialConfig) Listening for events and reacting - Accepting a Call
监听事件并响应 - 接听通话
In order to be able to accept a call, we first need to listen for invitations. We do this by getting the Telnyx Socket Response callbacks from our TelnyxClient:
为了能够接听通话,我们首先需要监听来电邀请。我们可以通过TelnyxClient获取Telnyx Socket响应回调来实现这一点:
Call
Call
Call
通话对象
The Call class is used to manage the call state and call actions. It is used to accept, decline, end, mute, hold, and send DTMF tones during a call.
Call类用于管理通话状态和通话操作,可用于在通话过程中执行接听、拒接、结束、静音、保持和发送DTMF音等操作。
Accept Call
接听通话
In order to accept a call, we simply retrieve the instance of the call and use the .acceptCall(callID) method:
dart
_telnyxClient.call.acceptCall(_incomingInvite?.callID);要接听通话,只需获取通话实例并调用.acceptCall(callID)方法:
dart
_telnyxClient.call.acceptCall(_incomingInvite?.callID);Decline / End Call
拒接 / 结束通话
In order to end a call, we can get a stored instance of Call and call the .endCall(callID) method. To decline an incoming call we first create the call with the .createCall() method and then call the .endCall(callID) method:
dart
if (_ongoingCall) {
_telnyxClient.call.endCall(_telnyxClient.call.callId);
} else {
_telnyxClient.createCall().endCall(_incomingInvite?.callID);
}要结束通话,可以获取已存储的Call实例并调用.endCall(callID)方法。要拒接来电,首先使用.createCall()方法创建通话对象,然后调用.endCall(callID)方法:
dart
if (_ongoingCall) {
_telnyxClient.call.endCall(_telnyxClient.call.callId);
} else {
_telnyxClient.createCall().endCall(_incomingInvite?.callID);
}DTMF (Dual Tone Multi Frequency)
DTMF (双音多频)
In order to send a DTMF message while on a call you can call the .dtmf(callID, tone), method where tone is a String value of the character you would like pressed:
dart
_telnyxClient.call.dtmf(_telnyxClient.call.callId, tone);要在通话过程中发送DTMF消息,可以调用.dtmf(callID, tone)方法,其中tone是你想要按下的字符对应的字符串值:
dart
_telnyxClient.call.dtmf(_telnyxClient.call.callId, tone);Mute a call
通话静音
To mute a call, you can simply call the .onMuteUnmutePressed() method:
dart
_telnyxClient.call.onMuteUnmutePressed();要将通话静音,只需调用.onMuteUnmutePressed()方法:
dart
_telnyxClient.call.onMuteUnmutePressed();Toggle loud speaker
切换扬声器
To toggle loud speaker, you can simply call .enableSpeakerPhone(bool):
dart
_telnyxClient.call.enableSpeakerPhone(true);要切换扬声器状态,只需调用.enableSpeakerPhone(bool)方法:
dart
_telnyxClient.call.enableSpeakerPhone(true);Put a call on hold
通话保持
To put a call on hold, you can simply call the .onHoldUnholdPressed() method:
dart
_telnyxClient.call.onHoldUnholdPressed();要将通话保持,只需调用.onHoldUnholdPressed()方法:
dart
_telnyxClient.call.onHoldUnholdPressed();