hope_cache 0.1.4 copy "hope_cache: ^0.1.4" to clipboard
hope_cache: ^0.1.4 copied to clipboard

Simple and fast caching for Dart/Flutter with TTL support, multiple eviction strategies, and pluggable storage.

Hope Cache #

Caching and data-fetching for Flutter — TTL, eviction, and a reactive widget layer.

pub package License

Features #

  • Zero dependencies — pure Dart + Flutter, nothing else
  • No wrapper widget — just HopeClient.init() in main, no QueryClientProvider needed
  • Own your cacheCacheManager works standalone without the query layer
  • Pluggable storage — bring your own storage backend
  • Eager cleanup — controllers disposed immediately when no widgets are listening
  • LRU, LFU, FIFO eviction — with TTL, global default and per-key overrides
  • Map keys, batch operations, pattern invalidation
  • HopeBuilder — reactive data fetching with loading, error, refetch
  • HopeMutationBuilder — write operations with callbacks
  • Infinite scroll — just add getNextPageParam, no extra setup
  • invalidatePrefix — invalidate a group of related queries in one call
  • Stale-while-revalidate, retry, polling, dependent queries
  • Optimistic updates, prefetch, invalidation

Installation #

dart pub add hope_cache

Setup #

Cache only

final cache = await CacheManager.create(
maxSize: 1024 * 1024,
defaultTTL: Duration(minutes: 5),
evictionPolicy: EvictionPolicy.lru,
);

Cache + Query

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

  await HopeClient.init(
    maxSize: 1024 * 1024,
    defaultTTL: Duration(minutes: 5),
    evictionPolicy: EvictionPolicy.lru,
  );

  runApp(MyApp());
}

HopeClient.init() sets up the cache internally — no need to create CacheManager separately.

Cache #

await cache.set('user_123', {'name': 'Alice'});
final user = await cache.getIfPresent('user_123');

Map Keys

await cache.set(
  {'resource': 'product', 'id': '123'},
  {'name': 'Laptop'},
);

// different order = same key
final product = await cache.getIfPresent(
  {'id': '123', 'resource': 'product'},
);

Query #

HopeBuilder<List<User>>(
  queryKey: ['users'],
  fetcher: (_) => api.getUsers(),
  builder: (context, state) {
    if (state.isLoading) return CircularProgressIndicator();
    if (state.isError) return Text(state.error.toString());
    return UserList(state.data!);
  },
)

Dependent Query

HopeBuilder<List<Post>>(
  queryKey: ['posts', userId],
  fetcher: (_) => api.getPosts(userId),
  options: HopeOptions(enabled: userId != null),
  builder: (context, state) {
    if (state.isIdle) return Text('No user selected');
    if (state.isLoading) return CircularProgressIndicator();
    if (state.isError) return Text(state.error.toString());
    return PostList(state.data!);
  },
)

Stale Time

HopeBuilder<List<User>>(
  queryKey: ['users'],
  fetcher: (_) => api.getUsers(),
  options: HopeOptions(staleTime: Duration(minutes: 2)),
  builder: (context, state) => UserList(state.data ?? []),
)

Polling

HopeBuilder<Stats>(
  queryKey: ['stats'],
  fetcher: (_) => api.getStats(),
  options: HopeOptions(refetchInterval: Duration(seconds: 30)),
  builder: (context, state) => StatsWidget(state.data),
)

Refetch on Resume

HopeBuilder<List<User>>(
  queryKey: ['users'],
  fetcher: (_) => api.getUsers(),
  options: HopeOptions(
    refetchOnResume: true,
    staleTime: Duration(minutes: 5),
  ),
  builder: (context, state) => UserList(state.data ?? []),
)

Manual Refetch

HopeBuilder<List<User>>(
  queryKey: ['users'],
  fetcher: (_) => api.getUsers(),
  builder: (context, state) {
    return Column(
      children: [
        UserList(state.data ?? []),
        ElevatedButton(
          onPressed: state.refetch,
          child: Text('Refresh'),
        ),
      ],
    );
  },
)

Infinite Scroll #

HopeBuilder<UserPage>(
  queryKey: ['users'],
  fetcher: (cursor) => api.getUsers(cursor: cursor),
  getNextPageParam: (lastPage) => lastPage.nextCursor,
  builder: (context, state) {
    final items = state.pages!.expand((p) => p.items).toList();
    return ListView.builder(
      itemCount: items.length,
      itemBuilder: (context, index) {
        if (index == items.length - 1 && state.hasMore == true) {
          state.fetchMore();
        }
        return UserCard(items[index]);
      },
    );
  },
)

Mutation #

HopeMutationBuilder<User, Map<String, dynamic>>(
  mutation: (data) => api.updateUser(data),
  onSuccess: (user) => HopeClient.instance.invalidate(['users']),
  builder: (context, state, mutate) {
    return ElevatedButton(
      onPressed: state.isMutating ? null : () => mutate({'name': 'Hope'}),
      child: state.isMutating ? CircularProgressIndicator() : Text('Save'),
    );
  },
)

Reset Mutation

HopeMutationBuilder<User, Map<String, dynamic>>(
  mutation: (data) => api.updateUser(data),
  builder: (context, state, mutate) {
    if (state.isError) return ElevatedButton(
      onPressed: state.reset,
      child: Text('Try Again'),
    );
    return ElevatedButton(
      onPressed: state.isMutating ? null : () => mutate({'name': 'Hope'}),
      child: state.isMutating ? CircularProgressIndicator() : Text('Save'),
    );
  },
)

Invalidation #

// single query
HopeClient.instance.invalidate(['users']);

// all queries
HopeClient.instance.invalidateAll();

// by prefix
HopeClient.instance.invalidatePrefix(['user']);

Prefetch #

// warm cache before user navigates
HopeClient.instance.prefetch(
  queryKey: ['user', userId],
  fetcher: () => api.getUser(userId),
);

Optimistic Updates #

// update UI instantly before mutation completes
await HopeClient.instance.cache
    .set('users', updatedUsers);

await mutation.mutate(input);

// get real server data
HopeClient.instance.invalidate(['users']);

License #

MIT - see LICENSE file.

Author #

Hope Richard
📧 hoperichardmaleko@gmail.com
🔗 GitHub

1
likes
0
points
67
downloads

Publisher

verified publishersupgrow.com

Weekly Downloads

Simple and fast caching for Dart/Flutter with TTL support, multiple eviction strategies, and pluggable storage.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter

More

Packages that depend on hope_cache