Source on GitHubfast_debounce/fast_throttle/fast_rate_limit/
Overview
Three static utilities built on dart:async Timer to tame high-frequency side effects (rapid taps, search input, scroll handlers). Each uses a tag so different call sites do not interfere.
| Utility | Behavior | Typical use |
|---|---|---|
FastDebounce | Each call resets a quiet timer; only the last call runs after quiet time | Search-as-you-type, resize |
FastThrottle | At most one run per throttle window (first call in the window runs immediately; extra in-window calls are dropped) | Submit buttons, likes |
FastRateLimit | First call runs immediately and starts time windows of length duration; in-window calls defer—only the latest callback runs at the next window boundary | Coalesced search submit, scroll pagination |
import 'package:fast_package/fast_package.dart';FastDebounce
Each call resets the quiet timer; onExecute runs only after duration passes with no further calls. When duration is Duration.zero, the callback runs immediately and any pending timer is cleared.
Basic usage
Search field: request after the user pauses typing.
TextField(
onChanged: (keyword) {
FastDebounce.debounce(
tag: 'search-suggest',
duration: const Duration(milliseconds: 300),
onExecute: () => fetchSuggestions(keyword),
);
},
);
@override
void dispose() {
FastDebounce.cancel('search-suggest');
super.dispose();
}void onKeywordChanged(String keyword) {
FastDebounce.debounce(
tag: 'search-suggest',
duration: const Duration(milliseconds: 300),
onExecute: () => fetchSuggestions(keyword),
);
}
void dispose() {
FastDebounce.cancel('search-suggest');
}API reference
FastDebounce.debounce
Delays execution on every call; runs only the last onExecute after quiet time with no new calls.
FastDebounce.debounce(
tag: 'search-suggest',
duration: const Duration(milliseconds: 300),
onExecute: () {
// runs after quiet time
},
);| Parameter | Type | Required | Description |
|---|---|---|---|
tag | String | yes | Instance id. Same tag shares one timer chain; use different tags per feature (e.g. 'search-suggest'). |
duration | Duration | yes | Quiet period. Each call resets the timer; onExecute runs only after duration with no further calls. Duration.zero runs immediately and clears any pending timer. |
onExecute | void Function() | yes | Side effect after quiet time (FastDebounceVoidCallback). |
FastDebounce.fire
Runs the cached onExecute for tag now (e.g. user taps “Search now”), without waiting for quiet time.fire does not stop the quiet timer already scheduled for that tag: if there are no further debounce calls, the timer still fires and onExecute runs again. To run once now and prevent that second run at expiry, call fire then cancel with the same tag.
FastDebounce.fire('search-suggest');
// Run now, and do not run again when quiet time expires:
FastDebounce.fire('search-suggest');
FastDebounce.cancel('search-suggest');| Parameter | Type | Required | Description |
|---|---|---|---|
tag | String | yes | Debounce instance to flush immediately. |
FastDebounce.cancel
Cancels the timer for tag and removes its record; a pending onExecute will not run. Call from dispose to avoid leaks or callbacks after navigation.
FastDebounce.cancel('search-suggest');| Parameter | Type | Required | Description |
|---|---|---|---|
tag | String | yes | Debounce instance to cancel. |
FastDebounce.cancelAll
Cancels all active debounces (e.g. on global logout or app reset).
FastDebounce.cancelAll();No parameters.
FastDebounce.count
How many debounces are still waiting for quiet time (debugging / monitoring).
| Type | Description | |
|---|---|---|
| Return value | int (getter) | Count of debounce instances with an active timer. |
FastThrottle
Maintains one throttle window per tag (length duration). The first call in a window runs onExecute immediately; further calls in the same window are dropped (skipped == true). Optionally runs onAfter when the window ends.
Basic usage
Submit button: ignore double-taps within the window.
FilledButton(
onPressed: () {
FastThrottle.throttle(
tag: 'checkout-submit',
duration: const Duration(seconds: 1),
onExecute: () => placeOrder(),
);
},
child: const Text('Place order'),
);void submitOrder() {
FastThrottle.throttle(
tag: 'checkout-submit',
duration: const Duration(seconds: 1),
onExecute: () => placeOrder(),
);
}API reference
FastThrottle.throttle
Keeps one throttle window per tag: the first call in a window runs onExecute immediately; duplicates in the same window are dropped.
final skipped = FastThrottle.throttle(
tag: 'checkout-submit',
duration: const Duration(seconds: 1),
onExecute: () => placeOrder(),
onAfter: () {}, // optional
);
// skipped == true: still inside the window; this call was dropped
// skipped == false: new window started; onExecute already ran| Parameter | Type | Required | Description |
|---|---|---|---|
tag | String | yes | Instance id. At most one active window per tag at a time. |
duration | Duration | yes | Window length. In-window calls do not extend the window. |
onExecute | void Function() | yes | Runs immediately on the first call in a new window (FastThrottleVoidCallback). |
onAfter | void Function()? | no | Runs when the window ends (timer fires); e.g. re-enable a button. |
| Return value | Type | Description |
|---|---|---|
true | bool | Name it skipped: call fell inside an existing window; onExecute did not run. |
false | bool | A new window started and onExecute already ran. |
FastThrottle.cancel
Cancels the throttle window and timer for tag; a pending onAfter for that window will not run.
FastThrottle.cancel('checkout-submit');| Parameter | Type | Required | Description |
|---|---|---|---|
tag | String | yes | Throttle instance to cancel. |
FastThrottle.cancelAll
Cancels all active throttles.
FastThrottle.cancelAll();No parameters.
FastThrottle.count
How many throttle windows are currently active.
| Type | Description | |
|---|---|---|
| Return value | int (getter) | Count of active throttle instances. |
FastRateLimit
Uses a fixed step duration to slice time windows (Timer.periodic). While a rate limit is active for a tag, the implementation keeps one deferred callback (in-window calls keep only the latest onExecute / onAfter you pass).
Behavior
- First call (no active limiter for this
tag): runsonExecuteimmediately and starts the window timer; returnsfalse. - In-window call: does not run now; replaces the deferred callback; returns
true. - Next window boundary (timer tick): if a deferred callback exists, runs it and its
onAfter, then clears; if the previous window had no in-window calls, the limiter stops and may run theonAfterfrom the first call.
Unlike FastThrottle: throttle drops extra in-window calls; rate limit coalesces them into one run at the next boundary.
Basic usage
Infinite list: coalesce rapid “near bottom” triggers.
void _onNearBottom() {
FastRateLimit.rateLimit(
tag: 'feed-load-more',
duration: const Duration(milliseconds: 500),
onExecute: () => loadNextPage(),
);
}void onNearBottom() {
FastRateLimit.rateLimit(
tag: 'feed-load-more',
duration: const Duration(milliseconds: 500),
onExecute: () => loadNextPage(),
);
}API reference
FastRateLimit.rateLimit
First call runs immediately and starts fixed time windows of length duration. In-window calls defer: only the latest callback is kept and runs at the next window boundary (see Behavior).
final deferred = FastRateLimit.rateLimit(
tag: 'feed-load-more',
duration: const Duration(milliseconds: 500),
onExecute: () => loadNextPage(),
onAfter: () {}, // optional
);
// deferred == true: limiter active; merged to next window
// deferred == false: first call (or restart); onExecute already ran| Parameter | Type | Required | Description |
|---|---|---|---|
tag | String | yes | Instance id. Same tag shares one deferred callback and window timer. |
duration | Duration | yes | Length of each time window (tick step). |
onExecute | void Function() | yes | Business callback (FastRateLimitCallback). Immediate on first call; in-window calls update the deferred callback (latest wins at next boundary). |
onAfter | void Function()? | no | Paired with deferred work: runs after the matching deferred onExecute; on idle shutdown may run the onAfter from the first call. |
| Return value | Type | Description |
|---|---|---|
true | bool | Name it deferred: limiter active; this call was merged; onExecute did not run now. |
false | bool | First call or restart after idle: onExecute already ran and the limiter started. |
FastRateLimit.cancel
Cancels the window timer for tag and removes its record; a pending deferred callback will not run.
FastRateLimit.cancel('feed-load-more');| Parameter | Type | Required | Description |
|---|---|---|---|
tag | String | yes | Rate limit instance to cancel. |
FastRateLimit.cancelAll
Cancels all active rate limiters.
FastRateLimit.cancelAll();No parameters.
FastRateLimit.count
How many rate limiters are still active (window timer running).
| Type | Description | |
|---|---|---|
| Return value | int (getter) | Count of active rate limit instances. |