img

Persistent limits. Effortlessly automated.

One line. No boilerplate. No setup. The limit package gives you instant, persistent control over cooldowns and rate limits โ€” across sessions, isolates, and app restarts. Define once, automate forever.

Table of Contents

  • โฒ Cooldown โ€” automatically manage cooldown periods (e.g. daily rewards, retry delays)
  • ๐Ÿ“Š Rate Limiter โ€” control rates using a token bucket (e.g. 1000 actions per 15 minutes)
  • ๐Ÿ“ฆ More Packages โ€” other packages by Jozz

Why Use limit?

Working with cooldowns and rate limits usually means:

  • Manual DateTime comparisons
  • Writing timers or expiration logic
  • Saving timestamps or counters
  • Handling null, casting, and cleanup

limit removes all that: you just define, call, and trust it.

  • โœ… Lets you define, control, and forget โ€” the system handles everything in the background
  • โœ… One-line setup, no manual storage or timers
  • โœ… Persisted across app restarts and isolates
  • โœ… Async-safe and cache-friendly
  • โœ… Works great for daily rewards, retry delays, API limits, chat quotas, and more

Choosing the Right Limiter

Each limiter is tailored for a specific pattern of time-based control.

Goal Use
"Only once every X time" Cooldown
"Allow N actions per Y minutes" RateLimiter

โฒ Cooldown

"Only once every 24 hours"
โ†’ Fixed cooldown timer from last activation
โ†’ Great for claim buttons, retry delays, or cooldown locks

๐Ÿ“Š RateLimiter

"Allow 100 actions per 15 minutes (rolling refill)"
โ†’ Token bucket algorithm
โ†’ Replenishes tokens over time (not per action)
โ†’ Great for APIs, messaging, or hard quota control


โฒ Cooldown Persistent Cooldown Service

โคด๏ธ Back -> Table of Contents

Cooldown is a plug-and-play utility service for managing cooldown windows (e.g. daily rewards, button lockouts, retry delays) that persist across sessions and isolates โ€” no timers, no manual bookkeeping, no re-implementation every time.

It handles:

  • Cooldown timing (DateTime.now() + duration)
  • Persistent storage (with caching and async-safety)
  • Activation tracking and expiration logic
  • Usage statistics (activation count, expiry progress, etc.)

๐Ÿ”ง How to Use

  • isCooldownActive() โ€” Returns true if the cooldown is still active
  • isExpired() โ€” Returns true if the cooldown has expired or was never started
  • activateCooldown() โ€” Starts the cooldown using the configured duration
  • tryActivate() โ€” Starts cooldown only if it's not active โ€” returns whether it was triggered
  • reset() โ€” Clears the cooldown timer, but keeps the activation count
  • completeReset() โ€” Fully resets both the cooldown and its usage counter
  • timeRemaining() โ€” Returns remaining time as a Duration
  • secondsRemaining() โ€” Same as above, in seconds
  • percentRemaining() โ€” Progress indicator between 0.0 and 1.0
  • getLastActivationTime() โ€” Returns DateTime? of last activation
  • getEndTime() โ€” Returns when the cooldown will end
  • whenExpires() โ€” Returns a Future that completes when the cooldown ends
  • getActivationCount() โ€” Returns the total number of activations
  • removeAll() โ€” Deletes all stored values (for testing/debugging)
  • anyStateExists() โ€” Returns true if any cooldown data exists in storage

โœ… Define a Cooldown

final cooldown = Cooldown('daily_reward', duration: Duration(hours: 24));

This creates a persistent cooldown that lasts 24 hours. It uses the prefix 'daily_reward' to store:

  • Last activation timestamp
  • Activation count

๐Ÿ” Check If Cooldown Is Active

if (await cooldown.isCooldownActive()) {
  print('Wait before trying again!');
}

โฑ Activate the Cooldown

await cooldown.activateCooldown();

This sets the cooldown to now and begins the countdown. The activation count is automatically incremented.


โšก Try Activating Only If Expired

if (await cooldown.tryActivate()) {
  print('Action allowed and cooldown started');
} else {
  print('Still cooling down...');
}

Use this for one-line cooldown triggers (e.g. claiming a daily gift or retrying a network call).


๐Ÿงผ Reset or Fully Clear Cooldown

await cooldown.reset();         // Clears only the time
await cooldown.completeReset(); // Clears time and resets usage counter

๐Ÿ•“ Check Time Remaining

final remaining = await cooldown.timeRemaining();
print('Still ${remaining.inMinutes} minutes left');

You can also use:

await cooldown.secondsRemaining();   // int
await cooldown.percentRemaining();   // double between 0.0โ€“1.0

๐Ÿ“… View Timing Info

final lastUsed = await cooldown.getLastActivationTime();
final endsAt = await cooldown.getEndTime();

