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:
- In-memory data cache
- Reactive layer (
Ref<T>) - 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.