Kaeru Preferences

kaeru_preferences is a lightweight reactive key–value storage layer built for the Kaeru ecosystem.
It provides a unified JSON-backed store with automatic reactivity through Ref<T>, making it ideal for app settings, small configuration states, and persistent UI preferences.

The store loads once at startup and stays fully in memory, ensuring fast access and consistent behavior across the widget tree.

Features

• Fully Reactive

Every key is wrapped in a Ref<T>:

final darkMode = SharedStore.useBool("dark_mode", def: false);

When the value changes:

  • UI updates automatically
  • The in-memory cache updates
  • The value persists to storage
  • All other listeners with the same key sync instantly

• Single JSON Key

All data is stored under one JSON entry, reducing I/O:

  • Faster cold starts
  • Fewer platform calls
  • Cleaner storage design

• Multi-Tree Sync

If multiple widget trees access the same key, they stay synchronized through internal listeners.

• Built for Kaeru

Works seamlessly with:

  • ref<T>()
  • onBeforeUnmount()
  • useKaeruContext()
  • Kaeru's lifecycle system

Installation

dependencies:
  kaeru_preferences:
    git:
      url: https://github.com/your/repo/kaeru_preferences.git

Initialization

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await SharedStore.load(); // Load JSON storage into memory

  runApp(const App());
}

Usage

Boolean

final dark = SharedStore.useBool("dark_mode", def: false);

Watch(() {
  print("Dark mode: ${dark.value}");
});

dark.value = true;

Integer

final counter = SharedStore.useInt("counter", def: 0);
counter.value++;

String

final username = SharedStore.useString("username", def: "");
username.value = "Kaeru";

Double

final opacity = SharedStore.useDouble("opacity", def: 1.0);

List

final items = SharedStore.useList<String>("items", def: []);
items.value = [...items.value, "new item"];

Custom Types

final config = SharedStore.useValue<Map<String, dynamic>>(
  "config",
  def: {},
);

Why Kaeru Preferences?

Pain point Solution
SharedPreferences is not reactive Ref<T> auto-updates UI
Too many get/set calls All data in one JSON object
Slow startup due to multiple disk reads Single load on app init
Inconsistent values across widgets Cross-sync with listeners
Rebuild noise Built on Kaeru’s fine-grained reactivity

Internal Architecture

Three internal layers:

  1. In-memory data cache
  2. Reactive layer (Ref<T>)
  3. Persistent layer (SharedPreferencesAsync)

Data flow:

Ref<T> change
      ↓
Update in-memory `_data`
      ↓
Persist JSON
      ↓
Notify all listeners for the same key

Custom Storage Backend

SharedStore.setter = (data) async {
  await File("store.json").writeAsString(jsonEncode(data));
};

SharedStore.getter = () async {
  return jsonDecode(await File("store.json").readAsString());
};

Limitations

  • Not designed for large binary data
  • Not ideal for very high-frequency updates
  • Does not merge deeply nested JSON structures

License

MIT License.

Libraries

kaeru_preferences