{"id":37057656,"url":"https://github.com/vietnguyentuan2019/flutter_event_limiter","last_synced_at":"2026-01-14T06:31:18.318Z","repository":{"id":326721835,"uuid":"1106612474","full_name":"vietnguyentuan2019/flutter_event_limiter","owner":"vietnguyentuan2019","description":"A powerful event handling library. Prevents double-clicks, fixes async race conditions, and manages loading states.","archived":false,"fork":false,"pushed_at":"2025-12-13T01:27:36.000Z","size":135,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-14T06:28:05.156Z","etag":null,"topics":["anti-spam","async","builder-pattern","dart","debounce","double-click-prevention","event-handling","flutter","flutter-package","loading-state","memory-leak-prevention","race-condition","rate-limiting","throttle","widget"],"latest_commit_sha":null,"homepage":"","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/vietnguyentuan2019.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":"2025-11-29T15:42:36.000Z","updated_at":"2025-12-13T01:27:38.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/vietnguyentuan2019/flutter_event_limiter","commit_stats":null,"previous_names":["vietnguyentuan2019/flutter_event_limiter"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/vietnguyentuan2019/flutter_event_limiter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vietnguyentuan2019%2Fflutter_event_limiter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vietnguyentuan2019%2Fflutter_event_limiter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vietnguyentuan2019%2Fflutter_event_limiter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vietnguyentuan2019%2Fflutter_event_limiter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vietnguyentuan2019","download_url":"https://codeload.github.com/vietnguyentuan2019/flutter_event_limiter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vietnguyentuan2019%2Fflutter_event_limiter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28412211,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T05:26:33.345Z","status":"ssl_error","status_checked_at":"2026-01-14T05:21:57.251Z","response_time":107,"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":["anti-spam","async","builder-pattern","dart","debounce","double-click-prevention","event-handling","flutter","flutter-package","loading-state","memory-leak-prevention","race-condition","rate-limiting","throttle","widget"],"created_at":"2026-01-14T06:31:17.640Z","updated_at":"2026-01-14T06:31:18.312Z","avatar_url":"https://github.com/vietnguyentuan2019.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Flutter Event Limiter 🛡️\n\n[![pub package](https://img.shields.io/pub/v/flutter_event_limiter.svg)](https://pub.dev/packages/flutter_event_limiter)\n[![Pub Points](https://img.shields.io/pub/points/flutter_event_limiter)](https://pub.dev/packages/flutter_event_limiter/score)\n[![Tests](https://img.shields.io/badge/tests-128%20passing-brightgreen.svg)](https://github.com/vietnguyentuan2019/flutter_event_limiter)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n**Production-ready throttle and debounce for Flutter apps.**\n\nStop wrestling with `Timer` boilerplate, race conditions, and setState crashes. Handle button spam, search debouncing, and async operations with **3 lines of code** instead of 15+.\n\n---\n\n## ⚡ Quick Start\n\n### Problem: Manual Throttling (15+ lines, error-prone)\n\n```dart\nTimer? _timer;\nbool _loading = false;\n\nvoid onSearch(String text) {\n  _timer?.cancel();\n  _timer = Timer(Duration(milliseconds: 300), () async {\n    setState(() =\u003e _loading = true);\n    try {\n      final result = await api.search(text);\n      if (!mounted) return; // Easy to forget!\n      setState(() =\u003e _result = result);\n    } finally {\n      if (mounted) setState(() =\u003e _loading = false);\n    }\n  });\n}\n\n@override\nvoid dispose() {\n  _timer?.cancel(); // Easy to forget!\n  super.dispose();\n}\n```\n\n### Solution: flutter_event_limiter (3 lines, safe)\n\n```dart\nAsyncDebouncedTextController(\n  onChanged: (text) async =\u003e await api.search(text),\n  onSuccess: (result) =\u003e setState(() =\u003e _result = result),\n  onLoadingChanged: (loading) =\u003e setState(() =\u003e _loading = loading),\n)\n```\n\n**Result:** 80% less code. Auto-dispose. Auto mounted checks. Auto loading state.\n\n---\n\n## ✨ Why This Library?\n\n**Built for production use:**\n- ✅ **160/160 pub points** - Perfect score, actively maintained\n- ✅ **128 comprehensive tests** - Edge cases covered, battle-tested\n- ✅ **Zero dependencies** - No bloat, no conflicts\n\n**Saves development time:**\n- ✅ **3 lines vs 15+** - Eliminates boilerplate\n- ✅ **Auto-safety** - No setState crashes, no memory leaks\n- ✅ **Works with any widget** - Material, Cupertino, custom widgets\n\n**Unique features:**\n- ✅ **Built-in loading state** - No manual `bool isLoading = false` needed\n- ✅ **Race condition prevention** - Auto-cancels stale API calls\n- ✅ **Universal builders** - Not locked into specific widgets\n\n[See detailed comparison with alternatives →](docs/comparison.md)\n\n---\n\n## 🚀 Common Use Cases\n\n### 1. Prevent Button Double-Clicks\n\n```dart\nThrottledInkWell(\n  onTap: () =\u003e submitOrder(), // Only executes once per 500ms\n  child: Text(\"Submit Order\"),\n)\n```\n\n[See E-Commerce example →](docs/examples/e-commerce.md)\n\n### 2. Smart Search Bar\n\n```dart\nAsyncDebouncedTextController(\n  duration: Duration(milliseconds: 300),\n  onChanged: (text) async =\u003e await api.search(text),\n  onSuccess: (products) =\u003e setState(() =\u003e _products = products),\n  onLoadingChanged: (isLoading) =\u003e setState(() =\u003e _loading = isLoading),\n)\n```\n\n[See Search example →](docs/examples/search.md)\n\n### 3. Form Submission with Loading UI\n\n```dart\nAsyncThrottledCallbackBuilder(\n  onPressed: () async =\u003e await uploadFile(),\n  builder: (context, callback, isLoading) {\n    return ElevatedButton(\n      onPressed: isLoading ? null : callback,\n      child: isLoading ? CircularProgressIndicator() : Text(\"Upload\"),\n    );\n  },\n)\n```\n\n[See Form example →](docs/examples/form-submission.md)\n\n### 4. Advanced Concurrency Control (NEW in v1.2.0)\n\nControl how multiple async operations are handled with 4 powerful modes:\n\n```dart\n// Chat App: Queue messages and send in order\nConcurrentAsyncThrottledBuilder(\n  mode: ConcurrencyMode.enqueue,\n  onPressed: () async =\u003e await api.sendMessage(text),\n  builder: (context, callback, isLoading, pendingCount) {\n    return ElevatedButton(\n      onPressed: callback,\n      child: Text(pendingCount \u003e 0 ? 'Sending ($pendingCount)...' : 'Send'),\n    );\n  },\n)\n\n// Search: Cancel old queries, only run latest\nConcurrentAsyncThrottledBuilder(\n  mode: ConcurrencyMode.replace,\n  onPressed: () async =\u003e await api.search(query),\n  builder: (context, callback, isLoading, _) {\n    return SearchBar(\n      onChanged: (q) { query = q; callback?.call(); },\n      trailing: isLoading ? CircularProgressIndicator() : null,\n    );\n  },\n)\n\n// Auto-save: Save current + latest only\nConcurrentAsyncThrottledBuilder(\n  mode: ConcurrencyMode.keepLatest,\n  onPressed: () async =\u003e await api.saveDraft(content),\n  builder: (context, callback, isLoading, _) {\n    return TextField(\n      onChanged: (text) { content = text; callback?.call(); },\n      decoration: InputDecoration(\n        suffixIcon: isLoading ? CircularProgressIndicator() : Icon(Icons.check),\n      ),\n    );\n  },\n)\n```\n\n**4 Concurrency Modes:**\n- 🔴 **Drop** (default): Ignore new calls while busy - perfect for preventing double-clicks\n- 📤 **Enqueue**: Queue all calls and execute sequentially - perfect for chat apps\n- 🔄 **Replace**: Cancel current and start new - perfect for search queries\n- 💾 **Keep Latest**: Execute current + latest only - perfect for auto-save\n\n[See interactive demo →](example/concurrency_demo.dart)\n\n---\n\n## 🎨 Universal Builder Pattern\n\n**The power of flexibility:** Works with **any** Flutter widget.\n\n```dart\nThrottledBuilder(\n  duration: Duration(seconds: 1),\n  builder: (context, throttle) {\n    return FloatingActionButton(\n      onPressed: throttle(() =\u003e saveData()),\n      child: Icon(Icons.save),\n    );\n  },\n)\n```\n\n**Use with:**\n- ✅ Material Design (`ElevatedButton`, `FloatingActionButton`, `InkWell`)\n- ✅ Cupertino (`CupertinoButton`, `CupertinoTextField`)\n- ✅ Custom widgets from any package\n- ✅ Third-party UI libraries\n\nUnlike other libraries that lock you into specific widgets, flutter_event_limiter **adapts to your UI framework**.\n\n---\n\n## 📊 Throttle vs Debounce: Which One?\n\n### Throttle (Anti-Spam)\n\nFires **immediately**, then blocks for duration.\n\n```\nUser clicks: ▼     ▼   ▼▼▼       ▼\nExecutes:    ✓     X   X X       ✓\n             |\u003c-500ms-\u003e|         |\u003c-500ms-\u003e|\n```\n\n**Use for:** Button clicks, refresh actions, preventing spam\n\n### Debounce (Wait for Pause)\n\nWaits for **pause** in events, then fires.\n\n```\nUser types:  a  b  c  d ... (pause) ... e  f  g\nExecutes:                   ✓                   ✓\n             |\u003c--300ms wait--\u003e|     |\u003c--300ms wait--\u003e|\n```\n\n**Use for:** Search input, auto-save, slider changes\n\n### AsyncDebouncer (Debounce + Auto-Cancel)\n\nWaits for pause **and** cancels previous async operations.\n\n```\nUser types:  a    b    c  (API starts) ... d\nAPI calls:   X    X    ▼ (running...)   X (cancelled)\nResult used:                            ✓ (only 'd')\n```\n\n**Use for:** Search APIs, autocomplete, async validation\n\n[Learn more about timing strategies →](docs/guides/throttle-vs-debounce.md)\n\n---\n\n## 📚 Complete Widget Reference\n\n### Throttling (Anti-Spam Buttons)\n\n| Widget | Use Case |\n|--------|----------|\n| `ThrottledInkWell` | Material buttons with ripple |\n| `ThrottledBuilder` | **Universal** - Any widget |\n| `AsyncThrottledCallbackBuilder` | Async with auto loading state |\n| `Throttler` | Direct class (advanced) |\n\n### Debouncing (Search, Auto-save)\n\n| Widget | Use Case |\n|--------|----------|\n| `DebouncedTextController` | Basic text input |\n| `AsyncDebouncedTextController` | Search API with loading |\n| `DebouncedBuilder` | **Universal** - Any widget |\n| `Debouncer` | Direct class (advanced) |\n\n### High-Frequency Events\n\n| Widget | Use Case |\n|--------|----------|\n| `HighFrequencyThrottler` | Scroll, mouse, resize (60fps) |\n\n[View full API documentation →](https://pub.dev/documentation/flutter_event_limiter)\n\n---\n\n## 🛠 Installation\n\n```bash\nflutter pub add flutter_event_limiter\n```\n\nOr add to `pubspec.yaml`:\n\n```yaml\ndependencies:\n  flutter_event_limiter: ^1.1.2\n```\n\nThen import:\n\n```dart\nimport 'package:flutter_event_limiter/flutter_event_limiter.dart';\n```\n\n---\n\n## 📖 Documentation\n\n### Getting Started\n- [Quick Start Guide](docs/getting-started.md)\n- [Throttle vs Debounce Explained](docs/guides/throttle-vs-debounce.md)\n- [FAQ](docs/faq.md) - Common questions answered\n\n### Examples\n- [E-Commerce: Prevent Double Checkout](docs/examples/e-commerce.md)\n- [Search with Race Condition Prevention](docs/examples/search.md)\n- [Form Submission with Loading State](docs/examples/form-submission.md)\n- [Chat App: Prevent Message Spam](docs/examples/chat-app.md)\n\n### Migration Guides\n- [From easy_debounce](docs/migration/from-easy-debounce.md) - Stop managing IDs manually\n- [From flutter_smart_debouncer](docs/migration/from-flutter-smart-debouncer.md) - Unlock from fixed widgets\n- [From rxdart](docs/migration/from-rxdart.md) - Simpler API for UI events\n\n### Advanced\n- [Detailed Comparison with Alternatives](docs/comparison.md)\n- [Roadmap](ROADMAP.md) - Upcoming features\n- [API Reference](https://pub.dev/documentation/flutter_event_limiter)\n\n---\n\n## 🎯 Features\n\n**Core Capabilities:**\n- ⏱️ **Throttle** - Execute immediately, block duplicates\n- ⏳ **Debounce** - Wait for pause, then execute\n- 🔄 **Async Support** - Built-in for async operations\n- 🏃 **High Frequency** - Optimized for scroll/mouse events (60fps)\n- 🎭 **Combo** - ThrottleDebouncer (leading + trailing)\n\n**Safety \u0026 Reliability:**\n- 🛡️ **Auto Dispose** - Zero memory leaks\n- ✅ **Auto Mounted Checks** - No setState crashes\n- 🏁 **Race Condition Prevention** - Auto-cancel stale calls\n- 📦 **Batch Execution** - Group multiple operations\n\n**Developer Experience:**\n- 🎨 **Universal Builders** - Works with any widget\n- 📊 **Built-in Loading State** - No manual flags\n- 🐛 **Debug Mode** - Log throttle/debounce events\n- 📈 **Performance Metrics** - Track execution time\n- ⚙️ **Conditional Execution** - Enable/disable dynamically\n\n---\n\n## 🧪 Testing\n\nWorks seamlessly with Flutter's test framework:\n\n```dart\ntestWidgets('throttle blocks rapid clicks', (tester) async {\n  int clickCount = 0;\n\n  await tester.pumpWidget(\n    MaterialApp(\n      home: ThrottledInkWell(\n        duration: Duration(milliseconds: 500),\n        onTap: () =\u003e clickCount++,\n        child: Text('Tap'),\n      ),\n    ),\n  );\n\n  await tester.tap(find.text('Tap'));\n  expect(clickCount, 1);\n\n  await tester.tap(find.text('Tap')); // Blocked\n  expect(clickCount, 1);\n\n  await tester.pumpAndSettle(Duration(milliseconds: 500));\n  await tester.tap(find.text('Tap')); // Works again\n  expect(clickCount, 2);\n});\n```\n\n[See more testing examples in FAQ →](docs/faq.md#testing)\n\n---\n\n## 🔧 Advanced Features (v1.1.0+)\n\n### Debug Mode\n\n```dart\nThrottler(\n  debugMode: true,\n  name: 'submit-button',\n  onMetrics: (duration, executed) {\n    print('Throttle took: $duration, executed: $executed');\n  },\n)\n```\n\n### Conditional Throttling\n\n```dart\nThrottledBuilder(\n  enabled: !isVipUser, // VIP users skip throttle\n  builder: (context, throttle) =\u003e ElevatedButton(...),\n)\n```\n\n### Custom Duration per Call\n\n```dart\nfinal throttler = Throttler();\n\nthrottler.callWithDuration(\n  () =\u003e criticalAction(),\n  duration: Duration(seconds: 2), // Override default\n);\n```\n\n### Manual Reset\n\n```dart\nfinal throttler = Throttler();\nthrottler.call(() =\u003e action());\nthrottler.reset(); // Clear throttle state\nthrottler.call(() =\u003e action()); // Executes immediately\n```\n\n[See all advanced features →](docs/guides/advanced-features.md)\n\n---\n\n## 💡 Integration with State Management\n\nWorks with **all** state management solutions:\n\n**GetX:**\n```dart\nThrottledInkWell(\n  onTap: () =\u003e Get.find\u003cMyController\u003e().submit(),\n  child: Text(\"Submit\"),\n)\n```\n\n**Riverpod:**\n```dart\nAsyncDebouncedTextController(\n  onChanged: (text) async {\n    return await ref.read(searchProvider.notifier).search(text);\n  },\n  onSuccess: (results) {\n    // Update state\n  },\n)\n```\n\n**Bloc:**\n```dart\nThrottledBuilder(\n  builder: (context, throttle) {\n    return ElevatedButton(\n      onPressed: throttle(() =\u003e context.read\u003cMyBloc\u003e().add(SubmitEvent())),\n      child: Text(\"Submit\"),\n    );\n  },\n)\n```\n\n**Provider:**\n```dart\nThrottledInkWell(\n  onTap: () =\u003e context.read\u003cCounterProvider\u003e().increment(),\n  child: Text(\"Increment\"),\n)\n```\n\n[See more state management examples →](docs/faq.md#state-management)\n\n---\n\n## ⚡ Performance\n\nNear-zero overhead:\n\n| Metric | Performance |\n|--------|------------|\n| Throttle/Debounce | ~0.01ms per call |\n| High-Frequency Throttler | ~0.001ms (100x faster) |\n| Memory | ~40 bytes per controller |\n\n**Benchmarked:** Handles 1000+ concurrent operations without frame drops.\n\n[See performance benchmarks →](docs/guides/performance.md)\n\n---\n\n## 🤝 Contributing\n\nContributions welcome! Please:\n1. Fork the repository\n2. Create a feature branch\n3. Add tests for new features\n4. Submit a pull request\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n---\n\n## 📄 License\n\nMIT License - See [LICENSE](LICENSE) file for details.\n\n---\n\n## 📮 Support\n\n- 💬 **Questions:** [FAQ](docs/faq.md) · [GitHub Discussions](https://github.com/vietnguyentuan2019/flutter_event_limiter/discussions)\n- 🐛 **Bugs:** [GitHub Issues](https://github.com/vietnguyentuan2019/flutter_event_limiter/issues)\n- ⭐ **Like it?** Star this repo!\n\n---\n\n\u003cp align=\"center\"\u003eMade with ❤️ for the Flutter community\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvietnguyentuan2019%2Fflutter_event_limiter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvietnguyentuan2019%2Fflutter_event_limiter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvietnguyentuan2019%2Fflutter_event_limiter/lists"}