โณ Wait for Expiry (e.g. for auto-retry)

await cooldown.whenExpires(); // Completes only when cooldown is over

๐Ÿ“Š Get Activation Count

final count = await cooldown.getActivationCount();
print('Used $count times');

๐Ÿงช Test Utilities

await cooldown.removeAll();                     // Clears all stored cooldown state
final exists = await cooldown.anyStateExists(); // Returns true if anything is stored

You can create as many cooldowns as you need โ€” each with a unique prefix. All state is persisted, isolate-safe, and instantly reusable.


โšก Optional useCache Parameter

Each limiter accepts a useCache flag:

final cooldown = Cooldown(
  'name_key',
  duration: Duration(minutes: 5),
  useCache: true // false by default
);
  • useCache: false (default):

    • Fully isolate-safe
    • Reads directly from storage every time
    • Best when multiple isolates might read/write the same data
  • useCache: true:

    • Uses memory caching for faster access
    • Not isolate-safe โ€” may lead to stale or out-of-sync data across isolates
    • Best when used in single-isolate environments (most apps)

โš ๏ธ Warning: Enabling useCache disables isolate safety. Use only when you're sure no other isolate accesses the same key.


๐Ÿ“Š RateLimiter Token Bucket Rate Limiter

โคด๏ธ Back -> Table of Contents

RateLimiter is a high-performance, plug-and-play utility that implements a token bucket algorithm to enforce rate limits โ€” like โ€œ100 actions per 15 minutesโ€ โ€” across sessions, isolates, and app restarts.

It handles:

  • Token-based rate limiting
  • Automatic time-based token refill
  • Persistent state using prf types (PrfIso<double>, PrfIso<DateTime>)
  • Async-safe, isolate-compatible behavior

Perfect for chat limits, API quotas, retry windows, or any action frequency cap โ€” all stored locally.


๐Ÿ”ง How to Use

  • tryConsume() โ€” Tries to use 1 token; returns true if allowed, or false if rate-limited
  • isLimitedNow() โ€” Returns true if no tokens are currently available
  • isReady() โ€” Returns true if at least one token is available
  • getAvailableTokens() โ€” Returns the current number of usable tokens (calculated live)
  • timeUntilNextToken() โ€” Returns a Duration until at least one token will be available
  • nextAllowedTime() โ€” Returns the exact DateTime when a token will be available
  • reset() โ€” Resets to full token count and updates last refill to now
  • removeAll() โ€” Deletes all limiter state (for testing/debugging)
  • anyStateExists() โ€” Returns true if limiter data exists in storage
  • runIfAllowed(action) โ€” Runs a callback if allowed, otherwise returns null
  • debugStats() โ€” Returns detailed internal stats for logging and debugging

The limiter uses fractional tokens internally to maintain precise refill rates, even across app restarts. No timers or background services required โ€” it just works.


โœ… RateLimiter Basic Setup

Create a limiter with a key, a maximum number of actions, and a refill duration:

final limiter = RateLimiter(
  'chat_send',
  maxTokens: 100,
  refillDuration: Duration(minutes: 15),
);

This example allows up to 100 actions per 15 minutes. The token count is automatically replenished over time โ€” even after app restarts.


๐Ÿš€ Check & Consume

To attempt an action:

final canSend = await limiter.tryConsume();

if (canSend) {
  // Allowed โ€“ proceed with the action
} else {
  // Blocked โ€“ too many actions, rate limit hit
}

Returns true if a token was available and consumed, or false if the limit was exceeded.


๐Ÿงฎ Get Available Tokens

To check how many tokens are usable at the moment:

final tokens = await limiter.getAvailableTokens();
print('Tokens left: ${tokens.toStringAsFixed(2)}');

Useful for debugging, showing rate limit progress, or enabling/disabling UI actions.


โณ Time Until Next Token

To wait or show feedback until the next token becomes available:

final waitTime = await limiter.timeUntilNextToken();
print('Try again in: ${waitTime.inSeconds}s');

You can also get the actual time point:

final nextTime = await limiter.nextAllowedTime();

๐Ÿ” Reset the Limiter

To fully refill the bucket and reset the refill clock:

await limiter.reset();

Use this after manual overrides, feature unlocks, or privileged user actions.


๐Ÿงผ Clear All Stored State

To wipe all saved token/refill data (for debugging or tests):

await limiter.removeAll();

To check if the limiter has any stored state:

final exists = await limiter.anyStateExists();

โšก Optional useCache Parameter

Each limiter accepts a useCache flag:

final limiter = RateLimiter(
  'key',
  maxTokens: 10,
  refillDuration: Duration(minutes: 5),
  useCache: true // false by default
);
  • useCache: false (default):

    • Fully isolate-safe
    • Reads directly from storage every time
    • Best when multiple isolates might read/write the same data
  • useCache: true:

    • Uses memory caching for faster access
    • Not isolate-safe โ€” may lead to stale or out-of-sync data across isolates
    • Best when used in single-isolate environments (most apps)

