Implementing Redis On Flutter Applications
Learn about the basics of implementing Redis of Flutter applications while exploring some of its base functionalities
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 forRedisService
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 theinit()
function which can be called when our app starts. By using this function, we can callClient.connect
fromdartis
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 useredis://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 aCommands
object. This connection can later be used to makeget
andset
calls. Here is an example of how you can createget
andset
methods using theRedisService
:
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
andquit
. 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 newPubsub
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. Thesubscribe()
function can be used to subscribe to any specific channel and return theStream<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 theflush
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 theclient
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 settingclientReply
withReplyMode.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 callingcommands.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 beforeawait 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.