Implementing Redis On Flutter Applications

Implementing Redis On Flutter Applications

Learn about the basics of implementing Redis of Flutter applications while exploring some of its base functionalities

ยท

6 min read

Redis is an open-source BSC licensed database that can be used to cache, message broker, and pub/sub-services. Being is a super-fast key-value store, it also provides data structures such as strings, lists, sets, hashes, bitmaps, hyperlogs, geospatial indexes, and streams.

In this article, we will see how to integrate and use Redis on your Flutter application. In the end, we will explore some use cases on how the technology can be used to increase an app's performance.

Redis in Dart

We will be using dartis package for creating our Redis client; you can also use the redis package as an alternative solution.

Features:

Here are the features available in the dartis package:

  • Run type-safe commands
  • Pub/Sub
  • Pipelining
  • Fire and forget
  • Transactions
  • Monitor mode
  • We will go through each of these features, and see how to implement them. To keep it standard and reusable, we will create a redis_service.dart file and create a class for RedisService using the code given below:
import 'package:dartis/dartis.dart';

class RedisService {
  RedisService._();
  static RedisService _instance = RedisService._();

  factory RedisService() => _instance;

  // Variables
  Client client;
  Commands commands;

  init() async {
    client =
        await Client.connect('redis://localhost:6379'); // Connection String
    commands = client.asCommands<String, String>();
  }

  disconnect() async {
    await client.disconnect();
  }
}
  • Here, we have created a singleton class for RedisService because of which we don't have to initialize the concerned class and make connections every single time we use a Redis feature. In this case, we have the init() function which can be called when our app starts. By using this function, we can call Client.connect from dartis and pass it through the connection string which is dependent on where your Redis server is located. If you have a remote Redis server, then enter your remote IP followed by the port number; but if you are testing this function locally, you have to use redis://localhost:6379.

Run type-safe commands

  • After creating a connection with Redis, we can call asCommads and assign it with a type which will return a Commands object. This connection can later be used to make get and set calls. Here is an example of how you can create get and set methods using the RedisService:
  Future<dynamic> get(String key) async {
    final value = await commands.get(key);
    return value;
  }

  Future<void> set(String key, String value) async {
    await commands.set(key, value);
  }

Pub/Sub

  • Redis pub/sub mode only allows subscribe, unsubscribe, psubscribe, punsubscribe, ping and quit. To create a Pub/sub stream, we need to create a connection with Redis again as the client that we have created cannot be used for Pub/Sub. Instead, we have to create a new Pubsub object and initialize it separately. Below is an example for your reference:
  PubSub pubsub;

  connectPubSub() async {
    pubsub = await PubSub.connect<String, String>('redis://localhost:6379');
  }

  Future<Stream<PubSubEvent>> subscribe() async {
    pubsub.subscribe(channel: "demo.redis");
    pubsub.psubscribe(pattern: 'demo.*');
    return pubsub.stream;
  }

  Future<void> publish() async {
    await commands.publish("demo.redis", "Hello World");
  }

  unsunscribe() {
    pubsub.unsubscribe();
  }
  • In the above code, we shall call the createPubSub() function and connect it to our Redis server as we did for the client connection. The subscribe() function can be used to subscribe to any specific channel and return the Stream<PubSubEvent> which can be used to listen for events.

Note: We have to use the client object in order to publish.

Pipelining

  • Pipelining in Redis is used when there are numerous calls to be made via the client but the intention is not to send multiple commands to the server. Instead, we can create a pipeline of commands which will store all the commands locally and which we will be able to utilise when the flush method is called. Use the code given below to proceed:
  initPipeline() {
    client.pipeline();
  }

  flushPipeline() {
    client.flush();
  }
  • To use pipelining in Redis, we simply have to call the pipeline method on the client proceeding which the commands are run.

Fire and forget

  • Fire and forget is a Redis mode where the technology will not send any reply for the commands that are sent by the client. This mode can be used by setting clientReply with ReplyMode.off. Use the following code to proceed:
  turnFireAndForgetOn() async {
    await commands.clientReply(ReplyMode.off);
  }

  turnFireAndForgetOf() async {
    await commands.clientReply(ReplyMode.on);
  }
  final res = await commands.set("Test", "Reply");
  print(res) 
  // null

