Micro Queue Butler

The Inspiration

Every day, we're surrounded by small waits such as laundry cycles, cooking timers, oven countdowns, microwave beeps. I found myself as a parent and partner managing my own home mentally juggling multiple timers, sometimes forgetting about them entirely especially laundry πŸ˜’. The cognitive load of tracking these "micro waits" was surprisingly draining.

I wanted to build something that would remember for me. Not just a simple timer app, but an intelligent assistant that learns my patterns. If I always wash my darks for 45 minutes, why should I have to set that timer every time?

The vision was simple: "Your phone knows what you're waiting for, so you don't have to."

What I Learned

Full-Stack Dart Development

Building both the frontend and backend in Dart was a revelation. Using Serverpod for the backend meant I could share types, leverage the same language paradigms, and maintain a cohesive mental model across the entire stack. The auto-generated API client eliminated the tedium of writing HTTP boilerplate.

Real-Time Communication Patterns

I dove deep into streaming architectures. The app doesn't just poll for updates, it maintains a persistent connection using Serverpod's message channels or streaming methods. When a timer completes on the server, clients receive the event instantly via pub/sub:

Channel: queue-completed-{userId}

The Mathematics of Smart Estimates

The "Smart Estimate" feature uses historical data to predict durations. The algorithm is elegantly simple. As users complete more tasks, the estimate converges toward their true preference.

Clean Architecture Principles

I implemented proper separation of concerns:

Layer Responsibility
Presentation UI, animations, user input
Domain Business logic, services
Data Repositories, API clients, data sources

This made the codebase testable and maintainable.

How I Built It

The Tech Stack

  • Frontend: Flutter 3.32+ with Provider for state management
  • Backend: Serverpod 3.2.3 (Dart-based backend framework)
  • Database: PostgreSQL 16 with pgvector extension
  • Cache: Redis for real-time capabilities
  • Infrastructure: Docker Compose for local development

The Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Flutter App                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Screens β†’ Repositories β†’ Data Sources β†’ Serverpod Clientβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
                           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  Serverpod Server                       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Endpoints β†’ Services β†’ Database Operations             β”‚
β”‚                    ↓                                    β”‚
β”‚            FutureCall System (scheduled tasks)          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Implementation Details

FutureCalls for Timer Completion: When a user creates a queue event, the server schedules a FutureCall to fire after the specified duration. This allows timer completion to happen server-side, even if the app is closed.

Real-Time Streaming: The app subscribes to a completion event stream:

client.queueEvent.streamCompletedEvents(userId)

Gamification Elements: I added streaks and statistics to encourage consistent usage; tracking consecutive days with completed tasks, weekly totals, and average wait times.

Challenges I Faced

1. Notification Reliability

Getting notifications to fire reliably when the app is in the background was tricky. I had to carefully orchestrate the server-side FutureCall system with client-side local notifications. The solution involved broadcasting completion events through Serverpod's message channels, which the Flutter app listens to and converts into local push notifications.

2. State Synchronization

Keeping the UI in sync with real-time server events required careful management of StreamSubscription lifecycles and proper disposal to prevent memory leaks. Pull-to-refresh also needed to coexist gracefully with streaming updates.

3. Smart Estimate Edge Cases

The averaging algorithm needed to handle:

  • Case-insensitive title matching
  • New tasks with no history (fallback to manual input)
  • Outliers that could skew the average

4. Animation Choreography

I wanted active timers to have a visual "pulse" (a blinking animation indicating they're counting down). Coordinating AnimationController instances with countdown timers and ensuring smooth UI updates every second took iteration to get right.

The Result

Micro Queue Butler is a functional, full-stack Dart application that demonstrates:

  • End-to-end type safety across client and server
  • Real-time streaming for instant updates
  • Background task scheduling with FutureCalls
  • Pattern-based intelligence that learns from user behavior
  • Clean architecture that scales

It solves a real problem I face daily and now using it for a couple of days my laundry never sits forgotten in the washer.


Built with Flutter & Serverpod for the Serverpod Hackathon

Built With

Share this project:

Updates