{"id":28389323,"url":"https://github.com/jozzdart/prf","last_synced_at":"2025-10-14T02:14:35.345Z","repository":{"id":288350960,"uuid":"967759578","full_name":"jozzdart/prf","owner":"jozzdart","description":"Easily save and load values locally. Effortless local persistence with type safety and zero boilerplate. Just get, set, and go.","archived":false,"fork":false,"pushed_at":"2025-09-29T16:32:38.000Z","size":364,"stargazers_count":8,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-29T18:30:39.531Z","etag":null,"topics":["dart","flutter","jozz","local-storage","localstorage","persistence","persistent-storage","shared-preferences","sharedpreferences"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/prf","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/jozzdart.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-04-17T00:44:33.000Z","updated_at":"2025-09-29T16:32:16.000Z","dependencies_parsed_at":"2025-04-17T13:34:57.362Z","dependency_job_id":"bc1393b8-5eae-40ed-a781-e68c018dc50c","html_url":"https://github.com/jozzdart/prf","commit_stats":null,"previous_names":["jozzzzep/prf","jozzdart/prf"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/jozzdart/prf","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jozzdart%2Fprf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jozzdart%2Fprf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jozzdart%2Fprf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jozzdart%2Fprf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jozzdart","download_url":"https://codeload.github.com/jozzdart/prf/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jozzdart%2Fprf/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279017671,"owners_count":26086125,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-14T02:00:06.444Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["dart","flutter","jozz","local-storage","localstorage","persistence","persistent-storage","shared-preferences","sharedpreferences"],"created_at":"2025-05-31T00:38:30.423Z","updated_at":"2025-10-14T02:14:35.332Z","avatar_url":"https://github.com/jozzdart.png","language":"Dart","funding_links":["https://buymeacoffee.com/yosefd99v"],"categories":[],"sub_categories":[],"readme":"![img](https://i.imgur.com/pAUltto.png)\n\n\u003ch3 align=\"center\"\u003e\u003ci\u003eDefine. Get. Set. Done.\u003c/i\u003e\u003c/h3\u003e\n\u003cp align=\"center\"\u003e\n        \u003cimg src=\"https://img.shields.io/codefactor/grade/github/jozzdart/prf/main?style=flat-square\"\u003e\n        \u003cimg src=\"https://img.shields.io/github/license/jozzdart/prf?style=flat-square\"\u003e\n        \u003cimg src=\"https://img.shields.io/pub/points/prf?style=flat-square\"\u003e\n        \u003cimg src=\"https://img.shields.io/pub/v/prf?style=flat-square\"\u003e\n        \n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://buymeacoffee.com/yosefd99v\" target=\"https://buymeacoffee.com/yosefd99v\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Buy%20me%20a%20coffee-Support (:-blue?logo=buymeacoffee\u0026style=flat-square\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nNo boilerplate. No repeated strings. No setup. Define your variables once, then `get()` and `set()` them anywhere with zero friction. `prf` makes local persistence faster, simpler, and easier to scale, with 20+ built-in types and a clean, type-safe API. Designed to fully replace raw use of `SharedPreferences`.\n\n#### Table of Contents\n\n- [**Introduction**](#define--get--set--done)\n- [Why Use `prf`?](#why-use-prf)\n- [**SharedPreferences** vs `prf`](#sharedpreferences-vs-prf)\n- [Setup \u0026 Basic Usage (Step-by-Step)](#-setup--basic-usage-step-by-step)\n- [Available Methods and Supported Types](#-available-methods-and-supported-types)\n- [Accessing `prf` Without async](#-accessing-prf-without-async)\n- [Migrating from _SharedPreferences_ to `prf`](#-migrating-from-sharedpreferences-to-prf)\n- [Recommended Companion Packages](#-recommended-companion-packages)\n- [Why `prf` Wins in Real Apps](#-why-prf-wins-in-real-apps)\n- [Adding Custom prfs (Advanced)](#how-to-add-custom-prf-types)\n- [More `jozz` Packages](#-more-jozz-packages)\n\n# Define → Get → Set → Done\n\nJust define your variable once — no strings, no boilerplate:\n\n```dart\nfinal username = Prf\u003cString\u003e('username');\n```\n\nThen get it:\n\n```dart\nfinal value = await username.get();\n```\n\nOr set it:\n\n```dart\nawait username.set('Joey');\n```\n\nThat’s it. You're done. Works out of the box with all of these:\n\n- `bool` `int` `double` `String` `num` `Duration` `DateTime` `BigInt` `Uri` `Uint8List` (binary)\n- Also lists `List\u003cString\u003e` `List\u003cint\u003e` `List\u003c***\u003e` of all supported types!\n- [JSON \u0026 enums](#-available-methods-and-supported-types)\n\n\u003e All supported types use efficient binary encoding under the hood for optimal performance and minimal storage footprint — no setup required. Just use `Prf\u003cT\u003e` with any listed type, and everything works seamlessly.\n\n### Why Use `prf`\n\nWorking with `SharedPreferences` often leads to:\n\n- Repeated string keys\n- Manual casting and null handling\n- Verbose async boilerplate\n- Scattered, hard-to-maintain logic\n\n`prf` solves all of that with a **one-line variable definition** that’s **type-safe**, **cached**, and **instantly usable** throughout your app. No key management, no setup, no boilerplate, no `.getString(...)` everywhere.\n\n### What Sets `prf` Apart?\n\n- ✅ **Single definition** — just one line to define, then reuse anywhere\n- ✅ **Type-safe** — no casting, no runtime surprises\n- ✅ **Automatic caching** — with `Prf\u003cT\u003e` for fast access\n- ✅ **Easy isolate safety** — with `.isolated`\n- ✅ **Lazy initialization** — no need to call `SharedPreferences.getInstance()` or anything.\n- ✅ **Supports more than just primitives** — [20+ types](#-available-methods-and-supported-types), `Enums` \u0026 `JSON`\n- ✅ **Built for testing** — easily reset, override, or mock storage\n- ✅ **Cleaner codebase** — no more scattered `prefs.get...()` or typo-prone string keys\n\n# `SharedPreferences` vs `prf`\n\n[⤴️ Back](#table-of-contents) -\u003e Table of Contents\n\n| Feature                         | `SharedPreferences` (raw)                                            | `prf`                                                                                                 |\n| ------------------------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |\n| **Define Once, Reuse Anywhere** | ❌ Manual strings everywhere                                         | ✅ One-line variable definition                                                                       |\n| **Type Safety**                 | ❌ Requires manual casting                                           | ✅ Fully typed, no casting needed                                                                     |\n| **Supports Advanced Types**     | ❌ No - only **5** types.                                            | ✅ Built-in support for `20+ types` and supports `enums` \u0026 `JSON`                                     |\n| **Readability**                 | ❌ Repetitive and verbose                                            | ✅ Clear, concise, expressive                                                                         |\n| **Centralized Keys**            | ❌ You manage key strings                                            | ✅ Keys are defined as variables                                                                      |\n| **Lazy Initialization**         | ❌ Must await `getInstance()` manually                               | ✅ Internally managed                                                                                 |\n| **Supports Primitives**         | ✅ Yes                                                               | ✅ Yes                                                                                                |\n| **Isolate \u0026 Caching**           | ⚠️ Partial — must manually choose between caching or no-caching APIs | ✅ Just `.isolate` for full isolate-safety\u003cbr\u003e✅ `Prf\u003cT\u003e` for faster cached access (not isolate-safe) |\n\n### Code Comparison\n\n**Using `SharedPreferences`:**\n\n```dart\nfinal prefs = await SharedPreferences.getInstance();\nawait prefs.setString('username', 'Joey');\nfinal username = prefs.getString('username') ?? '';\n```\n\n**Using `prf` with cached access (`Prf\u003cT\u003e`):**\n\n```dart\nfinal username = Prf\u003cString\u003e('username');\nawait username.set('Joey');\nfinal name = await username.get();\n```\n\n**Using `prf` with isolate-safe access (`PrfIso\u003cT\u003e`):**\n\n```dart\nfinal username = Prf\u003cString\u003e('username').isolated;\nawait username.set('Joey');\nfinal name = await username.get();\n```\n\nIf you're tired of:\n\n- Duplicated string keys\n- Manual casting and null handling\n- Scattered async boilerplate\n\nThen `prf` is your drop-in solution for **fast, safe, scalable, and elegant local persistence**.\n\n# 🚀 Setup \u0026 Basic Usage (Step-by-Step)\n\n[⤴️ Back](#table-of-contents) -\u003e Table of Contents\n\n### Step 1: Add `prf` to your `pubspec.yaml`\n\n```yaml\ndependencies:\n  prf: ^latest\n```\n\nThen run:\n\n```bash\nflutter pub get\n```\n\n---\n\n### Step 2: Define Your Variable\n\nYou only need **one line** to create a saved variable.  \nFor example, to save how many coins a player has:\n\n```dart\nfinal playerCoins = Prf\u003cint\u003e('player_coins');\n```\n\n\u003e This means:\n\u003e\n\u003e - You're saving an `int` (number)\n\u003e - The key is `'player_coins'`\n\n---\n\n### Step 3: Save a Value\n\nTo give the player 100 coins:\n\n```dart\nawait playerCoins.set(100);\n```\n\n---\n\n### Step 4: Read the Value\n\nTo read how many coins the player has:\n\n```dart\nfinal coins = await playerCoins.get();\n```\n\n```dart\nprint('Coins: $coins'); // 100\n```\n\nThat’s it! 🎉 You don’t need to manage string keys or setup anything. Just define once, then use anywhere in your app.\n\n---\n\n### Step 5 (Optional): Use `.prf\u003cT\u003e()` Shortcut\n\nInstead of defining the key explicitly, you can use the `.prf\u003cT\u003e()` extension on a string:\n\n```dart\nfinal playerCoins = 'player_coins'.prf\u003cint\u003e();\n```\n\nFrom there it behave the same as defining using `Prf\u003cT\u003e`\n\n```dart\nawait playerCoins.set(100);\nfinal coins = await playerCoins.get();\n```\n\n```dart\nprint('Coins: $coins');\n```\n\nThis works exactly the same — just a stylistic preference if you like chaining on string keys.\n\n# 📖 Available Methods and Supported Types\n\n\u003e [⤴️ Back](#table-of-contents) -\u003e Table of Contents\n\n### ✅ All `Prf\u003cT\u003e` types support these `methods` out of the box\n\n- **`get()`** → returns the current value (cached or from disk)\n- **`set(value)`** → saves the value and updates the cache (if applicable)\n- **`remove()`** → deletes the value from storage (and cache if applicable)\n- **`isNull()`** → returns `true` if the value is `null`\n- **`getOrFallback(fallback)`** → returns the value or a fallback if `null`\n- **`existsOnPrefs()`** → checks if the key exists in storage\n- **`getOrDefault()`** → returns the value, or throws if no value exists and no default is defined (safe alternative to assuming non-null values)\n\n### 📦 Supported `Types`:\n\n```dart\nfinal someData = Prf\u003cT\u003e('key');\n```\n\nAll of these work automatically **(practically every type)**:\n\n- `bool`, `int`, `double`, `num`, `String`, `Duration`, `DateTime`, `Uri`, `BigInt`, `Uint8List` (binary)\n- `List\u003cbool\u003e`, `List\u003cint\u003e`, `List\u003cString\u003e`, `List\u003cdouble\u003e`, `List\u003cnum\u003e`, `List\u003cDateTime\u003e`, `List\u003cDuration\u003e`, `List\u003cUint8List\u003e`, `List\u003cUri\u003e`, `List\u003cBigInt\u003e`\n\n\u003e All supported types use efficient binary encoding under the hood for optimal performance and minimal storage footprint — no setup required. Just use `Prf\u003cT\u003e` and everything works seamlessly.\n\n### 🔧 Specialized Types - `Enums` \u0026 `JSON`\n\nFor enums and custom models, use the built-in factory helpers:\n\n- `Prf.enumerated\u003cT\u003e()` → enum value\n- `Prf.enumeratedList\u003cT\u003e()` → list of enum values\n- `Prf.json\u003cT\u003e()` → custom model object\n- `Prf.jsonList\u003cT\u003e()` → list of custom model objects\n\n* `Prf.cast\u003cT, TCast\u003e()` → custom behavior\n\n---\n\n#### 🛰 Need Isolate Safety?\n\nEvery `Prf` object supports the `.isolated` getter — no matter the type (enums, bytes, JSON, lists, etc).  \nIt returns a `PrfIso` that works safely across isolates (no caching, always reads from disk).\n\nThese are practically the same:\n\n```dart\nfinal safeUser = Prf\u003cString\u003e('username').isolated; // Same\nfinal safeUser = PrfIso\u003cString\u003e('username');       // Same\n```\n\n---\n\n### 🎯 Example: Persisting an `Enum`\n\nDefine your enum:\n\n```dart\nenum AppTheme { light, dark, system }\n```\n\nStore it using `Prf.enumerated` (cached) or `PrfIso.enumerated` (isolate-safe):\n\n```dart\nfinal appTheme = Prf.enumerated\u003cAppTheme\u003e(\n  'app_theme',\n  values: AppTheme.values,\n);\n```\n\nUsage:\n\n```dart\nfinal currentTheme = await appTheme.get(); // AppTheme.light / dark / system\nawait appTheme.set(AppTheme.dark);\n```\n\n---\n\n### 📚 Persisting a `List` of `Enums`\n\nDefine your enum:\n\n```dart\nenum Permission { read, write, delete }\n```\n\nStore a list using `Prf.enumeratedList` (cached) or `PrfIso.enumeratedList` (isolate-safe):\n\n```dart\nfinal permissions = Prf.enumeratedList\u003cPermission\u003e(\n  'user_permissions',\n  values: Permission.values,\n);\n```\n\nUsage:\n\n```dart\nfinal current = await permissions.get(); // [Permission.read, Permission.write]\nawait permissions.set([Permission.read, Permission.delete]);\n```\n\n---\n\n### 🧠 Custom Types? No Problem\n\nWant to persist something more complex?  \nUse `Prf.json\u003cT\u003e()` or `PrfIso.json\u003cT\u003e()` with any model that supports `toJson` and `fromJson`:\n\n```dart\nfinal userData = Prf.json\u003cUser\u003e(\n  'user',\n  fromJson: (json) =\u003e User.fromJson(json),\n  toJson: (user) =\u003e user.toJson(),\n);\n\n```\n\n---\n\n### 🧠 Complex Lists? Just Use `jsonList`\n\nFor model lists, use `Prf.jsonList\u003cT\u003e()` or `PrfIso.jsonList\u003cT\u003e()`:\n\n```dart\nfinal favoriteBooks = Prf.jsonList\u003cBook\u003e(\n  'favorite_books',\n  fromJson: (json) =\u003e Book.fromJson(json),\n  toJson: (book) =\u003e book.toJson(),\n);\n```\n\nUsage:\n\n```dart\nawait favoriteBooks.set([book1, book2]);\nfinal list = await favoriteBooks.get(); // List\u003cBook\u003e\n```\n\n---\n\n### 🧩 Custom Casting Adapter with `.cast()`\n\nNeed to persist a custom object that can be converted to a supported type (like `String`, `int` and all 20+ types)?\nUse the `.cast()` factory to define **on-the-fly adapters** with custom encode/decode logic — no full adapter class needed!\n\n```dart\nfinal langPref = Prf.cast\u003cLocale, String\u003e(\n  'saved_language',\n  encode: (locale) =\u003e locale.languageCode,\n  decode: (string) =\u003e string == null ? null : Locale(string),\n);\n```\n\n- `T` → your custom type (e.g., `Locale`)\n- `TCast` → any built-in supported type (e.g., `String`, `int`, `List\u003cString\u003e`, etc)\n- `encode` → how to convert `T` to `TCast`\n- `decode` → how to restore `T` from `TCast`\n\nGreat for storing objects that don’t need full `toJson()` support — just convert to a native type and you're done!\n\n# ⚡ Accessing `prf` Without Async\n\n[⤴️ Back](#table-of-contents) -\u003e Table of Contents\n\nIf you want instant, non-async access to a stored value, you can pre-load it into memory.\nUse `Prf.value\u003cT\u003e()` to create a `prf` object that automatically initializes and caches the value.\n\nExample:\n\n```dart\nfinal userScore = await Prf.value\u003cint\u003e('user_score');\n\n// Later, anywhere — no async needed:\nprint(userScore.cachedValue); // e.g., 42\n```\n\n- `Prf.value\u003cT\u003e()` reads the stored value once and caches it.\n- You can access `.cachedValue` instantly after initialization.\n- If no value was stored yet, `.cachedValue` will be the `defaultValue` or `null`.\n\n✅ Best for fast access inside UI widgets, settings screens, and forms.  \n⚠️ Not suitable for use across isolates — use `.isolated` or `PrfIso\u003cT\u003e` for isolate safety.\n\n### Quick Summary\n\n- `await Prf.value\u003cT\u003e()` → loads and caches the value.\n- `.cachedValue` → direct, instant access afterward.\n- No async needed for future reads!\n\n### 💡 Altervative - `.prf()` from String Keys\n\n```dart\nfinal username = 'username'.prf\u003cString\u003e();\nawait username.set('Joey');\nfinal name = await username.get();\n```\n\nIsolate-safe version:\n\n```dart\nfinal username = 'username'.prf\u003cString\u003e().isolated;\nawait username.set('Joey');\nfinal name = await username.get();\n```\n\n# 🔁 Migrating from SharedPreferences to `prf`\n\n[⤴️ Back](#table-of-contents) -\u003e Table of Contents\n\nWhether you're using the modern `SharedPreferencesAsync` or the legacy `SharedPreferences`, migrating to `prf` is simple and gives you cleaner, type-safe, and scalable persistence — without losing any existing data.\n\nIn fact, you can use `prf` with your current keys and values out of the box, preserving all previously stored data. But while backwards compatibility is supported, we recommend reviewing [all built-in types and usage](#-available-methods-and-supported-types) that `prf` provide — which may offer a cleaner, more powerful way to structure your logic going forward, without relying on legacy patterns or custom code.\n\n---\n\n### ✅ If you're already using `SharedPreferencesAsync`\n\nYou can switch to `prf` with **zero configuration** — just use the same keys.\n\n#### Before (`SharedPreferencesAsync`):\n\n```dart\nfinal prefs = SharedPreferencesAsync();\nawait prefs.setBool('dark_mode', true);\nfinal isDark = await prefs.getBool('dark_mode');\n```\n\n#### After (`prf`):\n\n```dart\nfinal darkMode = Prf\u003cbool\u003e('dark_mode');\nawait darkMode.set(true);\nfinal isDark = await darkMode.get();\n```\n\n- ✅ **As long as you're using the same keys and types, your data will still be there. No migration needed.**\n- 🧼 **Or — if you don't care about previously stored values**, you can start fresh and use `prf` types right away. They’re ready to go with clean APIs and built-in caching for all dart types, `enums`, `JSONs`, and more.\n\n---\n\n### ✅ If you're using the legacy `SharedPreferences` class\n\nYou can still switch to `prf` using the same keys:\n\n#### Before (`SharedPreferences`):\n\n```dart\nfinal prefs = await SharedPreferences.getInstance();\nawait prefs.setString('username', 'Joey');\nfinal name = prefs.getString('username');\n```\n\n#### After (`prf`):\n\n```dart\nfinal username = Prf\u003cString\u003e('username');\nawait username.set('Joey');\nfinal name = await username.get();\n```\n\n- ⚠️ `prf` uses **SharedPreferencesAsync**, which is isolate-safe, more robust — and **does not share data with the legacy `SharedPreferences` API**. The legacy API is **already planned for deprecation**, so [migrating](#️-if-your-app-is-already-in-production-using-sharedpreferences) away from it is strongly recommended.\n- ✅ If you're still in development, you can safely switch to `prf` now — saved values from before will not be accessible, but that's usually fine while iterating.\n\n\u003e The migration bellow automatically migrates old values into the new backend if needed.\n\u003e Safe to call multiple times — it only runs once.\n\n---\n\n### ⚠️ If your app is already in production using `SharedPreferences`\n\nIf your app previously used `SharedPreferences` (the legacy API), and you're now using `prf` (which defaults to `SharedPreferencesAsync`):\n\n- You **must run a one-time migration** to move your data into the new backend (especially on Android, where the storage backend switches to DataStore).\n\nRun this **before any reads or writes**, ideally at app startup:\n\n```dart\nawait PrfService.migrateFromLegacyPrefsIfNeeded();\n```\n\n\u003e This ensures your old values are migrated into the new system.\n\u003e It is safe to call multiple times — migration will only occur once.\n\n### Summary\n\n| Case                                   | Do you need to migrate?     | Do your keys stay the same? |\n| -------------------------------------- | --------------------------- | --------------------------- |\n| Using `SharedPreferencesAsync`         | ❌ No migration needed      | ✅ Yes                      |\n| Using `SharedPreferences` (dev only)   | ❌ No migration needed      | ✅ Yes                      |\n| Using `SharedPreferences` (production) | ✅ Yes — run migration once | ✅ Yes                      |\n| Starting fresh                         | ❌ No migration, no legacy  | 🔄 You can pick new keys    |\n\nWith `prf`, you get:\n\n- 🚀 **Type-safe, reusable variables**\n- 🧠 **Cleaner architecture**\n- 🔄 **Built-in in-memory caching**\n- 🔐 **Isolate-safe behavior** with `SharedPreferencesAsync`\n- 📦 **Out-of-the-box support** for `20+ types`, `enums`, full `JSON` models and more\n\n# 🌟 Recommended Companion Packages\n\n[⤴️ Back](#table-of-contents) -\u003e Table of Contents\n\nIn addition to typed variables, `prf` connects seamlessly with **additional persistence power tools** — packages built specifically to extend the capabilities of `prf` into advanced real-world use cases.  \nThese tools offer plug-and-play solutions that carry over the same caching, async-safety, and persistence guarantees you expect from `prf`.\n\nPackages:  \n**`limit` package** → https://pub.dev/packages/limit  \n**`track` package** → https://pub.dev/packages/track\n\n- ⏲ **[`limit`](https://pub.dev/packages/limit)** — manage cooldowns and rate limits across sessions and isolates. Includes:\n\n  - **Cooldown** (fixed-time delays, e.g. daily rewards, retry timers)\n  - **RateLimiter** (token bucket rate limiting, e.g. 1000 actions per 15 minutes)\n\n- 🔥 **[`track`](https://pub.dev/packages/track)** — track progress, activity, and usage over time. Includes:\n\n  - **StreakTracker** (aligned streak tracking, e.g. daily habits)\n  - **HistoryTracker** (rolling lists of recent items with optional deduplication)\n  - **PeriodicCounter** (auto-reset counters per period, e.g. daily tasks)\n  - **RolloverCounter** (sliding-window counters, e.g. attempts per hour)\n  - **ActivityCounter** (detailed time-based activity stats)\n  - **BestRecord** (coming soon: track best performances or highscores)\n\n# 🔍 Why `prf` Wins in Real Apps\n\n[⤴️ Back](#table-of-contents) -\u003e Table of Contents\n\nWorking with `SharedPreferences` directly can quickly become **verbose, error-prone, and difficult to scale**. Whether you’re building a simple prototype or a production-ready app, clean persistence matters.\n\n### ❌ The Problem with Raw SharedPreferences\n\nEven in basic use cases, you're forced to:\n\n- Reuse raw string keys (risk of typos and duplication)\n- Manually cast and fallback every read\n- Handle async boilerplate (`getInstance`) everywhere\n- Encode/decode complex types manually\n- Spread key logic across multiple files\n\nLet’s see how this unfolds in practice.\n\n### Example: Saving and Reading Multiple Values\n\n**Goal**: Save and retrieve a `username`, `isFirstLaunch`, and a `signupDate`.\n\n### SharedPreferences (verbose and repetitive)\n\n```dart\nfinal prefs = await SharedPreferences.getInstance();\n\n// Save values\nawait prefs.setString('username', 'Joey');\nawait prefs.setBool('is_first_launch', false);\nawait prefs.setString(\n  'signup_date',\n  DateTime.now().toIso8601String(),\n);\n\n// Read values\nfinal username = prefs.getString('username') ?? '';\nfinal isFirstLaunch = prefs.getBool('is_first_launch') ?? true;\nfinal signupDateStr = prefs.getString('signup_date');\nfinal signupDate = signupDateStr != null\n  ? DateTime.tryParse(signupDateStr)\n  : null;\n```\n\n🔻 **Issues:**\n\n- Repeated string keys — no compile-time safety\n- Manual fallback handling and parsing\n- No caching — every `.get` hits disk\n- Boilerplate increases exponentially with more values\n\n### Example: Same Logic with `prf`\n\n```dart\nfinal username = Prf\u003cString\u003e('username');\nfinal isFirstLaunch = Prf\u003cbool\u003e('is_first_launch', defaultValue: true);\nfinal signupDate = Prf\u003cDateTime\u003e('signup_date');\n\n// Save\nawait username.set('Joey');\nawait isFirstLaunch.set(false);\nawait signupDate.set(DateTime.now());\n\n// Read\nfinal name = await username.get();         // 'Joey'\nfinal first = await isFirstLaunch.get();   // false\nfinal date = await signupDate.get();       // DateTime instance\n```\n\n💡 Defined once, used anywhere — fully typed, cached, and clean.\n\n### It Gets Worse with Models\n\nStoring a `User` model in raw `SharedPreferences` requires:\n\n1. Manual `jsonEncode` / `jsonDecode`\n2. Validation on read\n3. String-based key tracking\n\n### SharedPreferences with Model:\n\n```dart\n// Get SharedPreferences\nfinal prefs = await SharedPreferences.getInstance();\n// Encode to JSON\nfinal json = jsonEncode(user.toJson());\n// Set value\nawait prefs.setString('user_data', json);\n\n// Read\nfinal raw = prefs.getString('user_data');\nUser? user;\nif (raw != null) {\n  try {\n    // Decode JSON\n    final decoded = jsonDecode(raw);\n    // Convert to User\n    user = User.fromJson(decoded);\n  } catch (_) {\n    // fallback or error\n  }\n}\n```\n\n### ✅ Same Logic with `prf`\n\n```dart\n// Define once\nfinal userData = Prf.json\u003cUser\u003e(\n  'user_data',\n  fromJson: User.fromJson,\n  toJson: (u) =\u003e u.toJson(),\n);\n\n// Save\nawait userData.set(user);\n\n// Read\nfinal savedUser = await userData.get(); // User?\n```\n\nFully typed. Automatically parsed. Fallback-safe. Reusable across your app.\n\n### Built for Real Apps\n\n`prf` was built to eliminate the day-to-day pain of using SharedPreferences in production codebases:\n\n- ✅ Define once — reuse anywhere\n- ✅ Clean API — `get()`, `set()`, `remove()`, `isNull()` for all types\n- ✅ Supports `20+ types`, `enum`, `JSON`\n- ✅ Automatic caching — fast access after first read\n- ✅ Test-friendly — easily reset, mock, or inspect values\n\n# How to Add Custom `prf` Types\n\n[⤴️ Back](#table-of-contents) -\u003e Table of Contents\n\nFor most use cases, you can use built-in types or factories like `Prf.enumerated\u003cT\u003e()`, `Prf.json\u003cT\u003e()`, and now `Prf.cast\u003cT, TCast\u003e()` to persist almost anything.\nThis section is for advanced users who want full control — but with **less boilerplate** thanks to the new `.cast()` API.\n\n#### 🧪 1. Define Your Custom Class\n\n```dart\nclass Color {\n  final int r, g, b;\n  const Color(this.r, this.g, this.b);\n\n  Map\u003cString, dynamic\u003e toJson() =\u003e {'r': r, 'g': g, 'b': b};\n  factory Color.fromJson(Map\u003cString, dynamic\u003e json) =\u003e\n      Color(json['r'] ?? 0, json['g'] ?? 0, json['b'] ?? 0);\n}\n```\n\n#### ⚡ 2. Use `.cast()` to Store It\n\nYou can store `Color` as a `String` by encoding it as JSON:\n\n```dart\nfinal favoriteColor = Prf.cast\u003cColor, String\u003e(\n  'favorite_color',\n  encode: (color) =\u003e jsonEncode(color.toJson()),\n  decode: (string) =\u003e string == null\n      ? null\n      : Color.fromJson(jsonDecode(string)),\n);\n```\n\n#### 🧩 Access and Use It\n\n```dart\nawait favoriteColor.set(Color(255, 0, 0));\nfinal color = await favoriteColor.get();\n\nprint(color?.r); // 255\n```\n\n#### 🚦 Want Isolate-Safe?\n\nJust add `.isolated`:\n\n```dart\nfinal safeColor = favoriteColor.isolated;\n```\n\n## Summary\n\n- Use `Prf.cast\u003cT, TCast\u003e()` to quickly persist custom objects.\n- No need to write full adapter classes.\n- Encode to any supported type (`String`, `int`, `List`, etc.).\n- Add `.isolated` for isolate-safe usage.\n\n[⤴️ Back](#table-of-contents) -\u003e Table of Contents\n\n---\n\n# 📦 More `jozz` Packages\n\n_[⤴️ Back](#table-of-contents) → Table of Contents_\n\nI’m Jozz — and my packages share a simple philosophy: **developer experience first**.\nI 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.\n\nThey’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.\nIf you find them useful and feel like supporting, you’re welcome to do so (:\n\n\u003cp\u003e\n  \u003ca href=\"https://buymeacoffee.com/yosefd99v\" target=\"https://buymeacoffee.com/yosefd99v\"\u003e\n    ☕ Buy me a coffee\n  \u003c/a\u003e\n\u003c/p\u003e\n\n- [shrink](#-shrink--compress-anything-in-one-line) – Compress Anything in One Line\n- [track](#-track--persistent-streaks-counters--records) – Persistent Streaks, Counters \u0026 Records\n- [prf](#-prf--sharedpreferences-without-the-pain) – SharedPreferences, Without the Pain\n- [time_plus](#-time_plus--smarter-datetime--duration-extensions) – Smarter DateTime \u0026 Duration Extensions\n- [exui](#-exui--supercharge-your-flutter-ui) – Supercharge Your Flutter UI\n- [limit](#-limit--cooldowns--rate-limits-simplified) – Cooldowns \u0026 Rate Limits, Simplified\n- [jozz_events](#-jozz_events--strongly-typed-events-for-clean-architecture) – Strongly-Typed Events for Clean Architecture\n\n### 🔽 [shrink](https://pub.dev/packages/shrink) – Compress Anything in One Line\n\nBecause 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.\n\n### 📊 [track](https://pub.dev/packages/track) – Persistent Streaks, Counters \u0026 Records\n\nDefine 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.\n\n### 🐝 [hivez](https://pub.dev/packages/hivez) – Hive, but Safer \u0026 Smarter\n\n`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.\n\n### ⏱ [time_plus](https://pub.dev/packages/time_plus) – Smarter DateTime \u0026 Duration Extensions\n\nStop 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**.\n\n### 🎨 [exui](https://pub.dev/packages/exui) – Supercharge Your Flutter UI\n\nEverything 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.\n\nNo 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.\n\n### ⏲ [limit](https://pub.dev/packages/limit) – Cooldowns \u0026 Rate Limits, Simplified\n\nOne line. No boilerplate. No setup. `limit` gives you **persistent cooldowns** and **token-bucket rate limiting** across sessions, isolates, and restarts. Perfect for **daily rewards**, **retry delays**, **API quotas**, or **chat limits**. Define once, automate forever — the system handles the timing, persistence, and safety behind the scenes. Clear docs and practical examples included.\n\n### 📢 [jozz_events](https://pub.dev/packages/jozz_events) – Strongly-Typed Events for Clean Architecture\n\nA **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.\n\n## 🔗 License MIT © Jozz\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://buymeacoffee.com/yosefd99v\" target=\"https://buymeacoffee.com/yosefd99v\"\u003e\n    ☕ Enjoying this package? You can support it here.\n  \u003c/a\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjozzdart%2Fprf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjozzdart%2Fprf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjozzdart%2Fprf/lists"}