Transactions

  • We can make transactions in Redis which quite similar to the ones in SQL where we can group the commands together and run them as a single transaction. We can start a transaction by calling commands.multi() and end the transaction by calling commands.exec() shown below:
// Start transaction
await commands.multi();

// Run some commands
commands.set(key, 1).then(print);
commands.incr(key).then(print);

// End transaction
await commands.exec(); // Or abort: commands.discard()
  • We can call the watch command before await commands.multi(); for optimistic locking certain keys as there are chances of the transaction failing in case of key changes.
await commands.watch(key: "somekey");

Monitor mode

  • Monitor mode can be used to listen to all the commands processed by the Redis server. This mode provides a steady stream of events which will be triggered every time the server executes any command as shown below:
  // Monitor mode
  Future<Stream<List<int>>> startMonitorMode() async {
    monitor = await Monitor.connect('redis://localhost:6379');
    monitor.start();
    return monitor.stream;
  }
  • Here is the complete code for RedisService which can be used directly in your project:
import 'package:dartis/dartis.dart';

class RedisService {
  RedisService._();
  static RedisService _instance = RedisService._();

  factory RedisService() => _instance;

  // Variables
  Client client;
  Commands commands;
  PubSub pubsub;
  Monitor monitor;

  init() async {
    client =
        await Client.connect('redis://localhost:6379'); // Connection String
    commands = client.asCommands<String, String>();
  }

  Future<dynamic> get(String key) async {
    final value = await commands.get(key);
    return value;
  }

  Future<void> set(String key, String value) async {
    await commands.set(key, value);
  }

  // Pub/Sub
  connectPubSub() async {
    pubsub = await PubSub.connect<String, String>('redis://localhost:6379');
  }

  Future<Stream<PubSubEvent>> subscribe() async {
    pubsub.subscribe(channel: "demo.redis");
    pubsub.psubscribe(pattern: 'demo.*');
    return pubsub.stream;
  }

  Future<void> publish() async {
    await commands.publish("demo.redis", "Hello World");
  }

  unsunscribe() {
    pubsub.unsubscribe();
  }

  // Pipelining
  initPipeline() {
    client.pipeline();
  }

  flushPipeline() {
    client.flush();
  }

  // Fire and forget
  turnFireAndForgetOn() async {
    await commands.clientReply(ReplyMode.off);
  }

  turnFireAndForgetOf() async {
    await commands.clientReply(ReplyMode.on);
  }

  // Monitor mode
  Future<Stream<List<int>>> startMonitorMode() async {
    monitor = await Monitor.connect('redis://localhost:6379');
    monitor.start();
    return monitor.stream;
  }

  disconnect() async {
    await client.disconnect();
  }
}

Use cases of Redis:

  • Redis can be used for persistent caching sessions on the server.
  • Redis can be used along with IoT devices in which case the bandwidth usage is critical. Making lots of calls to the server to send data periodically can be hectic; that's where the Redis pipeline can be used to group a bunch of requests and execute commands.
  • Redis can be used for leaderboard/counting because it does an amazing job at increments and decrements as it stores data in-memory. Sets and sorted sets are other provisions making it easy to manage these kinds of operations.
  • The Redis Pub/Sub feature is boundless and can be used for social network connections, triggering scripts on your server through pub/sub events and even implementing a chat system.
  • Redis can be used for speeding up the online gaming experience. Because of its low latency, one can achieve both high performance and a virtually unlimited scale, both of which are critical for gaming situations where large volumes of data are required at high speed.

Conclusion

We have seen how easy it is to get started with implementing Redis on your Flutter application as all one has to do is make connections with the client and call the relevant methods. There is no doubt that using Redis on your Flutter app could be helpful for scaling up your app performance, especially if your app depends on data from the database.

That's all for Redis and Flutter. Thank you so much for reading ๐Ÿ˜. If you like this article, then please leave a ๐Ÿ‘ and come say hi on Twitter.

ย