Skip to content

The Traffic Control System for your App Architecture. Unifies debounce, throttle, rate limiting & async concurrency control. Like ABS brakes for your app. Zero dependencies, 360+ tests, runs everywhere.

License

Notifications You must be signed in to change notification settings

brewkits/flutter_debounce_throttle

Repository files navigation

flutter_debounce_throttle

pub package License: MIT Tests Coverage Pure Dart

The Traffic Control System for Your App Architecture

Stop using manual Timers. They cause memory leaks and crashes. Switch to the standard traffic control system for Flutter & Dart.

Production-ready library unifying debounce, throttle, rate limiting, and async concurrency control into a single, battle-tested package. Like ABS brakes for your app — smooth, safe, and automatic.

┌─────────────────────────────────────────────────────────────────────┐
│                   flutter_debounce_throttle                         │
├─────────────────────────────────────────────────────────────────────┤
│  Debounce  │  Throttle  │  Rate Limit  │  Async Queue  │  Batch    │
├─────────────────────────────────────────────────────────────────────┤
│  Flutter UI  │  Dart Backend  │  CLI  │  Serverpod  │  Dart Frog  │
└─────────────────────────────────────────────────────────────────────┘

Core Values

Universal Runs everywhere — Mobile, Web, Desktop, Server
Safety First No crashes, no memory leaks, lifecycle-aware
Zero Friction Simple API, no boilerplate, zero dependencies

Why You Need This

📱 Mobile Problems

Problem Impact Solution
Phantom Clicks User taps "Buy" 10x → 10 orders → refund nightmare ThrottledInkWell blocks duplicates
Battery Drain Search fires every keystroke → drains battery, burns data Debouncer waits for typing pause
UI Jank Scroll events fire 60x/sec → laggy animations HighFrequencyThrottler at 16ms
Race Conditions Old search results override new ones ConcurrencyMode.replace cancels stale

🖥️ Server Problems

Problem Impact Solution
Cost Explosion Calling OpenAI/Maps API every request → $$$$ bill RateLimiter controls outbound calls
Database Overload Writing logs one-by-one → DB locks up BatchThrottler batches 100 writes → 1
DDoS Vulnerability No rate limiting → server goes down RateLimiter with Token Bucket

How It Works — Visualized

Throttle vs Debounce

Understanding the core difference with duration: 300ms:

➤ Throttle (Button Clicks)

Executes immediately, then locks for the duration. Subsequent events are ignored during the lock.

Events:    (Click1)    (Click2)    (Click3)              (Click4)
Time:      |─ 0ms ─────── 100ms ──── 200ms ──── 300ms ──── 400ms ──|
           ▼                                     ▲
Execution: [EXECUTE] ····················· [LOCKED/DROP] ······· [EXECUTE]
           └─────── 300ms cooldown ──────┘

Use for: Payment buttons, save buttons, scroll events


➤ Debounce (Search Input)

Waits for a pause in events for the duration before executing.

Events:    (Type 'A')   (Type 'B')   (Type 'C')    [User stops typing]
Time:      |─ 0ms ──── 100ms ──── 200ms ────────────── 500ms ──────|
           ▼            ▼            ▼                  ▲
Execution: [WAIT] ····· [RESET] ····· [RESET] ········ [EXECUTE 'ABC']
                                      └─────── 300ms wait ──────┘

Use for: Search autocomplete, form validation, window resize


Concurrency Modes (Async)

How overlapping async tasks are handled (example: two 500ms API calls):

➤ Mode: drop (Default for Throttle)

If busy, new tasks are ignored entirely.

Task 1:  [──────── 500ms API Call ────────]  ✅ Completes
Task 2:            ↓ Try to start
                   [DROPPED ❌]
Result:  Only Task 1 runs. Task 2 is ignored.

Use for: Payment processing, file uploads


➤ Mode: replace (Perfect for Search)

The new task immediately cancels the running task.

