# Implementing Redis On Flutter Applications

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](https://pub.dev/packages/dartis) package for creating our Redis client; you can also use the [redis](https://pub.dev/packages/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:

```dart
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`:

```dart
  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:

```dart
  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:

```dart
  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:

```dart
  turnFireAndForgetOn() async {
    await commands.clientReply(ReplyMode.off);
  }

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

```dart
  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:

```dart
// 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.

```dart
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:

```dart
  // 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:

```dart
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](https://twitter.com/viral_sangani_).
