{"id":40049007,"url":"https://github.com/brewkits/flutter_debounce_throttle","last_synced_at":"2026-01-21T14:01:24.301Z","repository":{"id":332791172,"uuid":"1135012251","full_name":"brewkits/flutter_debounce_throttle","owner":"brewkits","description":"The Traffic Control System for your App Architecture. Unifies debounce, throttle, rate limiting \u0026 async concurrency control. Like ABS brakes for your app. Zero dependencies, 360+ tests, runs everywhere.","archived":false,"fork":false,"pushed_at":"2026-01-19T07:30:30.000Z","size":383,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-19T12:51:57.023Z","etag":null,"topics":["api-rate-limit","async-debounce","backpressure","batch-processing","concurrency-control","dart","dart-package","debounce","double-tap-prevention","event-throttling","flutter","flutter-package","pub-dev","rate-limiter","rate-limiting","throttle","token-bucket"],"latest_commit_sha":null,"homepage":"https://brewkits.dev","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brewkits.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-15T14:17:56.000Z","updated_at":"2026-01-19T07:30:34.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/brewkits/flutter_debounce_throttle","commit_stats":null,"previous_names":["brewkits/flutter_debounce_throttle"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/brewkits/flutter_debounce_throttle","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brewkits%2Fflutter_debounce_throttle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brewkits%2Fflutter_debounce_throttle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brewkits%2Fflutter_debounce_throttle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brewkits%2Fflutter_debounce_throttle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brewkits","download_url":"https://codeload.github.com/brewkits/flutter_debounce_throttle/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brewkits%2Fflutter_debounce_throttle/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28634785,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T04:47:28.174Z","status":"ssl_error","status_checked_at":"2026-01-21T04:47:22.943Z","response_time":86,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-rate-limit","async-debounce","backpressure","batch-processing","concurrency-control","dart","dart-package","debounce","double-tap-prevention","event-throttling","flutter","flutter-package","pub-dev","rate-limiter","rate-limiting","throttle","token-bucket"],"created_at":"2026-01-19T06:00:44.880Z","updated_at":"2026-01-21T14:01:24.295Z","avatar_url":"https://github.com/brewkits.png","language":"Dart","readme":"# flutter_debounce_throttle\n\n[![pub package](https://img.shields.io/pub/v/flutter_debounce_throttle.svg)](https://pub.dev/packages/flutter_debounce_throttle)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![Tests](https://img.shields.io/badge/tests-300%2B%20passed-brightgreen)](https://github.com/brewkits/flutter_debounce_throttle)\n[![Coverage](https://img.shields.io/badge/coverage-95%25-brightgreen)](https://github.com/brewkits/flutter_debounce_throttle)\n[![Pure Dart](https://img.shields.io/badge/pure-Dart-02569B)](https://dart.dev)\n\n## Enterprise-Grade Event Rate Limiter \u0026 Throttle Library\n\n\u003e The complete traffic control system for Flutter \u0026 Dart.\n\u003e Stop UI glitches, API spam, and race conditions with production-safe debounce, throttle, and rate limiting.\n\n\u003e Stop using manual Timers. They cause memory leaks and crashes.\n\u003e Switch to the production-grade event rate limiting library for Flutter \u0026 Dart.\n\nEnterprise-ready library unifying **debounce, throttle, rate limiting, and async concurrency control** into a single, battle-tested package with 360+ tests and 95% coverage.\n\n**Like ABS brakes for your app** — prevents crashes, stops memory leaks, handles edge cases automatically.\n\n```\n┌─────────────────────────────────────────────────────────────────────┐\n│                   flutter_debounce_throttle                         │\n├─────────────────────────────────────────────────────────────────────┤\n│  Debounce  │  Throttle  │  Rate Limit  │  Async Queue  │  Batch    │\n├─────────────────────────────────────────────────────────────────────┤\n│  Flutter UI  │  Dart Backend  │  CLI  │  Serverpod  │  Dart Frog  │\n└─────────────────────────────────────────────────────────────────────┘\n```\n\n### Core Values\n\n| | |\n|:---:|---|\n| **Universal** | Runs everywhere — Mobile, Web, Desktop, Server |\n| **Safety First** | No crashes, no memory leaks, lifecycle-aware |\n| **Zero Friction** | Simple API, no boilerplate, zero dependencies |\n\n---\n\n## Why You Need This\n\n### 📱 Mobile Problems\n\n| Problem | Impact | Solution |\n|---------|--------|----------|\n| **Phantom Clicks** | User taps \"Buy\" 10x → 10 orders → refund nightmare | `ThrottledInkWell` blocks duplicates |\n| **Battery Drain** | Search fires every keystroke → drains battery, burns data | `Debouncer` waits for typing pause |\n| **UI Jank** | Scroll events fire 60x/sec → laggy animations | `HighFrequencyThrottler` at 16ms |\n| **Race Conditions** | Old search results override new ones | `ConcurrencyMode.replace` cancels stale |\n\n### 🖥️ Server Problems\n\n| Problem | Impact | Solution |\n|---------|--------|----------|\n| **Cost Explosion** | Calling OpenAI/Maps API every request → $$$$ bill | `RateLimiter` controls outbound calls |\n| **Database Overload** | Writing logs one-by-one → DB locks up | `BatchThrottler` batches 100 writes → 1 |\n| **DDoS Vulnerability** | No rate limiting → server goes down | `RateLimiter` with Token Bucket |\n\n---\n\n## How It Works — Visualized\n\n### Throttle vs Debounce\n\nUnderstanding the core difference with `duration: 300ms`:\n\n#### ➤ Throttle (Button Clicks)\nExecutes **immediately**, then locks for the duration. Subsequent events are **ignored** during the lock.\n\n```\nEvents:    (Click1)    (Click2)    (Click3)              (Click4)\nTime:      |─ 0ms ─────── 100ms ──── 200ms ──── 300ms ──── 400ms ──|\n           ▼                                     ▲\nExecution: [EXECUTE] ····················· [LOCKED/DROP] ······· [EXECUTE]\n           └─────── 300ms cooldown ──────┘\n```\n\n**Use for:** Payment buttons, save buttons, scroll events\n\n---\n\n#### ➤ Debounce (Search Input)\nWaits for a **pause** in events for the duration before executing.\n\n```\nEvents:    (Type 'A')   (Type 'B')   (Type 'C')    [User stops typing]\nTime:      |─ 0ms ──── 100ms ──── 200ms ────────────── 500ms ──────|\n           ▼            ▼            ▼                  ▲\nExecution: [WAIT] ····· [RESET] ····· [RESET] ········ [EXECUTE 'ABC']\n                                      └─────── 300ms wait ──────┘\n```\n\n**Use for:** Search autocomplete, form validation, window resize\n\n---\n\n### Concurrency Modes (Async)\n\nHow overlapping async tasks are handled (example: two 500ms API calls):\n\n#### ➤ Mode: `drop` (Default for Throttle)\nIf busy, **new tasks are ignored** entirely.\n\n```\nTask 1:  [──────── 500ms API Call ────────]  ✅ Completes\nTask 2:            ↓ Try to start\n                   [DROPPED ❌]\nResult:  Only Task 1 runs. Task 2 is ignored.\n```\n\n**Use for:** Payment processing, file uploads\n\n---\n\n#### ➤ Mode: `replace` (Perfect for Search)\nThe new task **immediately cancels** the running task.\n\n```\nTask 1:  [──────── 500ms API Call ──X Cancelled\nTask 2:              ↓ New task starts\n                     [──────── 500ms API Call ────────]  ✅ Completes\nResult:  Task 1 cancelled. Only Task 2's result is used.\n```\n\n**Use for:** Search autocomplete, switching tabs, real-time filters\n\n---\n\n#### ➤ Mode: `enqueue` (Queue)\nTasks **wait in line** for their turn.\n\n```\nTask 1:  [──────── 500ms ────────]  ✅\nTask 2:            ↓ Queued\n                   [Waiting...]      [──────── 500ms ────────]  ✅\nResult:  Task 1 runs, then Task 2 runs immediately after.\n```\n\n**Use for:** Chat messages, notification queue, ordered operations\n\n---\n\n#### ➤ Mode: `keepLatest` (Current + Last Only)\nOnly keeps the **current running task** and **one latest queued task**.\n\n```\nTask 1:  [──────── 500ms ────────]  ✅\nTask 2:            ↓ Queued\nTask 3:                      ↓ Replaces Task 2 in queue\n                             [Waiting...]      [──────── 500ms ────────]  ✅\nResult:  Task 1 runs, Task 2 is dropped, Task 3 runs after Task 1.\n```\n\n**Use for:** Auto-save, data sync, real-time updates\n\n---\n\n## Solution Matrix\n\n*\"What should I use for...?\"*\n\n| Environment | Use Case | Solution | Why It's Better |\n|-------------|----------|----------|-----------------|\n| **Flutter UI** | Button Click | `ThrottledBuilder` | Auto loading state, auto dispose |\n| **Flutter UI** | Search Input | `DebouncedTextController` | One line, integrates with TextField |\n| **State Mgmt** | Provider/Bloc/GetX | `EventLimiterMixin` | No manual Timer management |\n| **Streams** | Socket/Sensor data | `StreamDebounceListener` | Auto-cancel subscription |\n| **Hooks** | Functional widgets | `useDebouncedCallback` | No nested widgets, clean code |\n| **Server** | Batch DB writes | `BatchThrottler` | 100x fewer DB calls |\n| **Server** | Rate limit API | `RateLimiter` | Token Bucket algorithm |\n\n---\n\n## Why Not Just Use easy_debounce or rxdart?\n\n| Capability | This Library | easy_debounce | rxdart | Manual Timer |\n|------------|:---:|:---:|:---:|:---:|\n| Debounce \u0026 Throttle | ✅ | ✅ | ✅ | ⚠️ Boilerplate |\n| **Memory Safe** (Auto-dispose) | ✅ | ❌ | ⚠️ Manual | ❌ Leaky |\n| **Async \u0026 Future Support** | ✅ | ❌ | ✅ | ❌ |\n| **Concurrency Control** (4 modes) | ✅ | ❌ | ⚠️ Complex | ❌ |\n| **Rate Limiter** (Token Bucket) | ✅ | ❌ | ❌ | ❌ |\n| **Server-side** (Pure Dart) | ✅ | ❌ | ❌ | ✅ |\n| **Flutter Widgets** | ✅ | ❌ | ❌ | ❌ |\n| **State Management Mixin** | ✅ | ❌ | ❌ | ❌ |\n| Dependencies | **0** | 0 | Many | 0 |\n\n\u003e **One library. All use cases. Zero compromises.**\n\n---\n\n## 🧹 Memory Management (NEW in v2.3.0)\n\n### The Problem: Dynamic IDs Can Leak Memory\n\n```dart\n// ⚠️ This pattern can leak memory:\nclass InfiniteScrollController with EventLimiterMixin {\n  void onPostLike(String postId) {\n    debounce('like_$postId', () =\u003e api.like(postId));  // New limiter per post!\n  }\n}\n// User scrolls through 1000+ posts → 1000+ limiters → OOM crash\n```\n\n### The Solution: Auto-Cleanup (Enabled by Default)\n\n**v2.3.0+ automatically cleans up unused limiters:**\n- Limiters unused for **10+ minutes** are auto-removed\n- Cleanup triggers when limiter count exceeds **100**\n- **No configuration needed** - works out of the box!\n\n```dart\n// ✅ This is now safe by default:\nclass SafeController with EventLimiterMixin {\n  void onPostLike(String postId) {\n    debounce('like_$postId', () =\u003e api.like(postId));\n    // Old limiters auto-cleanup after 10 minutes of inactivity\n  }\n}\n```\n\n### Customization (Optional)\n\n```dart\nvoid main() {\n  // Customize TTL and threshold\n  DebounceThrottleConfig.init(\n    limiterAutoCleanupTTL: Duration(minutes: 5),    // Faster cleanup\n    limiterAutoCleanupThreshold: 50,                // More aggressive\n  );\n  runApp(MyApp());\n}\n```\n\n**Learn more:** [Best Practices - Memory Management](docs/BEST_PRACTICES.md#memory-management)\n\n---\n\n## 5-Second Start\n\nJust need a throttled button? **One line:**\n\n```dart\nThrottledInkWell(onTap: () =\u003e pay(), child: Text('Pay'))\n```\n\nJust need debounced search? **One line:**\n\n```dart\nTextField(onChanged: (s) =\u003e debouncer(() =\u003e search(s)))\n```\n\nThat's it. No setup. No dispose. Works immediately.\n\n---\n\n## Quick Start by Level\n\n### 🟢 Basic — Just Works\n\n**Anti-Spam Button** (prevents double-tap)\n```dart\nThrottledInkWell(\n  duration: 500.ms,\n  onTap: () =\u003e processPayment(),\n  child: Text('Pay \\$99'),\n)\n```\n\n**Debounced Search** (waits for typing pause)\n```dart\nfinal debouncer = Debouncer(duration: 300.ms);\n\nTextField(\n  onChanged: (text) =\u003e debouncer(() =\u003e search(text)),\n)\n```\n\n### 🟡 Intermediate — More Control\n\n**Async with Loading State**\n```dart\nAsyncThrottledBuilder(\n  builder: (context, throttle) =\u003e ElevatedButton(\n    onPressed: throttle(() async =\u003e await submitForm()),\n    child: Text('Submit'),\n  ),\n)\n```\n\n**Cancel Stale Requests** (search autocomplete)\n```dart\nfinal controller = ConcurrentAsyncThrottler(mode: ConcurrencyMode.replace);\n\nvoid onSearch(String query) {\n  controller(() async {\n    final results = await api.search(query);  // Old requests auto-cancelled\n    updateUI(results);\n  });\n}\n```\n\n### 🔴 Advanced — Enterprise Features\n\n**Server-Side Batching** (100x fewer DB writes)\n```dart\nfinal batcher = BatchThrottler(\n  duration: 2.seconds,\n  maxBatchSize: 50,\n  onBatchExecute: (logs) =\u003e database.insertBatch(logs),\n);\n\nbatcher(() =\u003e logEntry);  // 1000 calls → 20 batches\n```\n\n**Token Bucket Rate Limiting** (API cost control)\n```dart\nfinal limiter = RateLimiter(maxTokens: 100, refillRate: 10);\n\nif (!limiter.tryAcquire()) {\n  return Response.tooManyRequests();\n}\n```\n\n---\n\n## Coming from easy_debounce?\n\nMigration takes 2 minutes. You get memory safety for free.\n\n```dart\n// Before (easy_debounce) - manual cancel, possible memory leak\nEasyDebounce.debounce('search', Duration(ms: 300), () =\u003e search(q));\n\n// After - auto-dispose, lifecycle-aware\nfinal debouncer = Debouncer(duration: 300.ms);\ndebouncer(() =\u003e search(q));\n```\n\nSee full [Migration Guide](MIGRATION_GUIDE.md) →\n\n---\n\n## Installation\n\n```yaml\n# Flutter App\ndependencies:\n  flutter_debounce_throttle: ^2.0.0\n\n# Flutter + Hooks\ndependencies:\n  flutter_debounce_throttle_hooks: ^2.0.0\n\n# Pure Dart (Server, CLI)\ndependencies:\n  dart_debounce_throttle: ^2.0.0\n```\n\n---\n\n## Quality Assurance\n\n| Guarantee | How |\n|-----------|-----|\n| **Stability** | 360+ tests, 95% coverage |\n| **Type Safety** | No `dynamic`, full generic support |\n| **Lifecycle Safe** | Auto-checks `mounted`, auto-cancel on dispose |\n| **Memory Safe** | Zero leaks (verified with LeakTracker) |\n| **Zero Dependencies** | Only `meta` package in core |\n\n---\n\n## Documentation\n\n| | |\n|---|---|\n| [**FAQ**](FAQ.md) | Common questions answered |\n| [**API Reference**](docs/API_REFERENCE.md) | Complete API documentation |\n| [**Best Practices**](docs/BEST_PRACTICES.md) | Patterns \u0026 recommendations |\n| [**Migration Guide**](MIGRATION_GUIDE.md) | From easy_debounce, rxdart |\n| [**Examples**](example/) | Interactive demos |\n\n---\n\n## Ecosystem\n\n| Package | Platform | Use Case |\n|---------|----------|----------|\n| [`flutter_debounce_throttle`](https://pub.dev/packages/flutter_debounce_throttle) | Flutter | Widgets, Mixin |\n| [`flutter_debounce_throttle_hooks`](https://pub.dev/packages/flutter_debounce_throttle_hooks) | Flutter + Hooks | useDebouncer, useThrottler |\n| [`dart_debounce_throttle`](https://pub.dev/packages/dart_debounce_throttle) | Pure Dart | Server, CLI, anywhere |\n\n---\n\n## Roadmap\n\nWe're committed to long-term maintenance and improvement.\n\n| Version | Status | Features |\n|---------|--------|----------|\n| **v1.0** | ✅ Released | Core debounce/throttle, widgets, mixin |\n| **v1.1** | ✅ Released | RateLimiter, extensions, leading/trailing edge, batch limits |\n| **v2.0** | ✅ Released | Package rename to dart_debounce_throttle, improved documentation |\n| **v2.2** | ✅ Released | Error handling (onError callbacks), TTL auto-cleanup, performance optimization |\n| **v2.3** | 🔜 Planned | Retry policies, circuit breaker pattern |\n| **v2.x** | 📋 Roadmap | Web Workers support, isolate-safe controllers |\n\nHave a feature request? [Open an issue](https://github.com/brewkits/flutter_debounce_throttle/issues)\n\n---\n\n\u003cp align=\"center\"\u003e\n  \u003cb\u003e300+ tests\u003c/b\u003e · \u003cb\u003eZero dependencies\u003c/b\u003e · \u003cb\u003eType-safe\u003c/b\u003e · \u003cb\u003eProduction-ready\u003c/b\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  Made with craftsmanship by \u003ca href=\"https://github.com/brewkits\"\u003eBrewkits\u003c/a\u003e\n\u003c/p\u003e\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrewkits%2Fflutter_debounce_throttle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrewkits%2Fflutter_debounce_throttle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrewkits%2Fflutter_debounce_throttle/lists"}