ably_flutter 1.2.0-preview.1 copy "ably_flutter: ^1.2.0-preview.1" to clipboard
ably_flutter: ^1.2.0-preview.1 copied to clipboard

outdated

A wrapper around Ably's Cocoa and Java client library SDKs, providing iOS and Android support.

Ably Flutter Plugin #

flutter_integration

A Flutter plugin wrapping the ably-cocoa (iOS) and ably-java (Android) client library SDKs for Ably, the realtime data delivery platform.

Ably provides the best infrastructure and APIs to power realtime experiences at scale, delivering billions of realtime messages everyday to millions of end users. We handle the complexity of realtime messaging so you can focus on your code.

Development Preview #

We are actively working on implementing the full Ably feature set in this plugin, so please do not hesitate to get in touch if you are interested in using Ably from your Flutter apps, especially if there is a feature or capability in particular that you think we should prioritise.

Create a public Issue or visit our Support and Help site to discuss privately.

Resources #

Supported Platforms #

iOS #

iOS 9 or newer.

Android #

API Level 19 (Android 4.4, KitKat) or newer.

This project uses Java 8 language features, utilising Desugaring to support lower versions of the Android runtime (i.e. API Levels prior to 24)

If your project needs support for SDK Version lower than 24, Android Gradle Plugin 4.0.0+ must be used. You might also need to upgrade gradle distribution accordingly.

Running the example #

  • Clone the repo
  • cd to example folder
  • run flutter packages get to install dependencies
  • flutter run will start the application on connected android / iOS device

Usage #

Specify Dependency #

Package home: pub.dev/packages/ably_flutter

See: Adding a package dependency to an app

Import the package #

import 'package:ably_flutter/ably_flutter.dart' as ably;

Configure a Client Options object #

final clientOptions = ably.ClientOptions.fromKey("<YOUR APP KEY>");
clientOptions.logLevel = ably.LogLevel.verbose; // optional

Using the REST API #

Creating the REST client instance:

ably.Rest rest = ably.Rest(options: clientOptions);

Getting a channel instance

ably.RestChannel channel = rest.channels.get('test');

Publishing messages using REST:

// both name and data
await channel.publish(name: "Hello", data: "Ably");

// just name
await channel.publish(name: "Hello");

// just data
await channel.publish(data: "Ably");

// an empty message
await channel.publish();

Get REST history:

void getHistory([ably.RestHistoryParams params]) async {
    // getting channel history, by passing or omitting the optional params
    var result = await channel.history(params);

    var messages = result.items; //get messages
    var hasNextPage = result.hasNext(); //tells whether there are more results
    if(hasNextPage){    
      result = await result.next();  //fetches next page results
      messages = result.items;
    }
    if(!hasNextPage){
      result = await result.first();  //fetches first page results
      messages = result.items;
    }
}

// history with default params
getHistory();

// sorted and filtered history
getHistory(ably.RestHistoryParams(direction: 'forwards', limit: 10));

Using the Realtime API #

Creating the Realtime client instance:

ably.Realtime realtime = ably.Realtime(options: clientOptions);

Listening for connection state change events:

realtime.connection
  .on()
  .listen((ably.ConnectionStateChange stateChange) async {
    print('Realtime connection state changed: ${stateChange.event}');
    setState(() {
      _realtimeConnectionState = stateChange.current;
    });
});

Listening for a particular connection state change event (e.g. connected):

realtime.connection
  .on(ably.ConnectionEvent.connected)
  .listen((ably.ConnectionStateChange stateChange) async {
    print('Realtime connection state changed: ${stateChange.event}');
    setState(() {
      _realtimeConnectionState = stateChange.current;
    });
});

Creating a Realtime channel instance:

ably.RealtimeChannel channel = realtime.channels.get('channel-name');

Listening for channel events:

channel.on().listen((ably.ChannelStateChange stateChange){
  print("Channel state changed: ${stateChange.current}");
});

Attaching to the channel:

await channel.attach();

Detaching from the channel:

await channel.detach();

Subscribing to receive messages received on the channel:

var messageStream = channel.subscribe();
var channelMessageSubscription = messageStream.listen((ably.Message message){
  print("New message arrived ${message.data}");
});

Use channel.subscribe(name: "event1") or channel.subscribe(names: ["event1", "event2"]) to listen to specific named messages.

UnSubscribing from receiving messages on the channel:

await channelMessageSubscription.cancel();

Publishing channel messages

// both name and data
await channel.publish(name: "event1", data: "hello world");
await channel.publish(name: "event1", data: {"hello": "world", "hey": "ably"});
await channel.publish(name: "event1", data: [{"hello": {"world": true}, "ably": {"serious": "realtime"}]);

// single message
await channel.publish(message: ably.Message()..name = "event1"..data = {"hello": "world"});

// multiple messages
await channel.publish(messages: [
  ably.Message()..name="event1"..data = {"hello": "ably"},
  ably.Message()..name="event1"..data = {"hello": "world"}
]);

Get realtime history

void getHistory([ably.RealtimeHistoryParams params]) async {
    var result = await channel.history(params);

    var messages = result.items; //get messages
    var hasNextPage = result.hasNext(); //tells whether there are more results
    if(hasNextPage){    
      result = await result.next();  //fetches next page results
      messages = result.items;
    }
    if(!hasNextPage){
      result = await result.first();  //fetches first page results
      messages = result.items;
    }
}

// history with default params
getHistory();

// sorted and filtered history
getHistory(ably.RealtimeHistoryParams(direction: 'forwards', limit: 10));

Caveats #

RTE6a compliance #

Using the Streams based approach doesn't fully conform with RTE6a from our client library features specification.

The Problem

StreamSubscription subscriptionToBeCancelled;

// Listener registered 1st
realtime.connection.on().listen((ably.ConnectionStateChange stateChange) async {
  if (stateChange.event == ably.ConnectionEvent.connected) {
    await subscriptionToBeCancelled.cancel();       // Cancelling 2nd listener
  }
});

// Listener registered 2nd
subscriptionToBeCancelled = realtime.connection.on().listen((ably.ConnectionStateChange stateChange) async {
  print('State changed');
});

In the example above, the 2nd listener is cancelled when the 1st listener is notified about the "connected" event. As per RTE6a, the 2nd listener should also be triggered. It will not be as the 2nd listener was registered after the 1st listener and stream subscription is cancelled immediately after 1st listener is triggered.

This wouldn't have happened if the 2nd listener had been registered before the 1st was.

However, using a neat little workaround will fix this...

The Workaround - Cancelling using delay

Instead of await subscriptionToBeCancelled.cancel();, use

Future.delayed(Duration.zero, (){
    subscriptionToBeCancelled.cancel();
});

Contributing #

We have some Developer Notes, but we're still learning too so they'll only help you so far, in fact there's probably a lot you can teach us!

Your pull requests are welcome but please keep them manageable and focused. Equally your input on any pull requests we have in flight at any given time is invaluable to us, so please get involved.

37
likes
0
pub points
91%
popularity

Publisher

verified publisherably.com

A wrapper around Ably's Cocoa and Java client library SDKs, providing iOS and Android support.

Repository (GitHub)
View/report issues

License

unknown (LICENSE)

Dependencies

collection, flutter, meta, pedantic

More

Packages that depend on ably_flutter