Skip to content

Repository files navigation

Riverpod Cache Engine

A powerful, zero-boilerplate caching library for Flutter Riverpod. Riverpod Cache Engine extends Riverpod's Notifier with an automatic, smart caching system that handles offline persistence, in-memory caching, background garbage collection, and seamless data refreshing.

Features

  • Zero-Boilerplate Extension: Access caching directly via caches.write() and caches.read() inside any Notifier.
  • Background Garbage Collector: Automatically cleans up expired cache keys without blocking the UI.
  • Smart Auto-Refresh: Supports onExpired and onNull callbacks to fetch fresh data automatically when the cache is missing or expired.
  • In-Memory Cache (Default): Lightning fast RAM caching out of the box if no storage engine is provided.
  • Persistent Storage Ready: Easily plug in Hive, SharedPreferences, or any other storage engine.
  • Production Ready Logging: Clean, formatted console logs that automatically disable themselves in Release mode.

Installation

Add riverpod_cache_engine to your pubspec.yaml:

dependencies:
  riverpod_cache_engine: ^1.0.0

Getting Started

1. Initialization

Call RiverpodCache.initialize() in your main.dart before runApp.

Option A: Zero Config (In-Memory Cache) If you don't provide a storage engine, the library defaults to lightning-fast RAM caching.

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  await RiverpodCache.initialize(
    cleanupInterval: const Duration(hours: 24), // Background GC interval
  );

  runApp(const ProviderScope(child: MyApp()));
}

Option B: Persistent Storage (e.g., Hive) If you want data to persist after the app closes, pass a CacheStore implementation.

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Hive.initFlutter();
  final box = await Hive.openBox('cache_box');
  
  await RiverpodCache.initialize(
    store: HiveCacheEngine(box), 
  );

  runApp(const ProviderScope(child: MyApp()));
}

Implementing a Custom Storage Engine

To use any database (Hive, SharedPreferences, Isar, SQLite), simply implement the CacheStore interface. It only requires 3 methods:

import 'package:riverpod_cache/riverpod_cache.dart';

class MyCustomStore implements CacheStore {
  final MyDatabase db;
  MyCustomStore(this.db);

  @override
  Future<Map<String, dynamic>?> read(String key) async {
    // Read and return the JSON map from your database
    return db.get(key);
  }

  @override
  Future<void> write(String key, Map<String, dynamic> data) async {
    // Save the JSON map to your database
    await db.put(key, data);
  }

  @override
  Future<void> delete(String key) async {
    // Delete the key from your database
    await db.delete(key);
  }
}

2. Usage (The Magic caches Extension)

Any Notifier or AsyncNotifier instantly gains access to the caches controller!

class UserNotifier extends Notifier<UserProfile?> {
  @override
  UserProfile? build() => null;

  Future<void> loadUser() async {
    // Read from cache. If not found (onNull) or expired (onExpired), 
    // the library will automatically call your API, save it, and return the fresh data!
    final cachedMap = await caches.read(
      'user_profile',
      options: CacheOptions(
        ttl: const Duration(hours: 1), // Time to live
        onNull: () => api.fetchUser(),
        onExpired: () => api.fetchUser(),
      ),
    );

    if (cachedMap != null) {
      state = UserProfile.fromJson(cachedMap);
    }
  }

  Future<void> updateUser(UserProfile newUser) async {
    state = newUser;
    // Write to cache manually
    await caches.write('user_profile', newUser.toJson());
  }
}

Advanced Background Auto-Refresh

The built-in Garbage Collector doesn't just delete old data; it refreshes it. If a user leaves the app open and the GC Timer finds an expired key, it will check if you registered an onExpired callback during read() or write(). If found, the GC will call it, fetch fresh data, and update the database silently in the background!

Logs & Debugging

The library prints beautiful, emoji-prefixed logs in Debug Mode:

[RiverpodCache] 🚀 INIT | Starting initialization...
[RiverpodCache] 📖 READ (Miss) | Key: [user_profile] not found. Calling onNull...
[RiverpodCache] ✅ FETCH (onNull) | Successfully fetched and saved key [user_profile]...
[RiverpodCache] 🔄 GC (Timer) | Key: [user_profile] is expired. Calling onExpired...

(Logging disables automatically in Release Mode to protect data and save resources).

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages