Publish/Subscribe API for Dart SDK

PubNub's publish-processing latency, the time to accept and acknowledge a publish request, is about 0.5 ms within the same region. Send a message to one recipient or broadcast to thousands of subscribers.

For higher-level conceptual details on publishing and subscribing, refer to Connection Management and to Publish Messages.

Publish​

publish() sends a message to all channel subscribers. PubNub replicates the message across its points of presence and delivers it to all subscribed clients on that channel.

#

#

#

#

#

#

#

Method(s)​

To Publish a message you can use the following method(s) in the Dart SDK:

1pubnub.publish(
2 String channel,
3 dynamic message,
4 {Keyset? keyset,
5 String? using,
6 dynamic meta,
7 bool? storeMessage,
8 int? ttl,
9 String? customMessageType}
10)
required
channel required#
Type: String
Default: no default
Destination of the message (channel ID).
message required#
Type: Any
Default: no default
The payload.
keyset#
Type: Keyset
Default: no default
Override for the PubNub default keyset configuration.
using#
Type: String
Default: no default
Keyset name from the keysetStore to be used for this method call.
meta#
Type: dynamic
Default: Not set
Metadata object which can be used with the filtering ability.
storeMessage#
Type: bool
Default: Account default
Store message in history.
If not specified, the decision depends on whether Message Persistence has been enabled for the key or not.
ttl#
Type: int
Default: no default
Set a per message time to live in Message Persistence.
1. If storeMessage = true, and ttl = 0, the message is stored with no expiry time.
2. If storeMessage = true and ttl = X (X is an Integer value), the message is stored with an expiry time of X hours unless you have message retention set to Unlimited on your keyset configuration in the Admin Portal.
3. If storeMessage = false, the ttl parameter is ignored.
4. If ttl isn't specified, then expiration of the message defaults back to the expiry value for the key.
customMessageType#
Type: String
Default: no default
A case-sensitive, alphanumeric string from 3 to 50 characters describing the business-specific label or category of the message. Dashes - and underscores _ are allowed. The value cannot start with special characters or the string pn_ or pn-.

Examples: text, action, poll.

Sample code​

Publish a message to a channel:

