Flutter PubNub Real-Time Messaging

Olivier Brand
Sep 21, 2019 · 6 min read

Introduction

This article is a follow up on a rather older article I wrote on real-time messaging with Flutter, which was mainly going through some early days Flutter plugin development techniques.

We will present here our open-source plugin published in pub dev: https://pub.dev/packages/flutter_pubnub

Why did we implement such a plugin

Our main product at Ingenio: Keen (https://www.keen.com/) enables conversations between advisors (life coaches, psychics, …) and consumers in a marketplace environment. All communications, calls, and chats are charged by the minute.

Our current offering includes web, mobile web, and native apps channels. Our native apps are implemented natively on Android and iOS and target consumers. We needed an application that enables our advisors to communicate with their customers on the go. We experimented with Flutter early on and were convinced that, due to the complexity of our project, it would be great if we could use a single code base to implement this app for iOS and Android, and proving the benefits of Flutter. I can say now that we were able to create a very complex application in record time and seriously cut down on QA cycles.

A major component of the app is the chat module. Customers can communicate in real-time with their customers through our platform using chat (and calls). The chat service is implemented using PubNub as the real-time messaging platform. As the chat service is a paid service, it was critical that we covered all aspects of disconnection, recharge mid-chat, inactivity, background handling, …

PubNub not offering a Dart plugin, we had to create our own plugin. We decided to implement the plugin on top of the iOS and Android PubNub SDKs. PubNub has a low-level REST API for handling real-time communication, however, this layer is used mainly to write an SDK from scratch, and therefore is rather complicated. Wrapping existing SDKs and providing this at the Dart layer sounded much easier.

The Plugin

The plugin handles the main capabilities PubNub has to provide:

Our roadmap includes other advanced capabilities that may be useful for other applications.

I will not drill down into the specifics of the plugin, documentation Google provides is excellent and should be enough to understand the main mechanics of a plugin. However, Google does not cover how to handle multiple instances of the plugin (the dart class that interacts with the iOS and Android layer via streams). Our application needed to be able to create multiple clients for separating very specific messages and capabilities.

Plugin Classes on iOS and Android are created/initialized once in a singleton fashion using the registerWithRegistrar method where all the streams and channels are initialized. Therefore the dart layer communicates with the native side of the plugin via pre-defined streams.

In order to enable multiple instances of our PubNub plugin and be able to channel messages to the relevant instance using the same channels was a challenge but at the end was pretty simple to implement.

The main idea is the following:

Using the Plugin

Usage of the plugin is described in the README and an example is also provided showing how to use most functionalities in a multi-client scenario. However, below is a recap of such functionalities.

Creating one or more clients

Creating one or more clients with PubNub subscribe and publish keys

// Note that the proper key values for PubNub are found in the client configuration dashboard under your PubNub account
final PubNub _client1 = PubNub(PubNubConfig('pub-c-xxxx', 'sub-cxxx'));
final PubNub _client2 = PubNub(PubNubConfig('pub-c-yyyy', 'sub-cyyy'));

Creating a client with a PubNub auth key

// Note that the proper key values for PubNub are found in the client configuration dashboard under your PubNub account
final PubNub _client = PubNub(PubNubConfig('pub-c-xxxx', 'sub-cxxx', authKey: 'auth-xxxx'));

Creating a client with a UUID

// Note that the proper key values for PubNub are found in the client configuration dashboard under your PubNub account
final PubNub _client = PubNub(PubNubConfig('pub-c-xxxx', 'sub-cxxx', uuid: '127c1ab5-fc7f-4c46-8460-3207b6782007'));

Creating a client with a presence timeout in seconds

// Note that the proper key values for PubNub are found in the client configuration dashboard under your PubNub account
final PubNub _client = PubNub(PubNubConfig('pub-c-xxxx', 'sub-cxxx', presenceTimeout: 120));

Creating a client with a filter expression

// The filter expression here is done, as an example on the uuid, so any messages sent from the client with that UUID will not be received in the subscribe method
final PubNub _client = PubNub(PubNubConfig('pub-c-xxxx', 'sub-cxxx', filter: 'uuid != "127c1ab5-fc7f-4c46-8460-3207b6782007"'));

Then in order to publish a message with the proper metadata that will trigger the filtering

// The filter expression here is done, as an example on the uuid, so any messages sent from the client with that UUID will not be received in the subscribe method
_client.publish('Test-Channel1', {'message': 'Hello World'},
metadata: {
'uuid': '127c1ab5-fc7f-4c46-8460-3207b6782007'
}
);

Retrieve the client UUID

In some cases, especially when the UUID is not set up during client creation, it can be helpful getting the UUID that was automatically generated by PubNub.

_client.uuid().then((uuid) => print('UUID: $uuid'));

Subscribe to channels and unsubscribe from channels

A client can subscribe to one or many channels. It can also unsubscribe from one or many/all channels

_client.subscribe(['Test-Channel1', 'Test-Channel1']); _client.unsubscribe('Test-Channel1');

_client.unsubscribeAll();

Publishing to a channel

A client can publish messages to one channel

_client.publish(('Test-Channel1', {'message': 'Hello World!'});

As previously described, if a filter has been set and if we need to react to it, metadata related to the configured filter must be passed during publication.

_client.publish('Test-Channel1', {'message': 'Hello World'},
metadata: {
'uuid': '127c1ab5-fc7f-4c46-8460-3207b6782007'
}
);

Handle presence

A client can respond to presence events generated by PubNub (client disconnection, …) but can also send custom presence events

_client.presence('Test-Channel1', {'state': 'busy'});

Subscribe to PubNub events

In order to build a chat service, for example, the client does not only send messages but must also consume messages, be aware of the client on the other end disconnected, status.

_client.onStatusReceived.listen((status) => print('Status:${status.toString()}'));_client.onPresenceReceived.listen((presence) => print('Presence:${presence.toString()}'));_client.onMessageReceived.listen((message) => print('Message:$message'));_client.onErrorReceived.listen((error) => print('Error:$error'));

Cleanup

When done with a PubNub client, the client can be closed and cleaned up.

_client.dispose();

Conclusion

We welcome any ideas on how to improve the plugin. The README contains a short term roadmap.

Writing plugins for Flutter is not the most fun things to do but is essential when Dart features do not exist yet. I hope that PubNub will write a pure Dart plugin so our app can also work on Flutter web and Flutter Desktop.

Flutter Community

Articles and Stories from the Flutter Community

Olivier Brand

Written by

I am passionate about mobile, especially designing end to end processes for delivering solid apps and supporting backend systems.

Flutter Community

Articles and Stories from the Flutter Community

More From Medium

More from Flutter Community

More from Flutter Community

More from Flutter Community

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade