cache_guard 0.0.1
cache_guard: ^0.0.1 copied to clipboard
Simple async caching for Flutter. Run once, reuse forever. Prevent duplicate calls, support TTL, force refresh — with zero configuration.
import 'package:flutter/material.dart';
import 'package:cache_guard/cache_guard.dart';
void main() => runApp(const CacheGuardExample());
class CacheGuardExample extends StatelessWidget {
const CacheGuardExample({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'cache_guard Example',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorSchemeSeed: Colors.teal,
useMaterial3: true,
brightness: Brightness.light,
),
home: const DemoPage(),
);
}
}
// ─── Simulated API ──────────────────────────────────────────────────────────
class FakeApi {
/// Simulates a user fetch with a 2-second delay.
static Future<Map<String, String>> fetchUser() async {
await Future.delayed(const Duration(seconds: 2));
return {
'name': 'John Doe',
'email': 'john@example.com',
'fetchedAt': DateTime.now().toIso8601String(),
};
}
}
// ─── Demo Page ──────────────────────────────────────────────────────────────
class DemoPage extends StatefulWidget {
const DemoPage({super.key});
@override
State<DemoPage> createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
bool _isLoading = false;
String? _result;
int _fetchCount = 0;
// ── Example 1: Basic cacheGuard() ──────────────────────────────────────
Future<void> _fetchWithCache() async {
setState(() {
_isLoading = true;
_result = null;
});
final user = await cacheGuard<Map<String, String>>(
'user',
() async {
_fetchCount++;
return FakeApi.fetchUser();
},
);
if (mounted) {
setState(() {
_isLoading = false;
_result = 'Name: ${user['name']}\n'
'Email: ${user['email']}\n'
'Fetched at: ${user['fetchedAt']}\n'
'API calls made: $_fetchCount';
});
}
}
// ── Example 2: With TTL ────────────────────────────────────────────────
Future<void> _fetchWithTtl() async {
setState(() {
_isLoading = true;
_result = null;
});
final user = await cacheGuard<Map<String, String>>(
'user_ttl',
() async {
_fetchCount++;
return FakeApi.fetchUser();
},
ttl: const Duration(seconds: 10),
);
if (mounted) {
setState(() {
_isLoading = false;
_result = 'TTL: 10 seconds\n'
'Name: ${user['name']}\n'
'Fetched at: ${user['fetchedAt']}\n'
'API calls made: $_fetchCount';
});
}
}
// ── Example 3: Force refresh ───────────────────────────────────────────
Future<void> _fetchForceRefresh() async {
setState(() {
_isLoading = true;
_result = null;
});
final user = await cacheGuard<Map<String, String>>(
'user',
() async {
_fetchCount++;
return FakeApi.fetchUser();
},
forceRefresh: true,
);
if (mounted) {
setState(() {
_isLoading = false;
_result = 'Force refreshed!\n'
'Name: ${user['name']}\n'
'Fetched at: ${user['fetchedAt']}\n'
'API calls made: $_fetchCount';
});
}
}
// ── Example 4: Extension syntax ────────────────────────────────────────
Future<void> _fetchWithExtension() async {
setState(() {
_isLoading = true;
_result = null;
});
_fetchCount++;
final user = await FakeApi.fetchUser().cache('user_ext');
if (mounted) {
setState(() {
_isLoading = false;
_result = 'Extension .cache()\n'
'Name: ${user['name']}\n'
'Fetched at: ${user['fetchedAt']}\n'
'API calls made: $_fetchCount';
});
}
}
// ── Clear all ──────────────────────────────────────────────────────────
void _clearAll() {
clearCache();
_fetchCount = 0;
setState(() => _result = 'All cache cleared!');
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(title: const Text('cache_guard Example')),
body: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// ── Cache status ─────────────────────────────────────────
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Cache Status', style: theme.textTheme.titleMedium),
const SizedBox(height: 8),
Text('hasCache("user"): ${hasCache('user')}'),
Text('hasCache("user_ttl"): ${hasCache('user_ttl')}'),
Text('hasCache("user_ext"): ${hasCache('user_ext')}'),
],
),
),
),
const SizedBox(height: 16),
// ── Basic cacheGuard ─────────────────────────────────────
ElevatedButton(
onPressed: _isLoading ? null : _fetchWithCache,
child: _isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Fetch User (cached)'),
),
const SizedBox(height: 12),
// ── With TTL ─────────────────────────────────────────────
ElevatedButton.icon(
onPressed: _isLoading ? null : _fetchWithTtl,
icon: const Icon(Icons.timer),
label: const Text('Fetch with TTL (10s)'),
),
const SizedBox(height: 12),
// ── Force refresh ────────────────────────────────────────
ElevatedButton.icon(
onPressed: _isLoading ? null : _fetchForceRefresh,
icon: const Icon(Icons.refresh),
label: const Text('Force Refresh'),
),
const SizedBox(height: 12),
// ── Extension syntax ─────────────────────────────────────
ElevatedButton.icon(
onPressed: _isLoading ? null : _fetchWithExtension,
icon: const Icon(Icons.extension),
label: const Text('Fetch with .cache() extension'),
),
const SizedBox(height: 12),
// ── Clear ────────────────────────────────────────────────
const Divider(),
OutlinedButton.icon(
onPressed: _clearAll,
icon: const Icon(Icons.delete_outline),
label: const Text('Clear All Cache'),
),
const SizedBox(height: 24),
// ── Result display ───────────────────────────────────────
if (_result != null)
Card(
color: _result!.contains('cleared')
? Colors.orange.shade50
: Colors.green.shade50,
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(
_result!,
style: TextStyle(
color: _result!.contains('cleared')
? Colors.orange.shade800
: Colors.green.shade800,
),
),
),
),
],
),
),
);
}
}