1import 'package:pubnub/pubnub.dart';
2
3void main() async {
4 // Create PubNub instance with default keyset.
5 var pubnub = PubNub(
6 defaultKeyset: Keyset(
7 subscribeKey: 'demo',
8 publishKey: 'demo',
9 userId: UserId('myUniqueUserId'),
10 ),
11 );
12
13 // Channel to publish the message to
14 String channel = 'myChannel';
15
show all 32 lines

Returns​

The publish() operation returns a PublishResult which contains the following operations:

description#
Type: String
The description, for example Sent.
timetoken#
Type: int
Returns an int representation of the timetoken when the message was published.

Other examples​

Publish with metadata​

1var result = await pubnub.publish('my_channel', 'hello', meta: '<json data>', customMessageType: 'text-message');

Publishing JsonObject (Google GSON)​

1var message = {'hello': 'world'};
2var result = await pubnub.publish('my_channel', message, customMessageType: 'text-message');

Publishing JsonArray (Google GSON)​

1var message = ['hello', 'world'];
2var result = await pubnub.publish('my_channel', message, customMessageType: 'text-message');

Publishing JSONObject (org.json)​

1var jsonString = '{"score": 40}';
2var result = await pubnub.publish('my_channel', jsonDecode(jsonString), customMessageType: 'text-message');

Publishing JSONArray (org.json)​

1var jsonString = '''
2[
3 {"score": 40},
4 {"score": 80}
5]
6''';
7var result = await pubnub.publish('my_channel', jsonDecode(jsonString), customMessageType: 'text-message');

Store the published message for 10 hours​

1var result =
2 await pubnub.publish('my_channel', 'hello', storeMessage: true, ttl: 10, customMessageType: 'text-message');

Signal​

The signal() function sends a signal to all subscribers of a channel.

By default, signals are limited to a message payload size of 64 bytes. This limit applies only to the payload, and not to the URI or headers. If you require a larger payload size, please contact support.

Method(s)​

To Signal a message you can use the following method(s) in the Dart SDK:

1pubnub.signal(
2 String channel,
3 dynamic message,
4 {Keyset? keyset,
5 String? using,
6 String? customMessageType}
7)
required
channel required#
Type: String
Destination of the message (channel ID).
message required#
Type: Any
The payload.
keyset#
Type: Keyset
Override for the PubNub default keyset configuration.
using#
Type: String
Keyset name from the keysetStore to be used for this method call.
customMessageType#
Type: String
A case-sensitive, alphanumeric string from 3 to 50 characters describing the business-specific label or category of the message. Dashes - and underscores _ are allowed. The value cannot start with special characters or the string pn_ or pn-.

Examples: text, action, poll.

Sample code​

Signal a message to a channel:

1var result = await pubnub.signal('myChannel', 'signal!', customMessageType: 'text-message');

Response​

The signal() operation returns a SignalResult which contains the following operations:

description#
Type: String
The description, for example Sent.
timetoken#
Type: int
Returns an int representation of the timetoken when the signal was published.

Subscribe​

Receive messages​

Your app receives messages and events via event listeners. The event listener is a single point through which your app receives all the messages, signals, and events that are sent in any channel you are subscribed to.

For more information about adding a listener, refer to the Subscription section.

No built-in event throttling

The PubNub SDK delivers every incoming event to your listener as it arrives. There is no built-in throttling or rate-limiting on the subscriber side. If you need to control how often your application processes events, wrap your listener callback with a throttle or debounce utility from your language or framework ecosystem.

To reduce the number of messages delivered to your client in the first place, use Subscribe Filters to filter messages server-side before they reach your listener.

Description​

This function causes the client to create an open TCP socket to the PubNub Real-Time Network and begin listening for messages on a specified channel ID. To subscribe to a channel ID the client must send the appropriate subscribeKey at initialization.

By default a newly subscribed client will only receive messages published to the channel after the subscribe() call completes.

If a client gets disconnected from a channel, it can automatically attempt to reconnect to that channel ID and retrieve any available messages that were missed during that period. This can be achieved by setting retryPolicy to RetryPolicy.linear, when initializing the client.

Unsubscribing from all channels

Unsubscribing from all channels, and then subscribing to a new channel Y is not the same as subscribing to channel Y and then unsubscribing from the previously subscribed channel(s). Unsubscribing from all channels resets the last-received timetoken and thus, there could be some gaps in the subscription that may lead to message loss.

Method(s)​

To Subscribe to a channel you can use the following method(s) in the Dart SDK:

1pubnub.subscribe(
2 {Set<String>? channels,
3 Set<String>? channelGroups,
4 bool withPresence = false,
5 String? withProjection,
6 Timetoken? timetoken,
7 Keyset? keyset,
8 String? using}
9)
required
channels required#
Type: Set<String>
channels to subscribe to. Either channel ID or channelGroup is required.
channelGroups required#
Type: Set<String>
channelGroups to subscribe to. Either channel ID or channelGroup is required.
withPresence#
Type: bool
Also subscribe to related presence information.

For information on how to receive presence events and what those events are, refer to Presence Events.
withProjection#
Type: String
DataSync projection to observe. Each ID in channels is replaced by its projection data channel __<projection>__<id>. It doesn't apply to channelGroups. null, a blank name, default, and __default__ refer to the base projection.
timetoken#
Type: Timetoken
Timetoken to start the subscription from.
keyset#
Type: Keyset
Override for the PubNub default keyset configuration.
using#
Type: String
Keyset name from the keysetStore to be used for this method call.

Sample code​

Subscribe to a channel:

1var channel = "my_channel";
2var subscription = pubnub.subscribe(channels: {channel});

Returns​

The subscribe() method returns a Subscription. For more information, refer to Subscription.

Subscription​

A Subscription contains a Dart stream of messages from the channel(s) to which you are subscribed. You can transform that stream in the usual ways, or add a listener using listen.

Listeners​

For a list of available listeners, refer to the Listeners section.

Cancel​

The cancel() method cancels the subscription. This disposes of internal streams, so the subscription becomes unusable.

Method(s)​
1subscription.cancel();
Sample code​
1// var subscription = pubnub.subscribe(channels: {'my_channel'});
2// active subscription available
3await subscription.cancel();
Returns​

The cancel() operation on Subscription doesn't return any object. It disposes of internal streams, so the subscription becomes unusable.

Dispose​

The dispose() method is an alias for the cancel() method.

Subscribe​

The subscribe() method is an alias for the resume() method with additional reconnection logic. If the subscription is not intentionally paused, it reconnects the subscription. Otherwise, it resumes the subscription.

Method(s)​
1subscription.subscribe()
Sample code​
1var subscription = pubnub.subscription(channels: {'my_channel'});
2// Later, activate the subscription
3subscription.subscribe();
Unsubscribe​

The unsubscribe() method is an alias for the pause() method.

Pause​

Pausing a subscription prevents the message and presence streams from emitting messages. Keep in mind that you may miss messages while subscription is paused. If subscription is currently paused, this method is a no-op.

Method(s)​
1subscription.pause() 
Sample code​
1var subscription = pubnub.subscribe(channels: {'my_channel'});
2// active subscription available
3subscription.pause();
Returns​

The pause() operation on Subscription doesn't return any object. Pausing subscription prevents the messages and presence streams from emitting messages.

Resume​

Resumes a paused subscription. If a subscription isn't paused, then this method is a no-op.

Method(s)​
1subscription.resume() 
Sample code​
1// If subscription is paused
2subscription.resume();
Returns​

The resume() operation on Subscription object doesn't return any object. If subscription isn't paused, then this method does nothing.

Restore​

Restores the subscription. This method is useful when a subscription encounters an error and you want to recover without creating a new subscription.

Method(s)​
1subscription.restore()
Sample code​
1var subscription = pubnub.subscribe(channels: {'my_channel'});
2
3subscription.messages.listen(
4 (message) {
5 print('Received: ${message.content}');
6 },
7 onError: (error) async {
8 print('Error occurred: $error');
9 // Restore the subscription after an error
10 await subscription.restore();
11 }
12);
Returns​

The restore() operation returns a Future<void> that completes when the subscription has been restored.

Add DataSync listener​

DataSync objects publish create, update, and delete events on ordinary channels. The Dart SDK provides a real-time handle for each of the three kinds that have their own channel: pubnub.dataSyncUser(id), pubnub.dataSyncChannel(id), and pubnub.dataSyncEntity(id). Call subscription() on the handle to get an inactive Subscription for the object, activate it with subscribe(), and listen on its dataSync stream. The same events also arrive on the events stream as DataSyncEvent objects.

The subscription() method of a handle takes two optional parameters: projection, the name of the projection to observe, and timetoken, the timetoken to start receiving events from. The three handle methods also accept keyset and using, like pubnub.subscribe(). Ids are used verbatim, so a wildcard such as customer.* works. A membership or relationship has no handle, so subscribe through the user, channel, or entity it connects.

You can still subscribe to a data channel by name with pubnub.subscribe(channels: ...) and listen on dataSync, which is the same thing without the handle.

Events fan out beyond the object's own channel

An update or delete on a user, channel, or entity is also delivered on the channel of every object connected to it by a relationship or membership. Refer to Where each event is delivered for the full delivery table.

The listener attaches to one Subscription, there is no client-wide DataSync listener in this SDK. A Subscription delivers only the events of its own channels, so each subscription, including one per projection, needs its own dataSync listener. Events that lack an id or carry an unknown object kind are discarded.

Enable DataSync events on the class

To receive DataSync events, enable event publishing for the object's class in the Admin Portal. Refer to DataSync events for details.

Projections filter what a token receives

A token scoped to a projection only receives events published to that projection's channel, and the event's payload and status only carry the fields the class tags with that projection. Refer to Projection channels.

DataSyncEvent carries:

event#
Type: DataSyncEventType
create, update, or delete. unknown stands for a change type the SDK doesn't recognize.
objectType#
Type: DataSyncObjectType
The object kind: user, channel, entity, membership, or relationship.
className#
Type: String?
The specific class within the kind.
classVersion#
Type: int?
The class schema version the event was published under.
classLevel#
Type: String?
Global or SubKey.
id#
Type: String
The id of the changed object.
status#
Type: String?
Free-form lifecycle status. Subject to projection filtering.
payload#
Type: Map<String, dynamic>?
Application data of the object. Subject to projection filtering.
eTag#
Type: String?
Content fingerprint of the object.
createdAt#
Type: String?
ISO 8601 creation timestamp.
updatedAt#
Type: String?
ISO 8601 timestamp of the last update.
expiresAt#
Type: String?
ISO 8601 timestamp at which the object expires.
deletedAt#
Type: String?
ISO 8601 deletion timestamp. Set on delete.
channelId#
Type: String?
Channel of a membership.
userId#
Type: String?
User of a membership.
entityAId#
Type: String?
First entity of a relationship.
entityBId#
Type: String?
Second entity of a relationship.
data#
Type: Map<String, dynamic>
Raw object data as received from the server.
source#
Type: String?
Always data-sync, events from other sources never reach the dataSync stream.
version#
Type: String?
Schema version of the event envelope.
channel#
Type: String
The channel the event was received on.
subscription#
Type: String?
The wildcard or channel group that delivered the event, if any.
timetoken#
Type: Timetoken
Publish timetoken of the event.

On a delete event, only id and deletedAt are set. id, eTag, createdAt, updatedAt, and expiresAt are never filtered by a projection, only payload and status are. A membership event reports the channel in channelId and the user in userId, and a relationship event reports the linked entities in entityAId and entityBId.

1

To handle DataSync events together with the other event kinds, listen on events and match on DataSyncEvent:

1

For the fetch methods, refer to DataSync API. For the concurrency pattern, refer to the ETags concept documentation.

Other examples​

Basic subscribe with logging​

1// Create a root logger
2var logger = StreamLogger.root('root', logLevel: Level.all);
3
4// Subscribe to messages with a default printer
5var sub = logger.stream.listen(
6 LogRecord.createPrinter(r'[$time] (${level.name}) $scope: $message'));
7
8// Provide logging only for the parts that you are interested in.
9var _ = await provideLogger(logger, () async {
10 var subscription = pubnub.subscribe(channels: {'test'});
11
12 var message = subscription.messages.first;
13
14 await pubnub.publish('test', {'message': 'My message'});
15
show all 19 lines

Unsubscribe from all channels​

Cancels all existing subscriptions across all keysets.

1// Create subscriptions
2var subscription1 = pubnub.subscribe(channels: {'channel1'});
3var subscription2 = pubnub.subscribe(channels: {'channel2', 'channel3'});
4
5// Later, unsubscribe from all channels
6await pubnub.unsubscribeAll();
Unsubscribing from all channels

Unsubscribing from all channels resets the last-received timetoken.

Subscribing to multiple channels​

It's possible to subscribe to more than one channel using the Multiplexing feature. The example shows how to do that using an array to specify the channel names.

Alternative subscription methods

You can also use Wildcard Subscribe and Channel Groups to subscribe to multiple channels at a time. To use these features, the Stream Controller add-on must be enabled on your keyset in the Admin Portal.

1var subscription = pubnub.subscribe(channels: {'my_channel', 'channel1'});

Subscribing to a presence channel​

Requires Presence

This method requires that the Presence add-on is enabled for your key in the Admin Portal.

For information on how to receive presence events and what those events are, refer to Presence Events.

For any given channel there is an associated Presence channel. You can subscribe directly to the channel by appending -pnpres to the channel name. For example the channel named my_channel would have the presence channel named my_channel-pnpres. Presence data can be observed inside the SubscribeCallback#message(PubNub, PNMessageResult) callback.

1var subscription =
2 pubnub.subscribe(channels: {'my_channel'}, withPresence: true);

Sample Responses​

Join event​

1{
2 "Event": "join",
3 "Uuid": "175c2c67-uuid-uuid-8f4b-1db94f90e39e",
4 "Timestamp": 1345546797,
5 "Occupancy": 2,
6 "State": null,
7 "Channel":" my_channel",
8 "Subscription": "",
9 "Timetoken": 15034141109823424,
10 "UserMetadata": null,
11 "Join": null,
12 "Timeout": null,
13 "Leave": null,
14 "HereNowRefresh": false
15}

Leave event​

1{
2 "Event": "leave",
3 "Uuid": "175c2c67-uuid-uuid-8f4b-1db94f90e39e",
4 "Timestamp": 1345546797,
5 "Occupancy": 1,
6 "State": null,
7 "Channel": "my_channel",
8 "Subscription": "",
9 "Timetoken": 15034141109823424,
10 "UserMetadata": null,
11 "Join": null,
12 "Timeout": null,
13 "Leave": null,
14 "HereNowRefresh": false
15}

Timeout event​

1{
2 "Event": "timeout",
3 "Uuid": "175c2c67-uuid-uuid-8f4b-1db94f90e39e",
4 "Timestamp": 1345546797,
5 "Occupancy": 0,
6 "State": null,
7 "Channel": "my_channel",
8 "Subscription": "",
9 "Timetoken": 15034141109823424,
10 "UserMetadata": null,
11 "Join": null,
12 "Timeout": null,
13 "Leave": null,
14 "HereNowRefresh": false
15}

State change event​

1{
2 "Event": "state-change",
3 "Uuid": "175c2c67-uuid-uuid-8f4b-1db94f90e39e",
4 "Timestamp": 1345546797,
5 "Occupancy": 1,
6 "State": {
7 "isTyping": true
8 },
9 "Channel": "my_channel",
10 "Subscription": "",
11 "Timetoken": 15034141109823424,
12 "UserMetadata": null,
13 "Join": null,
14 "Timeout": null,
15 "Leave": null,
show all 17 lines

Interval event​

1{
2 "Event": "interval",
3 "Uuid": "175c2c67-uuid-uuid-8f4b-1db94f90e39e",
4 "Timestamp": 1345546797,
5 "Occupancy": 2,
6 "State": null,
7 "Channel": "my_channel",
8 "Subscription": "",
9 "Timetoken": 15034141109823424,
10 "UserMetadata": null,
11 "Join": null,
12 "Timeout": null,
13 "Leave": null,
14 "HereNowRefresh": false
15}

When a channel is in interval mode with presence_deltas pnconfig flag enabled, the interval message may also include the following fields which contain an array of changed UUIDs since the last interval message. This settings can be altered in the Admin Portal.

  • joined
  • left
  • timed out

For example, this interval message indicates there were 2 new UUIDs that joined and 1 timed out UUID since the last interval:

1{
2 "Event": "interval",
3 "Uuid": "175c2c67-uuid-uuid-8f4b-1db94f90e39e",
4 "Timestamp": <unix timestamp>,
5 "Occupancy": <# users in channel>,
6 "State": null,
7 "Channel": "my_channel",
8 "Subscription": "",
9 "Timetoken": 15034141109823424,
10 "UserMetadata": null,
11 "Join": ["uuid2", "uuid3"],
12 "Timeout": ["uuid1"],
13 "Leave": null,
14 "HereNowRefresh": false
15}

If the full interval message is greater than 30 KiB (since the max publish payload is ∼32 KiB), none of the extra fields will be present. Instead, there will be a here_now_refresh Boolean field set to true. This indicates to the user that they should do a hereNow request to get the complete list of users present in the channel.

1{
2 "Event": "interval",
3 "Uuid": "175c2c67-uuid-uuid-8f4b-1db94f90e39e",
4 "Timestamp": <unix timestamp>,
5 "Occupancy": <# users in channel>,
6 "State": null,
7 "Channel": "my_channel",
8 "Subscription": "",
9 "Timetoken": 15034141109823424,
10 "UserMetadata": null,
11 "Join": null,
12 "Timeout": null,
13 "Leave": null,
14 "HereNowRefresh": true
15}

Wildcard subscribe to channels​

Requires Stream Controller add-on

This method requires that the Stream Controller add-on is enabled for your key in the Admin Portal (with Enable Wildcard Subscribe checked). See Enable the features your app calls.

Wildcard subscribes allow the client to subscribe to multiple channels using wildcard. For example, if you subscribe to a.* you will get all messages for a.b, a.c, a.x. The wildcarded * portion refers to any portion of the channel string name after the dot (.).

1var subscription = pubnub.subscribe(channels: {'foo.*'});
Wildcard grants and revokes

Only one level (a.*) of wildcarding is supported. If you grant on * or a.b.*, the grant will treat * or a.b.* as a single channel named either * or a.b.*. The same rule applies to revokes - you can revoke permissions with wildcards from one level deep, like a.*. However, you can do that only if you initially used wildcards to grant permissions to a.*.

Subscribe to a channel group​

Requires Stream Controller add-on

This method requires that the Stream Controller add-on is enabled for your key in the Admin Portal. See Enable the features your app calls.

1var subscription = pubnub.subscribe(channelGroups: {'cg1'});

Subscribe to the presence channel of a channel group​

Requires Stream Controller add-on

This method requires that the Stream Controller add-on is enabled for your key in the Admin Portal. See Enable the features your app calls.

1var subscription =
2 pubnub.subscribe(channelGroups: {'cg1', 'cg2'}, withPresence: true);

Was this page useful?

Last updated on