โš ๏ธ Warning: Enabling useCache disables isolate safety. Use only when you're sure no other isolate accesses the same key.

โคด๏ธ Back -> Table of Contents


๐Ÿ“ฆ More jozz Packages

โคด๏ธ Back โ†’ Table of Contents

Iโ€™m Jozz โ€” and my packages share a simple philosophy: developer experience first. I try to avoid boilerplate wherever possible, and most of these packages were born out of real needs in my own projects. Each one comes with clear documentation, minimal setup, and APIs that are easy to pick up without surprises.

Theyโ€™re built to be lightweight, reliable, and ready for production, always with simplicity in mind. There are more packages in the works, following the same approach. If you find them useful and feel like supporting, youโ€™re welcome to do so (:

โ˜• Buy me a coffee

  • shrink โ€“ Compress Anything in One Line
  • track โ€“ Persistent Streaks, Counters & Records
  • hivez โ€“ Hive, but Safer & Smarter
  • time_plus โ€“ Smarter DateTime & Duration Extensions
  • prf โ€“ SharedPreferences, Without the Pain
  • exui โ€“ Supercharge Your Flutter UI
  • jozz_events โ€“ Strongly-Typed Events for Clean Architecture

๐Ÿ”ฝ shrink โ€“ Compress Anything in One Line

Because every byte counts. shrink makes data compression effortless with a one-line API and fully lossless results. It auto-detects the best method, often cutting size by 5ร— to 40ร— (and up to 1,000ร—+ for structured data). Perfect for Firestore, local storage, or bandwidth-sensitive apps. Backed by clear docs and real-world benchmarks.

๐Ÿ“Š track โ€“ Persistent Streaks, Counters & Records

Define once, track forever. track gives you plug-and-play tools for streaks, counters, activity logs, and records โ€” all persisted safely across sessions and isolates. From daily streaks to rolling counters to best-ever records, it handles resets, history, and storage automatically. Clean APIs, zero boilerplate, and deeply detailed documentation.

๐Ÿ hivez โ€“ Hive, but Safer & Smarter

hivez is a production-ready layer on top of Hive CE that keeps its raw speed but makes it safer and easier to use. It auto-initializes boxes, enforces type safety, and gives you a single unified API for Box, LazyBox, and IsolatedBox. Concurrency issues are handled with built-in locks, and you also get extras like backup/restore, search, and crash recovery. Backed by clear, detailed documentation, hivez is designed for real-world apps where you want Hiveโ€™s performance without the boilerplate or pitfalls.

โฑ time_plus โ€“ Smarter DateTime & Duration Extensions

Stop wrestling with DateTime and Duration. time_plus adds the missing tools you wish Dart had built in: add and subtract time units, start/end of day/week/month, compare by precision, yesterday/tomorrow, fractional durations, and more. Built with 128+ extensions, 700+ tests, and zero dependencies, itโ€™s faster, more precise, and more reliable than the classic time package โ€” while keeping APIs clear and intuitive. Ideal for scheduling, analytics, or any app where every microsecond counts.

โšก prf โ€“ SharedPreferences, Without the Pain

No strings, no boilerplate, no setup. prf lets you define variables once, then get() and set() them anywhere with a type-safe API. It fully replaces raw SharedPreferences with support for 20+ built-in types (including DateTime, Duration, Uint8List, JSON, and enums). Every variable is cached, test-friendly, and isolate-safe with a .isolated mode. Designed for clarity, scale, and zero friction, with docs that make local persistence finally headache-free.

๐ŸŽจ exui โ€“ Supercharge Your Flutter UI

Everything your widgets wish they had. exui is a zero-dependency extension library for Flutter with 200+ chainable utilities for padding, margin, centering, gaps, visibility, constraints, gestures, buttons, text styling, and more โ€” all while keeping your widget tree fully native.

No wrappers. No boilerplate. Just concise, expressive methods that feel built into Flutter itself. Backed by hundreds of unit tests and exceptional documentation, exui makes UI code cleaner, faster, and easier to maintain.

๐Ÿ“ข jozz_events โ€“ Strongly-Typed Events for Clean Architecture

A domain-first, framework-agnostic event bus built for scalable apps. jozz_events enables decoupled, strongly-typed communication between features and layers โ€” without the spaghetti. Itโ€™s lightweight, dependency-free, lifecycle-aware, and integrates naturally with Clean Architecture. Ideal for Flutter or pure Dart projects where modularity, testability, and clarity matter most.

๐Ÿ”— License MIT ยฉ Jozz

โ˜• Enjoying this package? You can support it here.

Libraries

limit
limit Package Barrel File