بنقرة واحدة
add-throttle
Add throttle rate limiting to prevent too-frequent method calls for refresh, scroll, or API limits
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Add throttle rate limiting to prevent too-frequent method calls for refresh, scroll, or API limits
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Add custom error handling with catchError to a mix() call for logging, error conversion, or suppression
Add internet connectivity checking before executing a Cubit method with optional retry
Add debounce to delay method execution until after a period of inactivity for search or validation
Add EffectQueue to state for triggering multiple sequential one-time UI effects
Add Effect fields to state class for one-time UI notifications like snackbars, navigation, or form clearing
Add error state handling to widgets using context.isFailed() and context.getException()
| name | add-throttle |
| description | Add throttle rate limiting to prevent too-frequent method calls for refresh, scroll, or API limits |
This skill rate-limits method execution to prevent too-frequent calls.
Adds the throttle parameter to a mix() call so that:
Ask the user which Cubit method needs throttling, or identify methods that:
Add throttle: throttle to the mix() call:
import 'package:bloc_superpowers/bloc_superpowers.dart';
class DataCubit extends Cubit<DataState> {
DataCubit() : super(const DataState());
void refresh() => mix(
key: this,
throttle: throttle, // Add this line (default: 1 second)
() async {
final data = await api.fetchData();
emit(state.copyWith(data: data));
},
);
}
The default throttle duration is 1 second. Customize as needed:
void refresh() => mix(
key: this,
throttle: throttle(duration: 5.sec), // Rate limit to once per 5 seconds
() async {
final data = await api.fetchData();
emit(state.copyWith(data: data));
},
);
User taps refresh rapidly: tap → tap → tap → tap → tap
↓ ✗ ✗ ✗ ✗
executes (ignored until 1 second passes)
↓
1 second later...
↓
tap → executes
First call runs immediately, subsequent calls are blocked for the throttle period.
throttle // 1 second (default)
throttle(duration: 500.millis) // 500ms
throttle(duration: 5.sec) // 5 seconds
throttle(duration: 1.minutes) // 1 minute
Allow immediate retry if the method fails:
void refresh() => mix(
key: this,
throttle: throttle(
duration: 5.sec,
removeLockOnError: true, // Allow retry after failure
),
() async {
final data = await api.fetchData();
emit(state.copyWith(data: data));
},
);
Allow bypassing the throttle with a parameter:
void refresh({bool force = false}) => mix(
key: this,
throttle: throttle(
duration: 5.sec,
ignoreThrottle: force, // When true, ignores throttle
),
() async {
final data = await api.fetchData();
emit(state.copyWith(data: data));
},
);
// Normal call - respects throttle
cubit.refresh();
// Force refresh - ignores throttle
cubit.refresh(force: true);
Different items can have separate throttle timers:
void refreshFeed(String feedId) => mix(
key: this,
throttle: throttle(key: (FeedCubit, feedId)),
() async {
final posts = await api.fetchFeed(feedId);
emit(state.copyWith(feeds: {...state.feeds, feedId: posts}));
},
);
With this setup:
void refresh({bool force = false}) => mix(
key: this,
throttle: throttle(
duration: 5.sec,
ignoreThrottle: force,
removeLockOnError: true,
),
() async {
final data = await api.getData();
emit(state.copyWith(data: data));
},
);
// In widget
RefreshIndicator(
onRefresh: () async {
context.read<DataCubit>().refresh(force: true);
},
child: DataList(),
)
void loadMore() => mix(
key: LoadMore,
throttle: throttle(duration: 500.millis),
() async {
final nextPage = await api.getNextPage(state.currentPage + 1);
emit(state.copyWith(
items: [...state.items, ...nextPage.items],
currentPage: state.currentPage + 1,
));
},
);
void toggleLike(String itemId) => mix(
key: (ToggleLike, itemId),
throttle: throttle(duration: 1.sec),
() async {
await api.toggleLike(itemId);
emit(state.copyWith(
likedItems: state.likedItems.contains(itemId)
? state.likedItems.remove(itemId)
: state.likedItems.add(itemId),
));
},
);
void fetchData() => mix(
key: this,
throttle: throttle(duration: 1.sec), // Match API rate limit
retry: retry,
() async {
final data = await rateLimitedApi.getData();
emit(data);
},
);
| Feature | First Call | Subsequent Calls | Best For |
|---|---|---|---|
| Throttle | Executes immediately | Blocked until period ends | Refresh, scroll, rate limits |
| Debounce | Waits for inactivity | Reset timer | Search, validation |
| Fresh | Executes if stale | Skipped if fresh | Caching loaded data |
Throttle: "Execute now, then wait before allowing again" Debounce: "Wait until user stops, then execute" Fresh: "Skip if we already have recent data"
Clear throttle locks manually when needed:
// Clear throttle for a specific key
Superpowers.removeThrottleLock(DataCubit);
Superpowers.removeThrottleLock((FeedCubit, feedId));
// Clear all throttle locks
Superpowers.removeAllThrottleLocks();
class FeedCubit extends Cubit<FeedState> {
FeedCubit() : super(const FeedState());
// Throttle refresh to once per 5 seconds
void refresh({bool force = false}) => mix(
key: this,
throttle: throttle(
duration: 5.sec,
ignoreThrottle: force,
removeLockOnError: true,
),
retry: retry,
() async {
final posts = await api.getPosts();
emit(state.copyWith(posts: posts));
},
);
// Throttle scroll-based loading
void loadMore() => mix(
key: LoadMore,
throttle: throttle(duration: 500.millis),
() async {
if (state.hasMore) {
final nextPage = await api.getPosts(page: state.page + 1);
emit(state.copyWith(
posts: [...state.posts, ...nextPage.posts],
page: state.page + 1,
hasMore: nextPage.hasMore,
));
}
},
);
}
// Widget
class FeedScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: () async {
context.read<FeedCubit>().refresh(force: true);
},
child: NotificationListener<ScrollNotification>(
onNotification: (notification) {
if (notification.metrics.pixels >=
notification.metrics.maxScrollExtent - 200) {
context.read<FeedCubit>().loadMore();
}
return false;
},
child: PostsList(),
),
);
}
}
Ask the user: