> ## Documentation Index
> Fetch the complete documentation index at: https://www.cometchat.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Ringing

> CometChat Calling SDK v4 - Stable Release - Ringing for Flutter

## Overview

This section explains how to implement a complete calling workflow with ringing functionality, including incoming/outgoing call UI, call acceptance, rejection, and cancellation. Previously known as **Default Calling**.

<Note>
  After the call is accepted, you need to start the call session. See the [Call Session](/calls/v4/flutter/call-session#start-call-session) guide for details on starting and managing the actual call.
</Note>

**Call Flow:**

1. **Caller** initiates a call using `initiateCall()`
2. **Receiver** gets notified via `onIncomingCallReceived()` callback
3. **Receiver** can either:
   * Accept the call using `acceptCall()`
   * Reject the call using `rejectCall()` with status `CALL_STATUS_REJECTED`
4. **Caller** can cancel the call using `rejectCall()` with status `CALL_STATUS_CANCELLED`
5. Once accepted, both participants call `startSession()` to join the call

## Initiate Call

The `initiateCall()` method sends a call request to a user or a group. On success, the receiver gets an `onIncomingCallReceived()` callback.

```dart theme={null}
// User call
String receiverID = "UID";
String receiverType = CometChatConstants.RECEIVER_TYPE_USER;
String callType = CometChatConstants.CALL_TYPE_VIDEO;

Call call = Call(
  receiverUid: receiverID,
  receiverType: receiverType,
  type: callType,
);

CometChat.initiateCall(
  call,
  onSuccess: (Call call) {
    debugPrint("Call initiated: ${call.sessionId}");
    // Show outgoing call UI
    // Store call.sessionId for later use
  },
  onError: (CometChatException e) {
    debugPrint("Call initiation failed: $e");
  },
);
```

```dart theme={null}
// Group call
String receiverID = "GUID";
String receiverType = CometChatConstants.RECEIVER_TYPE_GROUP;
String callType = CometChatConstants.CALL_TYPE_VIDEO;

Call call = Call(
  receiverUid: receiverID,
  receiverType: receiverType,
  type: callType,
);

CometChat.initiateCall(
  call,
  onSuccess: (Call call) {
    debugPrint("Call initiated: ${call.sessionId}");
    // Show outgoing call UI
  },
  onError: (CometChatException e) {
    debugPrint("Call initiation failed: $e");
  },
);
```

| Parameter      | Description                                                                                                   |
| -------------- | ------------------------------------------------------------------------------------------------------------- |
| `receiverID`   | The UID or GUID of the recipient                                                                              |
| `receiverType` | The type of the receiver: `CometChatConstants.RECEIVER_TYPE_USER` or `CometChatConstants.RECEIVER_TYPE_GROUP` |
| `callType`     | The type of the call: `CometChatConstants.CALL_TYPE_AUDIO` or `CometChatConstants.CALL_TYPE_VIDEO`            |

On success, a `Call` object is returned containing the call details including a unique `sessionId` required for starting the call session.

## Call Listeners

Register the `CallListener` to receive real-time call events. Each listener requires a unique `listenerId` string to prevent duplicate registrations and enable targeted removal.

```dart theme={null}
String listenerId = "UNIQUE_LISTENER_ID";

class YourClassName extends State<YourScreen> with CallListener {
  @override
  void initState() {
    super.initState();
    CometChat.addCallListener(listenerId, this);
  }

  @override
  void dispose() {
    super.dispose();
    CometChat.removeCallListener(listenerId);
  }

  @override
  void onIncomingCallReceived(Call call) {
    debugPrint("Incoming call from: ${call.sender?.name}");
    // Show incoming call UI
  }

  @override
  void onOutgoingCallAccepted(Call call) {
    debugPrint("Outgoing call accepted");
    // Receiver accepted, start the call session
  }

  @override
  void onOutgoingCallRejected(Call call) {
    debugPrint("Outgoing call rejected");
    // Receiver rejected, dismiss outgoing call UI
  }

  @override
  void onIncomingCallCancelled(Call call) {
    debugPrint("Incoming call cancelled");
    // Caller cancelled, dismiss incoming call UI
  }

  @override
  void onCallEndedMessageReceived(Call call) {
    debugPrint("Call ended message received");
    // Call ended by remote participant
  }
}
```

### Events

| Event                                   | Description                                                                                                                              |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `onIncomingCallReceived(Call call)`     | Invoked when an incoming call is received. The `call` contains caller details, session ID, and call type. Display incoming call UI here. |
| `onOutgoingCallAccepted(Call call)`     | Invoked on the caller's device when the receiver accepts. Generate call token and start the session here.                                |
| `onOutgoingCallRejected(Call call)`     | Invoked on the caller's device when the receiver rejects the call. Dismiss outgoing call UI here.                                        |
| `onIncomingCallCancelled(Call call)`    | Invoked on the receiver's device when the caller cancels before answering. Dismiss incoming call UI here.                                |
| `onCallEndedMessageReceived(Call call)` | Invoked when a call ends. The `call` contains final status and duration. Update call history here.                                       |

## Accept Call

When an incoming call is received via `onIncomingCallReceived()`, use `acceptCall()` to accept it. On success, start the call session.

```dart theme={null}
String sessionId = call.sessionId!; // From onIncomingCallReceived

CometChat.acceptCall(
  sessionId,
  onSuccess: (Call call) {
    debugPrint("Call accepted");
    // Call accepted, now start the call session
    // Generate token and call startSession()
  },
  onError: (CometChatException e) {
    debugPrint("Accept call failed: $e");
  },
);
```

## Reject Call

Use `rejectCall()` to reject an incoming call. Set the status to `CALL_STATUS_REJECTED`.

```dart theme={null}
String sessionId = call.sessionId!;
String status = CometChatConstants.CALL_STATUS_REJECTED;

CometChat.rejectCall(
  sessionId,
  status,
  onSuccess: (Call call) {
    debugPrint("Call rejected");
    // Dismiss incoming call UI
  },
  onError: (CometChatException e) {
    debugPrint("Reject call failed: $e");
  },
);
```

## Cancel Call

The caller can cancel an outgoing call before it's answered using `rejectCall()` with status `CALL_STATUS_CANCELLED`.

```dart theme={null}
String sessionId = call.sessionId!;
String status = CometChatConstants.CALL_STATUS_CANCELLED;

CometChat.rejectCall(
  sessionId,
  status,
  onSuccess: (Call call) {
    debugPrint("Call cancelled");
    // Dismiss outgoing call UI
  },
  onError: (CometChatException e) {
    debugPrint("Cancel call failed: $e");
  },
);
```

## Start Call Session

Once the call is accepted, both participants need to start the call session.

**Caller flow:** In the `onOutgoingCallAccepted()` callback, generate a token and start the session.

**Receiver flow:** In the `acceptCall()` success callback, generate a token and start the session.

```dart theme={null}
String sessionId = call.sessionId!;
String userAuthToken = await CometChat.getUserAuthToken();

// Step 1: Generate call token
CometChatCalls.generateToken(
  sessionId,
  userAuthToken,
  onSuccess: (GenerateToken generateToken) {
    // Step 2: Configure call settings with listeners
    CallSettings callSettings = (CallSettingsBuilder()
      ..defaultLayout = true
      ..setAudioOnlyCall = false
      ..listener = CallEventsHandler(sessionId) // Your listener implementation
    ).build();

    // Step 3: Start the session
    CometChatCalls.startSession(
      generateToken.token!,
      callSettings,
      onSuccess: (Widget? callingWidget) {
        debugPrint("Call session started");
        // Display the callingWidget in your UI
      },
      onError: (CometChatCallsException e) {
        debugPrint("Start session failed: $e");
      },
    );
  },
  onError: (CometChatCallsException e) {
    debugPrint("Token generation failed: $e");
  },
);
```

For more details on call settings and customization, see the [Call Session](/calls/v4/flutter/call-session#start-call-session) guide.

## End Call

To end an active call in the ringing flow, the process differs based on who ends the call.

**User who ends the call:**

When the user presses the end call button, the `onCallEndButtonPressed()` callback is triggered. Inside this callback, call `CometChat.endCall()` to notify other participants. On success, call `CometChat.clearActiveCall()` and `CometChatCalls.endSession()` to release resources.

```dart theme={null}
@override
void onCallEndButtonPressed() {
  CometChat.endCall(
    sessionId,
    onSuccess: (Call call) {
      debugPrint("Call ended successfully");
      CometChat.clearActiveCall();
      CometChatCalls.endSession(
        onSuccess: (success) {
          debugPrint("Session ended");
          // Close the calling screen
        },
        onError: (e) {
          debugPrint("End session failed: $e");
        },
      );
    },
    onError: (CometChatException e) {
      debugPrint("End call failed: $e");
    },
  );
}
```

**Remote participant** (receives `onCallEnded()` callback):

```dart theme={null}
@override
void onCallEnded() {
  CometChat.clearActiveCall();
  CometChatCalls.endSession(
    onSuccess: (success) {
      debugPrint("Session ended");
      // Close the calling screen
    },
    onError: (e) {
      debugPrint("End session failed: $e");
    },
  );
}
```

For more details, see the [End Call Session](/calls/v4/flutter/call-session#end-call-session) guide.

## Busy Call Handling

If the receiver is already on another call, you can reject the incoming call with `CALL_STATUS_BUSY` status.

```dart theme={null}
String sessionId = call.sessionId!;
String status = CometChatConstants.CALL_STATUS_BUSY;

CometChat.rejectCall(
  sessionId,
  status,
  onSuccess: (Call call) {
    debugPrint("Busy status sent");
  },
  onError: (CometChatException e) {
    debugPrint("Busy rejection failed: $e");
  },
);
```
