track 1.1.2
track: ^1.1.2 copied to clipboard
Easily track streaks, counters, history, and records. Effortless persistent trackers with no manual timers or storage, just define and go.
From streaks to records β all your progress, automated.
One line. No boilerplate. No setup. The track package gives you instant, persistent tracking for streaks, counters, histories, and records β across sessions, isolates, and app restarts. Define once, track forever.
Table of Contents
- π₯ StreakTracker β track streaks that reset when a period is missed (e.g. daily habits)
- π§Ύ HistoryTracker β maintain a rolling list of recent items with max length and deduplication
- π PeriodicCounter β count events within aligned time periods (e.g. daily tasks, hourly goals)
- β³ RolloverCounter β track counts over a sliding window that resets after inactivity
- π ActivityCounter β capture detailed activity stats over hours, days, months, and years
- π BestRecord β track the best (max or min) performance over time, with history and fallback
- π’ BasicCounter β simple persistent counter with no expiration or alignment
π₯ Why Use track
? #
Working with streaks, counters, and history usually means:
- Manually managing resets
- Writing timestamp logic and period alignment
- Saving counters and records yourself
- Cleaning up old or expired data
track removes all that: you just define, call, and trust it.
- β Lets you define, track, and forget β the system handles everything in the background
- β One-line setup, no manual timers or storage
- β Persisted across app restarts and isolates
- β Async-safe and cache-friendly
- β Perfect for streaks, habits, counters, leaderboards, activity stats, and more
π Choosing the Right Tool #
Each service is tailored for a specific pattern of time-based control.
Goal | Use |
---|---|
"Track a streak of daily activity" | StreakTracker |
"Keep a list of recent values" | HistoryTracker<T> |
"Count per hour / day / week" | PeriodicCounter |
"Reset X minutes after last use" | RolloverCounter |
"Track activity history over time" | ActivityCounter |
"Track the best result or score" | BestRecord |
"Simple always-on counter" | BasicCounter |
"Maintain a daily learning streak"
β Aligned periods (daily
,weekly
, etc.)
β Resets if user misses a full period
β Ideal for habit chains, gamified streaks
β Tracks best streak ever (with BestRecord)
"Track recent searches, actions, or viewed items"
β FIFO list stored inPrf<List<T>>
β Supports deduplication, max length, and type-safe adapters
β Perfect for autocomplete history, usage trails, or navigation stacks
"How many times today?"
β Auto-reset at the start of each period (e.g. midnight)
β Clean for tracking daily usage, hourly limits
"Max 5 actions per 10 minutes (sliding)"
β Resets after duration from last activity
β Perfect for soft rate caps, retry attempt tracking
"Track usage over time by hour, day, month, year"
β Persistent time-series counter
β Supports summaries, totals, active dates, and trimming
β Ideal for activity heatmaps, usage analytics, or historical stats
"Record your highest score or fastest time"
β Tracks best (max/min) values with full history and fallback
β Great for highscores, fastest runs, or top performance
"Count total taps, visits, or actions"
β Simple always-on counter without reset logic
β Now with synchronizedclearValueOnly()
for safe updates
π₯ StreakTracker
Persistent Streak Tracker #
β€΄οΈ Back -> Table of Contents
StreakTracker
is a drop-in utility for managing activity streaks β like daily check-ins, learning streaks, or workout chains β with automatic expiration logic and aligned time periods. It resets automatically if a full period is missed, and persists streak progress across sessions and isolates.
It handles:
- Aligned period tracking (
daily
,weekly
, etc.) viaTimePeriod
- Persistent storage with
prf
usingPrfIso<int>
andDateTime
- Automatic streak expiration logic if a period is skipped
- Best streak record tracking with integrated
BestRecord
- Tracks the highest (or lowest) streak ever achieved
- Maintains optional record history and fallback record
- Useful metadata like last update time, next reset estimate, and time remaining
π§ How to Use #
bump([amount])
β Marks the current period as completed and increases the streakcurrentStreak()
β Returns the current streak value (auto-resets if expired)isStreakBroken()
β Returnstrue
if the streak has been broken (a period was missed)isStreakActive()
β Returnstrue
if the streak is still activenextResetTime()
β Returns when the streak will break if not continuedpercentRemaining()
β Progress indicator (0.0β1.0) until streak breakstreakAge()
β Time passed since the last streak bumpreset()
β Fully resets the streak to 0 and clears last updatepeek()
β Returns the current value without checking expirationgetLastUpdateTime()
β Returns the timestamp of the last streak updatetimeSinceLastUpdate()
β Returns how long ago the last streak bump occurredisCurrentlyExpired()
β Returnstrue
if the streak is expired right nowhasState()
β Returnstrue
if any streak data is savedclear()
β Deletes all streak data (value + timestamp)
You can also access period-related properties:
currentPeriodStart
β Returns theDateTime
representing the current aligned period startnextPeriodStart
β Returns theDateTime
when the next period will begintimeUntilNextPeriod
β Returns aDuration
until the next reset occurselapsedInCurrentPeriod
β How much time has passed since the period beganpercentElapsed
β A progress indicator (0.0 to 1.0) showing how far into the period we are
Best Streak Records:
The StreakTracker
includes a built-in records
property, powered by the BestRecord
service.
It automatically tracks the highest (or lowest) streak ever achieved, with optional record history, fallback values, and flexible record modes β all accessible through the records
API.
β± Available Periods (TimePeriod
) #
You can choose from a wide range of aligned time intervals:
-
Seconds:
seconds10
,seconds20
,seconds30
-
Minutes:
minutes1
,minutes2
,minutes3
,minutes5
,minutes10
,
minutes15
,minutes20
,minutes30
-
Hours:
hourly
,every2Hours
,every3Hours
,every6Hours
,every12Hours
-
Days and longer:
daily
,weekly
,monthly
Each period is aligned automatically β e.g., daily resets at midnight, weekly at the start of the week, monthly on the 1st.
β Define a Streak Tracker
final streak = StreakTracker('daily_exercise', period: TimePeriod.daily);
This creates a persistent streak tracker that:
- Uses the key
'daily_exercise'
- Tracks aligned daily periods (e.g. 00:00β00:00)
- Increases the streak when
bump()
is called - Resets automatically if a full period is missed
β‘ Mark a Period as Completed
await streak.bump();
This will:
- Reset the streak to 0 if the last bump was too long ago (missed period)
- Then increment the streak by 1
- Then update the internal timestamp to the current aligned time
π Get Current Streak Count
final current = await streak.currentStreak();
Returns the current streak (resets first if broken).
π§― Manually Reset the Streak
await streak.reset();
Sets the value back to 0 and clears the last update timestamp.
β Check if Streak Is Broken
final isBroken = await streak.isStreakBroken();
Returns true
if the last streak bump is too old (i.e. period missed).
π View Streak Age
final age = await streak.streakAge();
Returns how much time passed since the last bump (or null
if never set).
β³ See When the Streak Will Break
final time = await streak.nextResetTime();
Returns the timestamp of the next break opportunity (end of allowed window).
π Percent of Time Remaining
final percent = await streak.percentRemaining();
Returns a double
between 0.0
and 1.0
indicating time left before the streak is considered broken.
π Peek at the Current Value
final raw = await streak.peek();
Returns the current stored streak without checking if it expired.
π§ͺ Debug or Clear State
await streak.clear(); // Removes all saved state
final hasData = await streak.hasState(); // Checks if any value exists
β‘ Optional useCache
Parameter
Each utility accepts a useCache
flag:
final streak = StreakTracker(
'daily_exercise',
period: TimePeriod.daily,
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.
π§Ύ HistoryTracker<T>
β Persistent History Tracker #
β€΄οΈ Back -> Table of Contents
HistoryTracker<T>
makes it easy to store and manage persistent, ordered lists of items β like recent searches, viewed content, or activity logs. It automatically handles trimming, deduplication, and persistence, so you can focus on your appβs logic without worrying about list management.
It automatically:
- Keeps the most recent items first
- Limits the list to a maximum number of entries
- Optionally removes duplicates (so the newest version stays on top)
- Supports JSON, enum, and custom item types
- Works safely across isolates with optional caching
You can also plug it in easily using .historyTracker()
on any prf
adapter.
π§° Core Features #
add(value)
β Adds a new item to the front (most recent). Trims and deduplicates if neededsetAll(values)
β Replaces the entire history with a new listremove(value)
β Removes a single matching itemremoveWhere(predicate)
β Removes all matching items by conditionclear()
β Clears the entire list, resets to emptyremoveKey()
β Deletes the key from persistent storagegetAll()
β Returns the full history (most recent first)contains(value)
β Returns whether a given item existslength()
β Number of items currently in the listisEmpty()
β Whether the history is emptyfirst()
β Most recent item in the list, ornull
last()
β Oldest item in the list, ornull
exists()
β Whether this key exists in SharedPreferences
- Fields:
key
β The full key name used for persistenceuseCache
β Toggles between cachedPrf
or isolate-safePrfIso
maxLength
β The maximum number of items to keepdeduplicate
β If enabled, removes existing instances of an item before adding it
β Define a History Tracker
final history = HistoryTracker<String>('recent_queries');
This creates a persistent history list for 'recent_queries'
with a default max length of 50 items. You can customize:
maxLength
β maximum number of items retained (default: 50)deduplicate
β remove existing items before re-adding (default: false)useCache
β iftrue
will toggle off isolate safety (default: false)
HistoryTracker<T> supports out of the box (with zero setup) these types:
β
bool
,int
,double
,num
,String
,Duration
,DateTime
,Uri
,BigInt
,Uint8List
(binary data)List<bool>
,List<int>
,List<String>
,List<double>
,List<num>
,List<DateTime>
,List<Duration>
,List<Uint8List>
,List<Uri>
,List<BigInt>
For custom types, use one of the factory constructors:
π§± JSON Object History
final history = HistoryTracker.json<Book>(
'books_set',
fromJson: Book.fromJson,
toJson: (b) => b.toJson(),
);
π§ Enum History
final history = HistoryTracker.enumerated<LogType>(
'log_type_history',
values: LogType.values,
deduplicate: true,
);
β Add a New Entry
await history.add('search_term');
Adds an item to the front of the list. If deduplicate
is enabled, the item is moved to the front instead of duplicated.
π§Ί Replace the Entire List
await history.setAll(['one', 'two', 'three']);
Sets the full list. Will apply deduplication and trimming automatically if configured.
β Remove a Value
await history.remove('two');
Removes a single item from the history by value.
π§Ή Remove Matching Items
await history.removeWhere((item) => item.length > 5);
Removes all items that match a custom condition.
π§Ό Clear or Delete the History
await history.clear(); // Clears all values
await history.removeKey(); // Removes the key from preferences entirely
Use clear()
to reset the list but keep the key; removeKey()
to fully delete the key from storage.
π Read & Inspect History
final items = await history.getAll(); // Full list, newest first
final exists = await history.exists(); // true if key exists
final hasItem = await history.contains('abc'); // true if present
π’ Get Meta Info
final total = await history.length(); // Number of items
final empty = await history.isEmpty(); // Whether the list is empty
π― Get Specific Entries
final newest = await history.first(); // Most recent (or null)
final oldest = await history.last(); // Oldest (or null)
π Store Recently Viewed Models (with Deduplication)
final productHistory = HistoryTracker.json<Product>(
'recent_products',
fromJson: Product.fromJson,
toJson: (p) => p.toJson(),
deduplicate: true,
maxLength: 100,
);
π Track Reading Progress by Enum
enum ReadStatus { unread, reading, finished }
final readingHistory = HistoryTracker.enumerated<ReadStatus>(
'reading_statuses',
values: ReadStatus.values,
maxLength: 20,
);
π Store Recent Login Accounts
final logins = HistoryTracker<DateTime>(
'recent_logins',
deduplicate: true,
maxLength: 5,
);
π§ͺ Use a Custom Adapter for Byte-Chunks
final someCustomAdapter = SomeCustomAdapter(); // PrfAdapter<List<T>>
final hisory = someCustomAdapter.historyTracker(
'special_data',
maxLength: 20,
deduplicate: false,
);
β‘ Optional useCache
Parameter
Each utility accepts a useCache
flag:
final fastCache = HistoryTracker<int>(
'cached_ints',
useCache: true,
);
-
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.
π PeriodicCounter
Aligned Timed Counter #
β€΄οΈ Back -> Table of Contents
PeriodicCounter
is a persistent counter that automatically resets at the start of each aligned time period, such as daily, hourly, or every 10 minutes. Itβs perfect for tracking time-bound events like βdaily logins,β βhourly uploads,β or βweekly tasks,β without writing custom reset logic.
It handles:
- Aligned period math (e.g. resets every day at 00:00)
- Persistent storage with isolate safety
- Auto-expiring values based on time alignment
- Counter tracking with optional increment amounts
- Period progress and time tracking
π§ How to Use #
get()
β Returns the current counter value (auto-resets if needed)increment()
β Increments the counter, by a given amount (1 is the default)reset()
β Manually resets the counter and aligns the timestamp to the current period startpeek()
β Returns the current value without checking or triggering expirationraw()
β Alias forpeek()
(useful for debugging or display)isNonZero()
β Returnstrue
if the counter value is greater than zeroclearValueOnly()
β Resets only the counter, without modifying the timestampclear()
β Removes all stored values, including the timestamphasState()
β Returnstrue
if any persistent state existsisCurrentlyExpired()
β Returnstrue
if the counter would reset right nowgetLastUpdateTime()
β Returns the last reset-aligned timestamptimeSinceLastUpdate()
β Returns how long itβs been since the last reset
You can also access period-related properties:
currentPeriodStart
β Returns theDateTime
representing the current aligned period startnextPeriodStart
β Returns theDateTime
when the next period will begintimeUntilNextPeriod
β Returns aDuration
until the next reset occurselapsedInCurrentPeriod
β How much time has passed since the period beganpercentElapsed
β A progress indicator (0.0 to 1.0) showing how far into the period we are
β± Available Periods (TimePeriod
) #
You can choose from a wide range of aligned time intervals:
-
Seconds:
seconds10
,seconds20
,seconds30
-
Minutes:
minutes1
,minutes2
,minutes3
,minutes5
,minutes10
,
minutes15
,minutes20
,minutes30
-
Hours:
hourly
,every2Hours
,every3Hours
,every6Hours
,every12Hours
-
Days and longer:
daily
,weekly
,monthly
Each period is aligned automatically β e.g., daily resets at midnight, weekly at the start of the week, monthly on the 1st.
β Define a Periodic Counter
final counter = PeriodicCounter('daily_uploads', period: TimePeriod.daily);
This creates a persistent counter that automatically resets at the start of each aligned period (e.g. daily at midnight).
It uses the prefix 'daily_uploads'
to store:
- The counter value (
int
) - The last reset timestamp (
DateTime
aligned to period start)
β Increment the Counter
await counter.increment(); // adds 1
await counter.increment(3); // adds 3
You can increment by any custom amount. The value will reset if expired before incrementing.
π’ Get the Current Value
final count = await counter.get();
This returns the current counter value, automatically resetting it if the period expired.
π Peek at Current Value (Without Reset Check)
final raw = await counter.peek();
Returns the current stored value without checking expiration or updating anything.
Useful for diagnostics, stats, or UI display.
β Check If Counter Is Non-Zero
final hasUsage = await counter.isNonZero();
Returns true
if the current value is greater than zero.
π Manually Reset the Counter
await counter.reset();
Resets the value to zero and stores the current aligned timestamp.
βοΈ Clear Stored Counter Only (Preserve Timestamp)
await counter.clearValueOnly();
Resets the counter but keeps the current period alignment intact.
ποΈ Clear All Stored State
await counter.clear();
Removes both value and timestamp from persistent storage.
β Check if Any State Exists
final exists = await counter.hasState();
Returns true
if the counter or timestamp exist in SharedPreferences.
β Check if Current Period Is Expired
final expired = await counter.isCurrentlyExpired();
Returns true
if the stored timestamp is from an earlier period than now.
π View Timing Info
final last = await counter.getLastUpdateTime(); // last reset-aligned timestamp
final since = await counter.timeSinceLastUpdate(); // Duration since last reset
π Period Insight & Progress
final start = counter.currentPeriodStart; // start of this period
final next = counter.nextPeriodStart; // start of the next period
final left = counter.timeUntilNextPeriod; // how long until reset
final elapsed = counter.elapsedInCurrentPeriod; // time passed in current period
final percent = counter.percentElapsed; // progress [0.0β1.0]
β‘ Optional useCache
Parameter
Each utility accepts a useCache
flag:
final counter = PeriodicCounter(
'daily_uploads',
period: TimePeriod.daily
useCache: true,
);
-
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.
β³ RolloverCounter
Sliding Window Counter #
β€΄οΈ Back -> Table of Contents
RolloverCounter
is a persistent counter that automatically resets itself after a fixed duration from the last update. Ideal for tracking rolling activity windows, such as "submissions per hour", "attempts every 10 minutes", or "usage in the past day".
It handles:
- Time-based expiration with a sliding duration window
- Persistent storage using with full isolate-safety
- Seamless session persistence and automatic reset logic
- Rich time utilities to support countdowns, progress indicators, and timer-based UI logic
π§ How to Use #
get()
β Returns the current counter value (auto-resets if expired)increment([amount])
β Increases the count byamount
(default:1
)reset()
β Manually resets the counter and sets a new expiration timeclear()
β Deletes all stored state from preferenceshasState()
β Returnstrue
if any saved state existspeek()
β Returns the current value without triggering a resetgetLastUpdateTime()
β Returns the last update timestamp, ornull
if never usedisCurrentlyExpired()
β Returnstrue
if the current window has expiredtimeSinceLastUpdate()
β Returns how much time has passed since last usetimeRemaining()
β Returns how much time remains before auto-resetsecondsRemaining()
β Same as above, in secondspercentElapsed()
β Progress of the current window as a0.0β1.0
valuegetEndTime()
β Returns theDateTime
when the current window endswhenExpires()
β Completes when the reset window expires
β Define a Rollover Counter
final counter = RolloverCounter('usage_counter', resetEvery: Duration(minutes: 10));
This creates a persistent counter that resets automatically 10 minutes after the last update. It uses the key 'usage_counter'
to store:
- Last update timestamp
- Rolling count value
β Increment the Counter
await counter.increment(); // +1
await counter.increment(5); // +5
This also refreshes the rollover timer.
π Get the Current Value
final count = await counter.get(); // Auto-resets if expired
You can also check the value without affecting expiration:
final value = await counter.peek();
π Reset or Clear the Counter
await counter.reset(); // Sets count to 0 and updates timestamp
await counter.clear(); // Deletes all stored state
π Check Expiration Status
final expired = await counter.isCurrentlyExpired(); // true/false
You can also inspect metadata:
final lastUsed = await counter.getLastUpdateTime();
final since = await counter.timeSinceLastUpdate();
β³ Check Time Remaining
final duration = await counter.timeRemaining();
final seconds = await counter.secondsRemaining();
final percent = await counter.percentElapsed(); // 0.0β1.0
These can be used for progress bars, countdowns, etc.
π Get the End Time
final end = await counter.getEndTime(); // DateTime when it auto-resets
π€ Wait for Expiry
await counter.whenExpires(); // Completes when timer ends
Useful for polling, UI disable windows, etc.
π§ͺ Test Utilities
await counter.clear(); // Removes all saved values
final exists = await counter.hasState(); // true if anything stored
β‘ Optional useCache
Parameter
Each utility accepts a useCache
flag:
final counter = RolloverCounter(
'usage_counter',
resetEvery: Duration(minutes: 10),
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.
π ActivityCounter
β Persistent Activity Tracker #
β€΄οΈ Back -> Table of Contents
ActivityCounter
is a powerful utility for tracking user activity over time, across hour
, day
, month
, and year
spans. It is designed for scenarios where you want to record frequency, analyze trends, or generate statistics over long periods, with full persistence across app restarts and isolates.
It handles:
- Span-based persistent counters (hourly, daily, monthly, yearly)
- Automatic time-based bucketing using
DateTime.now()
- Per-span data access and aggregation
- Querying historical data without manual cleanup
- Infinite year tracking
π§ How to Use #
add(int amount)
β Adds to the current time bucket (across all spans)increment()
β Shortcut foradd(1)
amountThis(span)
β Gets current value for nowβshour
,day
,month
, oryear
amountFor(span, date)
β Gets the value for any given date and spansummary()
β Returns a map of all spans for the current time ({year: X, month: Y, ...}
)total(span)
β Total sum of all recorded entries in that spanall(span)
β Returns{index: value}
map of non-zero entries for a spanmaxValue(span)
β Returns the largest value ever recorded for the spanactiveDates(span)
β Returns a list ofDateTime
objects where any activity was trackedhasAnyData()
β Returnstrue
if any activity has ever been recordedthisHour
,today
,thisMonth
,thisYear
β Shorthand foramountThis(...)
reset()
β Clears all data in sall spansclear(span)
β Clears a single spanclearAllKnown([...])
β Clears multiple spans at onceremoveAll()
β Permanently deletes all stored data for this counter
ActivityCounter tracks activity simultaneously across all of the following spans:
TimeSpan.hour
β hourly activity (rolling 24-hour window)TimeSpan.day
β daily activity (up to 31 days)TimeSpan.month
β monthly activity (up to 12 months)TimeSpan.year
β yearly activity (from year 2000 onward, uncapped)
β Define an Activity Counter
final counter = ActivityCounter('user_events');
This creates a persistent activity counter with a unique prefix. It automatically manages:
- Hourly counters
- Daily counters
- Monthly counters
- Yearly counters
β Add or Increment Activity
await counter.add(5); // Adds 5 to all time buckets
await counter.increment(); // Adds 1 (shortcut)
Each call will update the counter in all spans (hour
, day
, month
, and year
) based on DateTime.now()
.
π Get Current Time Span Counts
final currentHour = await counter.thisHour;
final today = await counter.today;
final thisMonth = await counter.thisMonth;
final thisYear = await counter.thisYear;
You can also use:
await counter.amountThis(TimeSpan.day);
await counter.amountThis(TimeSpan.month);
π Read Specific Time Buckets
final value = await counter.amountFor(TimeSpan.year, DateTime(2022));
Works for any TimeSpan
and DateTime
.
π Get Summary of All Current Spans
final summary = await counter.summary();
// {TimeSpan.year: 12, TimeSpan.month: 7, ...}
π’ Get Total Accumulated Value
final sum = await counter.total(TimeSpan.day); // Sum of all recorded days
π View All Non-Zero Buckets
final map = await counter.all(TimeSpan.month); // {5: 3, 6: 10, 7: 1}
Returns a {index: value}
map of all non-zero entries.
π© View Active Dates
final days = await counter.activeDates(TimeSpan.day);
Returns a list of DateTime
objects representing each tracked entry.
π View Max Value in Span
final peak = await counter.maxValue(TimeSpan.hour);
Returns the highest value recorded in that span.
π Check If Any Data Exists
final exists = await counter.hasAnyData();
π§Ό Reset or Clear Data
await counter.reset(); // Clears all spans
await counter.clear(TimeSpan.month); // Clears only month data
await counter.clearAllKnown([TimeSpan.year, TimeSpan.hour]);
β Permanently Remove Data
await counter.removeAll();
Deletes all stored values associated with this key. Use this in tests or during debug cleanup.
β‘ Optional useCache
Parameter
Each utility accepts a useCache
flag:
final counter = ActivityCounter(
'user_events',
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.
π
BestRecord
β Track Best Values Over Time #
β€΄οΈ Back β Table of Contents
BestRecord
makes it simple to track and store the best (or worst) values your app has ever seen β like high scores, fastest times, longest streaks, or minimum records. It automatically compares new values, updates the record if appropriate, and keeps a persistent, timestamped history of all bests.
It automatically:
- Tracks the highest or lowest value, based on
RecordMode
(max
ormin
) - Stores a history of record-breaking entries
- Provides fallback records if no data exists
- Works safely across isolates with optional caching
π§° Core Features #
update(value)
β Adds a new record only if it beats the current bestgetBestRecord()
β Returns the best value saved so fargetBestEntry()
β Returns the best record asRecordEntry
(with value + date)getBestOrFallback()
β Returns the best record or fallback if none existgetBestDate()
β Returns the date when the best record was setgetHistory()
β Returns the full record history (most recent first)reset()
β Clears all saved recordsremoveKey()
β Deletes the record key from persistent storagemanualSet(value)
β Force-adds a record without comparisonremoveAt(index)
β Removes a record at a specific index in historyremoveWhere(predicate)
β Removes all records matching a conditionfirst()
β Returns the most recent record in history, ornull
last()
β Returns the oldest record in history, ornull
exists()
β Checks if any records are saved
-
Fields:
mode
β Whether to track the maximum or minimum valuehistoryLength
β Maximum number of records to keepfallback
β Default record if no entries existkey
β Unique key used for persistenceuseCache
β Enables fast, non-isolate-safe mode
β Define a BestRecord Service
final record = BestRecord('highscore', mode: historyLength: 5);
This creates a persistent best-record tracker that saves the highest value ever achieved.
It uses the key 'best_record_highscore'
to store:
- Best value and its timestamp
- A rolling history of the top 5 records
β Update the Record
await record.update(150); // Only saves if 150 > current best
await record.update(200); // Updates best to 200 if higher
New values are only saved if they beat the current best (or are lower, in min
mode).
π Get the Best Value
final best = await record.getBestRecord(); // e.g., 200
final entry = await record.getBestEntry(); // RecordEntry(value, date)
You can also fetch a fallback:
final bestOrFallback = await record.getBestOrFallback();
π View Record History
final history = await record.getHistory(); // List<RecordEntry>
Example use: display top 5 scores or fastest runs.
π Reset or Clear Records
await record.reset(); // Clears all saved records
await record.removeKey(); // Removes the key from storage entirely
βοΈ Manually Add or Remove Records
await record.manualSet(300); // Force-add 300, bypassing checks
await record.removeAt(0); // Remove the most recent record
await record.removeWhere((e) => e.value < 100); // Remove low scores
π Inspect Record Details
final date = await record.getBestDate(); // When the best was set
final first = await record.first(); // Most recent record
final last = await record.last(); // Oldest record
π§ͺ Check and Manage State
final exists = await record.exists(); // true if any records saved
β‘ Optional useCache
Parameter
Each utility accepts a useCache
flag:
final record = BestRecord(
'highscore',
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.
π’ BasicCounter
β Simple Persistent Counter #
β€΄οΈ Back β Table of Contents
BasicCounter
gives you a simple, persistent counter with no expiration or time-based reset β perfect for tracking things like total app launches, button taps, or items added. It automatically persists across sessions and works safely with or without isolate-safe caching.
It automatically:
- Stores an integer counter with an optional last update timestamp
- Provides reset, clear, and direct access methods
- Supports thread-safe (
synchronized
) operations - Works with or without in-memory (
useCache
) mode
π§° Core Features #
increment([amount])
β Increases the counter byamount
(default:1
)get()
β Returns the current value (auto-checks expiration β always valid inBasicCounter
)peek()
β Returns the current value without checking expirationraw()
β Returns the stored value, even if stalereset()
β Resets counter to fallback value (0
) and updates last update timeclearValueOnly()
β Resets value to0
but keeps the last update timestampclear()
β Deletes both the value and last update timestamp from storagehasState()
β Checks if any saved data (value or timestamp) existsexists()
β Same ashasState()
, checks if stored data existsisNonZero()
β Checks if the current counter value is greater than0
isCurrentlyExpired()
β Checks if the counter is expired (false
forBasicCounter
)getLastUpdateTime()
β Returns the last time the counter was updated (ornull
)timeSinceLastUpdate()
β Returns duration since last update (ornull
if never)fallbackValue()
β Always returns0
β the default reset value
-
Fields:
key
β Unique string key used for persistenceuseCache
β Enables in-memory (non-isolate-safe) caching
β Define a Basic Counter
final counter = BasicCounter('my_counter');
This creates a persistent counter that:
- Uses the key
'my_counter_basic'
- Tracks a simple integer value
- Never expires or resets automatically
β Increment the Counter
await counter.increment(); // +1
await counter.increment(5); // +5
This increases the counter value by the given amount.
π Get Current Counter Value
final current = await counter.get();
Returns the current counter value.
π Peek at Stored Value
final peeked = await counter.peek();
Returns the stored value without refreshing or checking expiration.
π¦ Get Raw Stored Value
final raw = await counter.raw();
Returns the stored value directly, even if stale.
π Reset the Counter
await counter.reset();
Sets the counter back to 0
and updates the last update time.
π§Ή Clear Only the Value
await counter.clearValueOnly();
Resets the value to 0
but keeps the last update timestamp.
π§ͺ Clear All State
await counter.clear();
Removes both the counter value and its timestamp from storage.
β Check If Any Data Exists
final hasState = await counter.hasState();
final exists = await counter.exists(); // same as hasState
Returns true
if any saved value or timestamp exists.
π Check If Counter Is Non-Zero
final isActive = await counter.isNonZero();
Returns true
if the counter value is greater than zero.
β° Check Expiration (Always False)
final expired = await counter.isCurrentlyExpired();
Returns false
β BasicCounter
never expires.
π Get Last Update Time
final lastUpdate = await counter.getLastUpdateTime();
Returns the DateTime
when the counter was last updated (or null
if never).
β Time Since Last Update
final since = await counter.timeSinceLastUpdate();
Returns how long ago the counter was updated (or null
if never).
β‘ Optional useCache
Parameter
Each utility accepts a useCache
flag:
final counter = BasicCounter(
'my_counter',
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.