Task 1:  [──────── 500ms API Call ──X Cancelled
Task 2:              ↓ New task starts
                     [──────── 500ms API Call ────────]  ✅ Completes
Result:  Task 1 cancelled. Only Task 2's result is used.

Use for: Search autocomplete, switching tabs, real-time filters


➤ Mode: enqueue (Queue)

Tasks wait in line for their turn.

Task 1:  [──────── 500ms ────────]  ✅
Task 2:            ↓ Queued
                   [Waiting...]      [──────── 500ms ────────]  ✅
Result:  Task 1 runs, then Task 2 runs immediately after.

Use for: Chat messages, notification queue, ordered operations


➤ Mode: keepLatest (Current + Last Only)

Only keeps the current running task and one latest queued task.

Task 1:  [──────── 500ms ────────]  ✅
Task 2:            ↓ Queued
Task 3:                      ↓ Replaces Task 2 in queue
                             [Waiting...]      [──────── 500ms ────────]  ✅
Result:  Task 1 runs, Task 2 is dropped, Task 3 runs after Task 1.

Use for: Auto-save, data sync, real-time updates


Solution Matrix

"What should I use for...?"

Environment Use Case Solution Why It's Better
Flutter UI Button Click ThrottledBuilder Auto loading state, auto dispose
Flutter UI Search Input DebouncedTextController One line, integrates with TextField
State Mgmt Provider/Bloc/GetX EventLimiterMixin No manual Timer management
Streams Socket/Sensor data StreamDebounceListener Auto-cancel subscription
Hooks Functional widgets useDebouncedCallback No nested widgets, clean code
Server Batch DB writes BatchThrottler 100x fewer DB calls
Server Rate limit API RateLimiter Token Bucket algorithm

Why Not Just Use easy_debounce or rxdart?

Capability This Library easy_debounce rxdart Manual Timer
Debounce & Throttle ⚠️ Boilerplate
Memory Safe (Auto-dispose) ⚠️ Manual ❌ Leaky
Async & Future Support
Concurrency Control (4 modes) ⚠️ Complex
Rate Limiter (Token Bucket)
Server-side (Pure Dart)
Flutter Widgets
State Management Mixin
Dependencies 0 0 Many 0

One library. All use cases. Zero compromises.


5-Second Start

Just need a throttled button? One line:

ThrottledInkWell(onTap: () => pay(), child: Text('Pay'))

Just need debounced search? One line:

TextField(onChanged: (s) => debouncer(() => search(s)))

That's it. No setup. No dispose. Works immediately.


Quick Start by Level

🟢 Basic — Just Works

Anti-Spam Button (prevents double-tap)

ThrottledInkWell(
  duration: 500.ms,
  onTap: () => processPayment(),
  child: Text('Pay \$99'),
)

Debounced Search (waits for typing pause)

final debouncer = Debouncer(duration: 300.ms);

TextField(
  onChanged: (text) => debouncer(() => search(text)),
)

🟡 Intermediate — More Control

Async with Loading State

AsyncThrottledBuilder(
  builder: (context, throttle) => ElevatedButton(
    onPressed: throttle(() async => await submitForm()),
    child: Text('Submit'),
  ),
)

Cancel Stale Requests (search autocomplete)

final controller = ConcurrentAsyncThrottler(mode: ConcurrencyMode.replace);

void onSearch(String query) {
  controller(() async {
    final results = await api.search(query);  // Old requests auto-cancelled
    updateUI(results);
  });
}

🔴 Advanced — Enterprise Features

Server-Side Batching (100x fewer DB writes)

final batcher = BatchThrottler(
  duration: 2.seconds,
  maxBatchSize: 50,
  onBatchExecute: (logs) => database.insertBatch(logs),
);

batcher(() => logEntry);  // 1000 calls → 20 batches

Token Bucket Rate Limiting (API cost control)

final limiter = RateLimiter(maxTokens: 100, refillRate: 10);

if (!limiter.tryAcquire()) {
  return Response.tooManyRequests();
}

Coming from easy_debounce?

Migration takes 2 minutes. You get memory safety for free.

// Before (easy_debounce) - manual cancel, possible memory leak
EasyDebounce.debounce('search', Duration(ms: 300), () => search(q));

// After - auto-dispose, lifecycle-aware
final debouncer = Debouncer(duration: 300.ms);
debouncer(() => search(q));

See full Migration Guide


Installation

# Flutter App
dependencies:
  flutter_debounce_throttle: ^2.0.0

# Flutter + Hooks
dependencies:
  flutter_debounce_throttle_hooks: ^2.0.0

# Pure Dart (Server, CLI)
dependencies:
  dart_debounce_throttle: ^2.0.0

Quality Assurance

Guarantee How
Stability 360+ tests, 95% coverage
Type Safety No dynamic, full generic support
Lifecycle Safe Auto-checks mounted, auto-cancel on dispose
Memory Safe Zero leaks (verified with LeakTracker)
Zero Dependencies Only meta package in core

Documentation

FAQ Common questions answered
API Reference Complete API documentation
Best Practices Patterns & recommendations
Migration Guide From easy_debounce, rxdart
Examples Interactive demos

Ecosystem

Package Platform Use Case
flutter_debounce_throttle Flutter Widgets, Mixin
flutter_debounce_throttle_hooks Flutter + Hooks useDebouncer, useThrottler
dart_debounce_throttle Pure Dart Server, CLI, anywhere

Roadmap

We're committed to long-term maintenance and improvement.

Version Status Features
v1.0 ✅ Released Core debounce/throttle, widgets, mixin
v1.1 ✅ Released RateLimiter, extensions, leading/trailing edge, batch limits
v2.0 ✅ Released Package rename to dart_debounce_throttle, improved documentation
v2.1 🔜 Planned Retry policies, circuit breaker pattern
v2.x 📋 Roadmap Web Workers support, isolate-safe controllers

Have a feature request? Open an issue


360+ tests · Zero dependencies · Type-safe · Production-ready

Made with craftsmanship by Brewkits

About

The Traffic Control System for your App Architecture. Unifies debounce, throttle, rate limiting & async concurrency control. Like ABS brakes for your app. Zero dependencies, 360+ tests, runs everywhere.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages