# Signaling Quickstart (/en/realtime-media/rtm/quickstart/flutter)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

Use Signaling SDK to add low-latency, high-concurrency signaling and synchronization capabilities to your app.

Signaling also helps you enhance the user experience in Video Calling, Voice Calling, Interactive Live Streaming, and Broadcast Streaming applications.

This page shows you how to use the Signaling SDK to rapidly build a simple application that sends and receives messages. It shows you how to integrate the Signaling SDK in your project and implement pub/sub messaging through [Message channels](/en/realtime-media/rtm/build/work-with-channels/message-channel). To get started with stream channels, follow this guide to create a basic Signaling app and then refer to the [Stream channels](/en/realtime-media/rtm/build/work-with-channels/stream-channel) guide.

      
  
      
  
      
  
      
    ## Understand the tech [#understand-the-tech-3]

    To use Signaling features in your app, you initialize a Signaling client instance and add event listeners. To connect to Signaling, you login using an authentication token. To send a message to a message channel, you publish the message. Signaling creates a channel when a user subscribes to it. To receive messages other users publish to a channel, your app listens for events.

    To create a pub/sub session for Signaling, implement the following steps in your app:

    <Accordions>
      <Accordion title="Signaling workflow">
        ![Signaling workflow for Flutter](https://assets-docs.agora.io/images/signaling/get-started-workflow-flutter.svg)
      </Accordion>
    </Accordions>

    ## Prerequisites [#prerequisites-3]

    To implement the code presented on this page you need to have:

    * An Agora [account](/en/realtime-media/rtm/manage-agora-account) and [project](/en/realtime-media/rtm/manage-agora-account).

    * [Enabled Signaling](/en/realtime-media/rtm/manage-agora-account) in Agora Console

    * [Flutter](https://docs.flutter.dev/get-started/install) 2.0.0 or higher

    * Dart 2.15.1 or higher

    * [Android Studio](https://developer.android.com/studio), IntelliJ, VS Code, or any other IDE that supports Flutter, see [Set up an editor](https://docs.flutter.dev/get-started/editor).

    * If your target platform is iOS:
      * Xcode on macOS (latest version recommended)
      * A physical iOS device
      * iOS version 12.0 or later

    * If your target platform is Android:
      * Android Studio on macOS or Windows (latest version recommended)
      * An Android emulator or a physical Android device.

    * If you are developing a desktop application for Windows, macOS or Linux, make sure your development device meets the [Flutter desktop development requirements](https://docs.flutter.dev/development/platform-integration/desktop).

    * Ensure that a firewall is not blocking your network communication.

    <CalloutContainer type="info">
      <CalloutDescription>
        Signaling 2.x is an enhanced version compared to 1.x with a wide range of new features. It follows a new pricing structure. See [Pricing](/en/realtime-media/rtm/reference/pricing) for details.
      </CalloutDescription>
    </CalloutContainer>

    ## Project setup [#project-setup-3]

    To create a new Flutter project, follow these steps:

    1. Open a terminal and execute the following command:

       ```bash
       flutter create sdk_quickstart
       ```

    2. Open the project in your IDE. Add the Signaling SDK dependency to `pubspec.yaml` under `dependencies`:

       ```yaml
       dependencies:
       # Add the Signaling SDK dependency, use the latest version number
         agora_rtm: ^2.2.1
       ```

       <CalloutContainer type="info">
         <CalloutDescription>
           To integrate Signaling SDK version 2.2.1 or above, and Video SDK version 4.3.0 or above at the same time, refer to [handle integration issues](/en/api-reference/faq/integration/rtm2_rtc_integration_issue).
         </CalloutDescription>
       </CalloutContainer>

    3. To install the dependencies, open the terminal and execute the following command in the project path:

       ```bash
       flutter pub get
       ```

    4. To create a basic template for your project, open `main.dart`, delete the contents of the file, and paste the following code:

       ```dart
       import 'package:agora_rtm/agora_rtm.dart';
       import 'package:flutter/material.dart';
       import 'dart:convert';

       void main() async {
           // Wait for Widgets to be initialized.
           WidgetsFlutterBinding.ensureInitialized();

           // Create a Signaling client instance

           // Login to Signaling

           // Subscribe to a channel

           // Publish messages

           // Unsubscribe from a channel

           // Logout from Signaling

       }
       ```

    ## Implement Signaling [#implement-signaling-3]

    A complete code sample that implements the basic features of Signaling is presented here for your reference. To use the sample code, copy the following lines into the `lib/main.dart` file.

    **Complete sample code**

    ```dart
    import 'package:flutter/material.dart';
    import 'package:agora_rtm/agora_rtm.dart';
    import 'dart:convert';

    void main() async {
      WidgetsFlutterBinding.ensureInitialized();

      const userId = 'your_userId';
      const appId = 'your_appId';
      const token = 'your_token';
      const channelName = 'getting-started';
      late RtmClient rtmClient;

      // Create a Signaling client instance
      try {
        // Create rtm client
        final (status, client) = await RTM( appId, userId);
        if (status.error == true) {
            print('\${status.operation} failed due to \${status.reason}, error code: \${status.errorCode}');
        } else {
            rtmClient = client;
            print('Initialize success!');
        }

        // Add events listener
        rtmClient.addListener(
        // Add message event handler
        message: (event) {
          print('received a message from channel: \${event.channelName}, channel type : \${event.channelType}');
          print('message content: \${utf8.decode(event.message!)}, custom type: \${event.customType}');
        },
        // add link state event handler
        linkState: (event) {
          print('link state changed from \${event.previousState} to \${event.currentState}');
          print('reason: \${event.reason}, due to operation \${event.operation}');
        });
      } catch (e) {
        print('Initialize failed!:\${e}');
      }

      // Login to Signaling
      try {
        // login rtm service
        var (status,response) = await rtmClient.login(token);
        if (status.error == true) {
            print('\${status.operation} failed due to \${status.reason}, error code: \${status.errorCode}');
        } else {
            print('login RTM success!');
        }
      } catch (e) {
        print('Failed to login: $e');
      }

      // Subscribe to a channel
      try {
        var (status,response) = await rtmClient.subscribe(channelName);
        if (status.error == true) {
            print('\${status.operation} failed due to \${status.reason}, error code: \${status.errorCode}');
        } else {
            print('subscribe channel: \${channelName} success!');
        }
      } catch (e) {
        print('Failed to subscribe channel: $e');
      }

      // Publish messages
      // Send a message every second for 100 seconds
      for (var i = 0; i < 100; i++) {
        try {
          var (status, response) = await rtmClient.publish(
              channelName,
              'message number : $i',
              channelType: RtmChannelType.message,
              customType: 'PlainText'
              );
          if (status.error == true ){
            print('\${status.operation} failed, errorCode: \${status.errorCode}, due to \${status.reason}');
          } else {
            print('\${status.operation} success! message number:$i');
          }
        } catch (e) {
          print('Failed to publish message: $e');
        }
        await Future.delayed(Duration(seconds: 1));
      }

      // Unsubscribe from a channel
      try {
        var (status,response) = await rtmClient.unsubscribe(channelName);
        if (status.error == true) {
            print('\${status.operation} failed due to \${status.reason}, error code: \${status.errorCode}');
        } else {
            print('unsubscribe success!');
        }
      } catch (e) {
        print('something went wrong with logout: $e');
      }

      // Logout of Signaling
      try {
        // logout rtm service
        var (status,response) = await rtmClient.logout();
        if (status.error == true) {
            print('\${status.operation} failed due to \${status.reason}, error code: \${status.errorCode}');
        } else {
            print('logout RTM success!');
        }
      } catch (e) {
        print('something went wrong with logout: $e');
      }
    }
    ```

    Follow the implementation steps to understand the core API calls in the sample code or to build the demo step-by-step.

    ### Initialize the Signaling engine [#initialize-the-signaling-engine-3]

    Before calling any other Signaling SDK API, initialize an `RtmClient` object instance. Add the following code to the `lib/main.dart` file.

    ```dart
    import 'package:agora_rtm/agora_rtm.dart';
    import 'package:flutter/material.dart';
    import 'dart:convert';

    void main() async {
      // Wait for Widgets to be initialized.
      WidgetsFlutterBinding.ensureInitialized();

      const userId = 'your_userId';
      const appId = 'your_appId';
      const token = 'your_token';
      const channelName = 'getting-started';
      late RtmClient rtmClient;

      try {
        // Create rtm client
        final (status, client) = await RTM( appId, userId);
        if (status.error == true) {
            print('${status.operation} failed due to ${status.reason}, error code: ${status.errorCode}');
        } else {
            rtmClient = client;
            print('Initialize success!');
        }
      // Add events listener

      } catch (e) {
        print('Initialize failed!:${e}');
      }
    }
    ```

    ### Add an event listener [#add-an-event-listener-3]

    The event listener enables you to implement the processing logic in response to Signaling events. Use the following code to handle event notifications or display received messages:

    ```dart
    // Paste the following code snippet below the "add event listener" comment
    rtmClient.addListener(
      // Add message event handler
      message: (event) {
        print('received a message from channel: ${event.channelName}, channel type : ${event.channelType}');
        print('message content: ${utf8.decode(event.message!)}, custome type: ${event.customType}');
      },
      // Add linkState event handler
      linkState: (event) {
        print('link state changed from ${event.previousState} to ${event.currentState}');
        print('reason: ${event.reason}, due to operation ${event.operation}');
      });
    ```

    ### Log in to Signaling [#log-in-to-signaling-3]

    To connect to Signaling and access Signaling network resources, such as sending messages, and subscribing to channels, call `login`.

    During a login operation, the client attempts to establish a connection with Signaling. Once the connection is established, the client transmits heartbeat information to the Signaling server at fixed intervals to keep the client active until the client actively logs out or is disconnected. The connection is interrupted when timeout occurs. During this period, users may freely access the Signaling network resources subject to their own permissions and usage restrictions.

    ```dart
    // Paste the following code snippet below "Login to Signaling" comment
    try {
      // Login to Signaling
      var (status,response) = await rtmClient.login(token);
      if (status.error == true) {
          print('${status.operation} failed due to ${status.reason}, error code: ${status.errorCode}');
      } else {
          print('login RTM success!');
      }
    } catch (e) {
      print('Failed to login: $e');
    }
    ```

    ### Send a message [#send-a-message-2]

    To distribute a message to all subscribers of a message channel, call `publish`. The following code sends a string type message every second for 100 seconds.

    ```dart
    // Paste the following code snippet below the "Publish messages" comment
    // Send a message every second for 100 seconds
    for (var i = 0; i < 100; i++) {
      try {
        var (status, response) = await rtmClient.publish(
            channelName,
            'message number : $i',
            channelType: RtmChannelType.message,
            customType: 'PlainText'
            );
        if (status.error == true ){
          print('${status.operation} failed, errorCode: ${status.errorCode}, due to ${status.reason}');
        } else {
          print('${status.operation} success! message number:$i');
        }
      } catch (e) {
        print('Failed to publish message: $e');
      }
      await Future.delayed(Duration(seconds: 1));
    }
    ```

    ### Subscribe and unsubscribe [#subscribe-and-unsubscribe-3]

    To receive messages sent to a channel, call `subscribe` to subscribe to channel messages:

    ```dart
    // Paste the following code snippet below the "Subscribe to a channel" comment
    try {
      // Subscribe to a channel
      var (status,response) = await rtmClient.subscribe(channelName);
      if (status.error == true) {
          print('${status.operation} failed due to ${status.reason}, error code: ${status.errorCode}');
      } else {
          print('subscribe channel: ${channelName} success!');
      }
    } catch (e) {
      print('Failed to subscribe channel: $e');
    }
    ```

    When you no longer need to receive messages from a channel, `unsubscribe` from the channel:

    ```dart
    // Paste the following code snippet below the "Unsubscribe from a channel" comment
    try {
      // Unsubscribe from a channel
      var (status,response) = await rtmClient.unsubscribe(channelName);
      if (status.error == true) {
          print('${status.operation} failed due to ${status.reason}, error code: ${status.errorCode}');
      } else {
          print('unsubscribe success!');
      }
    } catch (e) {
      print('something went wrong with logout: $e');
    }
    ```

    For more information about subscribing and sending messages, see [Message channels](/en/realtime-media/rtm/build/work-with-channels/message-channel) and [Stream channels](/en/realtime-media/rtm/build/work-with-channels/stream-channel).

    ### Log out of Signaling [#log-out-of-signaling-3]

    When a user no longer needs to use Signaling, call `logout`. Logging out means closing the connection between the client and Signaling. The user is automatically logged out or unsubscribed from all message and stream channels. Other users in the channel receive an `onPresenceEvent` notification of the user leaving the channel.

    ```dart
    // Paste the following code snippet below the "Log out of Signaling" comment
    try {
      // Log out of Signaling
      var (status,response) = await rtmClient.logout();
      if (status.error == true) {
          print('${status.operation} failed due to ${status.reason}, error code: ${status.errorCode}');
      } else {
          print('logout RTM success!');
      }
    } catch (e) {
      print('something went wrong with logout: $e');
    }
    ```

    ## Test Signaling [#test-signaling-3]

    Take the following steps to test the sample code:

    1. Use the [Token Builder](https://agora-token-generator-demo.vercel.app/) to generate a Signaling token:

       1. Select Signaling from the Agora products dropdown.
       2. Fill in your app ID and app certificate from [Agora Console](https://console.agora.io/v2). Leave the remaining fields blank.
       3. Click **Generate Token**.

    2. In your code, replace `your_appId` with your app ID from Agora Console, `your_userId` with a numeric string and `your_token` with the generated token.

    3. Save the project.

    4. Connect the target device to your computer, or open the device emulator.

    5. In the terminal, execute the following command in the project path to run the sample project on the target device:

       ```bash
       flutter run
       ```

       You see the following output in the terminal:

       ```text
       flutter: publish success! message number:0
       flutter: publish success! message number:1
       flutter: publish success! message number:2
       flutter: publish success! message number:3
       ```

    6. Run a second instance of the project on another device or emulator using a different `userId`.

       You see the following notifications for messages sent from the first app instance:

       ```text
       flutter: received a message from channel: getting-started, channel type : RtmChannelType.message
       flutter: message content: message number : 0, custom type: PlainText
       flutter: received a message from channel: getting-started, channel type : RtmChannelType.message
       flutter: message content: message number : 1, custom type: PlainText
       ```

    ## Reference [#reference-3]

    This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.

    ### Token authentication [#token-authentication-3]

    In this guide you retrieve a temporary token from a [Token Builder](https://agora-token-generator-demo.vercel.app/). To understand how to create an authentication server for development purposes, see [Secure authentication with tokens](./build/connect-and-authenticate/authentication-workflow).

    ### Sample project [#sample-project-1]

    Agora provides an open source [sample project](https://github.com/AgoraIO/RTM2/tree/main/Agora-RTM2-QuickStart-Flutter) on GitHub for your reference. Download it or view the source code for a more detailed example.

    ### API reference [#api-reference-3]

    * [API reference](/en/api-reference/api-ref/signaling)
    * [Event listeners](./build/send-and-receive-messages/add-event-listener)

    
  
      
  
      
  
