cached_builder 1.0.1 copy "cached_builder: ^1.0.1" to clipboard
cached_builder: ^1.0.1 copied to clipboard

A smart FutureBuilder replacement with built-in caching, retry, and stale-while-revalidate strategies. Integrated with connectivity_plus_wrapper for smart network awareness.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:cached_builder/cached_builder.dart';
import 'package:connectivity_plus_wrapper/connectivity_plus_wrapper.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late ConnectivityWrapper connectivityWrapper;
  bool _isInitialized = false;
  int _testScenario = 0;

  @override
  void initState() {
    super.initState();
    _initialize();
  }

  Future<void> _initialize() async {
    connectivityWrapper = ConnectivityWrapper();
    setState(() => _isInitialized = true);
  }

  @override
  Widget build(BuildContext context) {
    if (!_isInitialized) {
      return const MaterialApp(
        home: Scaffold(
          body: Center(child: CircularProgressIndicator()),
        ),
      );
    }

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Cached Builder Test'),
          actions: [
            IconButton(
              icon: const Icon(Icons.refresh),
              onPressed: () => setState(() {}),
            ),
            PopupMenuButton<int>(
              onSelected: (value) => setState(() => _testScenario = value),
              itemBuilder: (context) => [
                const PopupMenuItem(value: 0, child: Text('Normal Data')),
                const PopupMenuItem(value: 1, child: Text('Slow Network (3s)')),
                const PopupMenuItem(value: 2, child: Text('Error Simulation')),
                const PopupMenuItem(value: 3, child: Text('Empty Data')),
              ],
            ),
          ],
        ),
        body: _buildTestBody(),
      ),
    );
  }

  Widget _buildTestBody() {
    return Column(
      children: [
        // Test Controls
        Card(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              children: [
                const Text('Test Scenarios:', style: TextStyle(fontWeight: FontWeight.bold)),
                const SizedBox(height: 8),
                Text('Current: ${_getScenarioName(_testScenario)}'),
                const SizedBox(height: 8),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    _buildTestButton(0, 'Normal'),
                    _buildTestButton(1, 'Slow'),
                    _buildTestButton(2, 'Error'),
                    _buildTestButton(3, 'Empty'),
                  ],
                ),
              ],
            ),
          ),
        ),
        
        // CachedBuilder Test
        Expanded(
          child: _buildCachedBuilderTest(),
        ),
      ],
    );
  }

  Widget _buildTestButton(int scenario, String label) {
    return ElevatedButton(
      onPressed: () => setState(() => _testScenario = scenario),
      style: ElevatedButton.styleFrom(
        backgroundColor: _testScenario == scenario ? Colors.blue : null,
      ),
      child: Text(label),
    );
  }

  Widget _buildCachedBuilderTest() {
    switch (_testScenario) {
      case 0:
        return _buildNormalTest();
      case 1:
        return _buildSlowTest();
      case 2:
        return _buildErrorTest();
      case 3:
        return _buildEmptyTest();
      default:
        return _buildNormalTest();
    }
  }

  Widget _buildNormalTest() {
    return CachedBuilder<List<Map<String, dynamic>>>(
      k: 'normal-test',
      future: _fetchNormalData,
      connectivityWrapper: connectivityWrapper,
      builder: (posts, isFromCache) {
        return Column(
          children: [
            if (isFromCache)
              Container(
                padding: const EdgeInsets.all(8),
                color: Colors.green[100],
                child: Row(
                  children: [
                    const Icon(Icons.cached, size: 16, color: Colors.green),
                    const SizedBox(width: 8),
                    const Text('✅ Showing CACHED Data'),
                    const Spacer(),
                    Text('Items: ${posts.length}'),
                  ],
                ),
              ),
            Expanded(
              child: ListView.builder(
                itemCount: posts.length,
                itemBuilder: (context, index) {
                  final post = posts[index];
                  return ListTile(
                    title: Text('${post['title']} ${isFromCache ? '(Cached)' : ''}'),
                    subtitle: Text(post['body']),
                    leading: Icon(isFromCache ? Icons.cached : Icons.new_releases),
                  );
                },
              ),
            ),
          ],
        );
      },
      loading: const Center(child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          CircularProgressIndicator(),
          SizedBox(height: 16),
          Text('Loading normal data...'),
        ],
      )),
    );
  }

  Widget _buildSlowTest() {
    return CachedBuilder<List<Map<String, dynamic>>>(
      k: 'slow-test',
      future: _fetchSlowData,
      connectivityWrapper: connectivityWrapper,
      builder: (posts, isFromCache) {
        return Column(
          children: [
            if (isFromCache)
              Container(
                padding: const EdgeInsets.all(8),
                color: Colors.orange[100],
                child: const Row(
                  children: [
                    Icon(Icons.timer, size: 16, color: Colors.orange),
                    SizedBox(width: 8),
                    Text('⏳ Showing cached slow data'),
                  ],
                ),
              ),
            Expanded(
              child: ListView.builder(
                itemCount: posts.length,
                itemBuilder: (context, index) {
                  final post = posts[index];
                  return ListTile(
                    title: Text('${post['title']} (Slow)'),
                    subtitle: Text(post['body']),
                    leading: const Icon(Icons.timer),
                  );
                },
              ),
            ),
          ],
        );
      },
      loading: const Center(child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          CircularProgressIndicator(),
          SizedBox(height: 16),
          Text('Loading slow data (3 seconds)...'),
        ],
      )),
    );
  }

  Widget _buildErrorTest() {
    return CachedBuilder<List<Map<String, dynamic>>>(
      k: 'error-test',
      future: _fetchErrorData,
      connectivityWrapper: connectivityWrapper,
      builder: (posts, isFromCache) {
        return ListView.builder(
          itemCount: posts.length,
          itemBuilder: (context, index) {
            final post = posts[index];
            return ListTile(
              title: Text(post['title']),
              subtitle: Text(post['body']),
            );
          },
        );
      },
      loading: const Center(child: CircularProgressIndicator()),
      error: (error, retry, status) => Center(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              const Icon(Icons.error_outline, size: 64, color: Colors.red),
              const SizedBox(height: 16),
              Text(
                'Error: ${error.toString()}',
                textAlign: TextAlign.center,
                style: const TextStyle(fontSize: 16),
              ),
              const SizedBox(height: 16),
              if (status != null)
                Text(
                  'Connection: ${status.description}',
                  style: const TextStyle(color: Colors.grey),
                ),
              const SizedBox(height: 24),
              ElevatedButton(
                onPressed: retry,
                child: const Text('Try Again'),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildEmptyTest() {
    return CachedBuilder<List<Map<String, dynamic>>>(
      k: 'empty-test',
      future: _fetchEmptyData,
      connectivityWrapper: connectivityWrapper,
      builder: (posts, isFromCache) {
        return Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              const Icon(Icons.inbox_outlined, size: 64, color: Colors.grey),
              const SizedBox(height: 16),
              Text(
                isFromCache ? 'No cached data available' : 'No data found',
                style: const TextStyle(fontSize: 18),
              ),
              const SizedBox(height: 8),
              const Text('Try refreshing or check your connection'),
            ],
          ),
        );
      },
      loading: const Center(child: CircularProgressIndicator()),
    );
  }

  // Data fetching methods
  Future<List<Map<String, dynamic>>> _fetchNormalData() async {
    await Future.delayed(const Duration(milliseconds: 500)); // Simulate network
    return [
      {'id': 1, 'title': 'Normal Post 1', 'body': 'This is normal data'},
      {'id': 2, 'title': 'Normal Post 2', 'body': 'This is also normal data'},
      {'id': 3, 'title': 'Normal Post 3', 'body': 'More normal content here'},
      {'id': 4, 'title': 'Normal Post 4', 'body': 'More normal content here'},
      {'id': 5, 'title': 'Normal Post 5', 'body': 'More normal content here'},
      {'id': 6, 'title': 'Normal Post 6', 'body': 'More normal content here'},
      {'id': 7, 'title': 'Normal Post 7', 'body': 'More normal content here'},
      {'id': 8, 'title': 'Normal Post 8', 'body': 'More normal content here'},
      {'id': 9, 'title': 'Normal Post 9', 'body': 'More normal content here'},
      {'id': 10, 'title': 'Normal Post 10', 'body': 'More normal content here'},
    ];
  }

  Future<List<Map<String, dynamic>>> _fetchSlowData() async {
    await Future.delayed(const Duration(seconds: 3)); // Simulate slow network
    return [
      {'id': 1, 'title': 'Slow Post 1', 'body': 'This took 3 seconds to load'},
      {'id': 2, 'title': 'Slow Post 2', 'body': 'Very slow connection simulation'},
    ];
  }

  Future<List<Map<String, dynamic>>> _fetchErrorData() async {
    await Future.delayed(const Duration(seconds: 1));
    throw Exception('Failed to load data: Server returned 500 error');
  }

  Future<List<Map<String, dynamic>>> _fetchEmptyData() async {
    await Future.delayed(const Duration(seconds: 1));
    return []; // Empty list
  }

  String _getScenarioName(int scenario) {
    switch (scenario) {
      case 0: return 'Normal Data';
      case 1: return 'Slow Network';
      case 2: return 'Error Simulation';
      case 3: return 'Empty Data';
      default: return 'Unknown';
    }
  }
}
0
likes
140
points
130
downloads

Publisher

unverified uploader

Weekly Downloads

A smart FutureBuilder replacement with built-in caching, retry, and stale-while-revalidate strategies. Integrated with connectivity_plus_wrapper for smart network awareness.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

connectivity_plus_wrapper, flutter, hive

More

Packages that depend on cached_builder