When to Use the redis_task_queue Flutter Package

The redis_task_queue Flutter package is a lightweight, Redis‑backed task queue designed for server‑side Dart. It shines when you need to:

  • Offload work from the UI thread to a background worker.
  • Schedule jobs for later execution or retry failed jobs automatically.
  • Maintain a dead‑letter list for jobs that exceed retry limits.
  • Implement weighted queues to prioritize critical tasks.

Because the package talks directly to a Redis instance, it is best suited for apps that already use Redis for caching, session storage, or pub/sub. If your Flutter project runs on mobile devices without a Redis server, consider using a cloud function or a backend service that hosts the queue.

Installation

Add the package to your pubspec.yaml with the official Flutter command:

Code
flutter pub add redis_task_queue

After the command completes, run flutter pub get to fetch the dependency.

Basic Setup and Configuration

Before you can enqueue or process jobs, you need a running Redis server and the connection details (host, port, optional password). Store these values securely, for example in .env files or using the flutter_dotenv package.

Tip: Keep the Redis connection logic inside a dedicated service class. This isolates third‑party code and makes unit testing easier.

Creating a Queue Service

Code
import 'package:redis_task_queue/redis_task_queue.dart';
import 'package:dotenv/dotenv.dart' show load, env;

class QueueService {
  late final RedisTaskQueue _queue;

  QueueService() {
    // Load environment variables (ensure you call load() early in main())
    final host = env['REDIS_HOST'] ?? '127.0.0.1';
    final port = int.tryParse(env['REDIS_PORT'] ?? '6379') ?? 6379;
    final password = env['REDIS_PASSWORD'];

    // Initialise the Redis client used by the package
    final client = RedisTaskQueueClient(
      host: host,
      port: port,
      password: password,
    );

    // Create a named queue; you can have multiple queues for different priorities
    _queue = RedisTaskQueue(client: client, queueName: 'flutter_jobs');
  }

  Future enqueueJob(String taskName, Map payload) async {
    await _queue.enqueue(taskName, payload);
  }

  // Example of scheduling a job 5 minutes from now
  Future scheduleJob(String taskName, Map payload) async {
    final scheduleTime = DateTime.now().add(Duration(minutes: 5));
    await _queue.schedule(taskName, payload, scheduleTime);
  }
}

The above service wraps the core API and can be injected wherever you need to enqueue work (e.g., from a Bloc, Provider, or Riverpod provider).

Processing Jobs with a Worker

In a production setup you typically run a separate Dart process (or a server‑side isolate) that continuously pulls jobs from the queue and executes them. Below is a minimal worker implementation that you can run on a server or in a background isolate.

Code
import 'package:redis_task_queue/redis_task_queue.dart';
import 'dart:async';

Future main() async {
  // Re‑use the same connection configuration as the QueueService
  final client = RedisTaskQueueClient(host: '127.0.0.1', port: 6379);
  final worker = RedisTaskQueueWorker(
    client: client,
    queueName: 'flutter_jobs',
    // Define how many jobs to fetch in one batch (optional)
    batchSize: 10,
    // Optional: maximum number of retries before moving to dead‑letter list
    maxRetries: 3,
    // Provide a handler that knows how to process each task type
    handler: (String taskName, Map payload) async {
      switch (taskName) {
        case 'sendEmail':
          await _sendEmail(payload);
          break;
        case 'generateReport':
          await _generateReport(payload);
          break;
        default:
          print('Unknown task: $taskName');
      }
    },
  );

  // Start the infinite processing loop
  await worker.start();
}

Future _sendEmail(Map data) async {
  // Placeholder implementation – replace with real email logic
  print('Sending email to ${data['to']} with subject ${data['subject']}');
}

Future _generateReport(Map data) async {
  // Placeholder implementation – replace with real report generation
  print('Generating report for user ${data['userId']}');
}

The RedisTaskQueueWorker handles retries, moves failed jobs to a dead‑letter list, and respects the queue's weighting if you configure multiple queues.

Common Mistakes to Avoid

  • Running the worker on the UI thread. The worker blocks while waiting for jobs. Always run it in a separate isolate, server process, or background service.
  • Hard‑coding Redis credentials. Store them securely and never commit them to version control.
  • Ignoring serialization errors. The package stores payloads as JSON. Ensure your data structures are JSON‑serializable.
  • Assuming the package works on the client‑only side. It is intended for server‑side Dart. If you need a pure client solution, look for a different queue strategy.

Testing the Integration

Because the queue interacts with an external Redis instance, write integration tests that spin up a temporary Redis container (e.g., using Docker) and verify enqueue/dequeue behavior. Mocking the RedisTaskQueueClient is also possible for unit tests.

FAQ

  • Q: Can I use redis_task_queue directly in a mobile Flutter app?

    A: Technically you can, but the package expects a persistent Redis connection. Mobile networks are unreliable, and embedding a Redis server in a mobile app is impractical. The recommended pattern is to call a backend API that enqueues jobs on your behalf.

  • Q: How do I monitor the dead‑letter list?

    A: The package provides a deadLetterQueue accessor. You can query it with await _queue.deadLetterQueue.getAll() or use Redis CLI commands like LRANGE dead_letter:flutter_jobs 0 -1.

  • Q: Does the package support job prioritization?

    A: Yes, you can create multiple queues with different names and assign weights when pulling jobs. The worker can be configured with a queueWeights map to prioritize certain queues.

  • Q: What happens if the Redis server goes down?

    A: Enqueue calls will throw a connection error. It is advisable to wrap calls in try/catch and implement exponential back‑off or fallback logic.

Next Steps

After you have a stable worker running, consider adding:

  • Metrics collection (e.g., job latency, success/failure counts).
  • Graceful shutdown handling for the worker process.
  • Integration with your existing logging and monitoring stack.

For the most up‑to‑date API surface, always refer to the official package page on pub.dev.

Frequently Asked Questions

Can I use redis_task_queue directly in a mobile Flutter app?

Technically you can, but the package expects a persistent Redis connection. Mobile networks are unreliable, and embedding a Redis server in a mobile app is impractical. The recommended pattern is to call a backend API that enqueues jobs on your behalf.

How do I monitor the dead‑letter list?

The package provides a deadLetterQueue accessor. You can query it with await _queue.deadLetterQueue.getAll() or use Redis CLI commands like LRANGE dead_letter:your_queue 0 -1.

Does the package support job prioritization?

Yes, you can create multiple queues with different names and assign weights when pulling jobs. The worker can be configured with a queueWeights map to prioritize certain queues.

What happens if the Redis server goes down?

Enqueue calls will throw a connection error. Wrap calls in try/catch and implement exponential back‑off or fallback logic to handle temporary outages.