How to implement Caching in Flutter

How to implement Caching in Flutter

RxCacheManager is a caching library for Flutter that provides a simple and flexible way to cache data and retrieve it efficiently. It uses a strategy-based approach, allowing you to define different caching strategies and apply them to your data. This article will guide you through the basic usage of RxCacheManager and how to integrate it into your Flutter project.

Getting Started

To use RxCacheManager in your Flutter project, follow these steps:

GitHub Repository: FlutterNReact/rx_cache_manager

1. Add the rx_cache_manager dependency to your pubspec.yaml file:

dependencies:

  flutter:
    sdk: flutter
  rx_cache_manager: ^x.x.x # Replace with the latest version available

# Run flutter pub get to fetch the new dependency.

Import the library in your Dart files:

import 'package:rx_cache_manager/rx_cache_manager.dart';

Usage

RxCacheManager provides a caching mechanism for any type of data you want to cache. To use it, you need to define the data type you want to cache and its corresponding serializer and deserializer functions.

Initializing the RxCacheManager

To start using RxCacheManager, you need to initialize it with an instance of CacheStorage. The CacheStorage is responsible for handling the actual storage of cached data. Here's an example of how to initialize RxCacheManager:

void main() async {
  final cache = RxCacheManager(CacheStorage("default"));
  // Other code...
}

PayoutSettings

In this example, we will demonstrate how to cache and retrieve PayoutSettings data using RxCacheManager. First, let's define the PayoutSettings class and its serializer and deserializer functions:

class PayoutSettings {
  String? id;
  String? interval;
 PayoutSettings({
    this.id,
    this.interval
 });
  // Class implementation...
  factory PayoutSettings.fromMap(Map<String, dynamic> json) {
    // Deserialization logic...
    return PayoutSettings()
      ..id = json['id']
      ..interval = json['interval'];
  }

  Map<String, dynamic> toMap() {
    // Serialization logic...
   return {
     "id": id,
     "interval": interval
   };
  }
}

Caching Data

To cache data, you can use the write method of the RxCacheManager instance. Here's an example of caching PayoutSettings data:

void main() async {
  final cache = RxCacheManager(CacheStorage("default"));
  // Caching PayoutSettings data
  final payoutSettings = PayoutSettings(
    id: "1234",
    interval: "monthly",
    // Other properties...
  );
  await cache.write("payout-settings", payoutSettings.toMap());
  // Other code...
}

Retrieving Data

To retrieve data from the cache, you'll use the from method of the RxCacheManager instance along with a caching strategy. Here's an example of retrieving PayoutSettings data:

In this example, we used the CacheOrAsyncStrategy strategy, which first tries to fetch the data from the cache and, if not available or expired, falls back to an asynchronous data source.

void main() async {
  final cache = RxCacheManager(CacheStorage("default"));
  // Retrieving PayoutSettings data
  final request = await cache
      .from<PayoutSettings, Map>("payout-settings")
      .withSerializer((json) => PayoutSettings.fromMap(json))
      .withStrategy(JustCacheStrategy())
      .withTtl(30000000)
      .execute();
  print(request?.toMap());
  // Other code...
}

Using the RxCacheManager With Flutter

To start using RxCacheManager with Flutter, you need to initialize it with an instance of CacheStorage. The CacheStorage is responsible for handling the actual storage of cached data. Here's an example of how to initialize RxCacheManager with FutureBuilder:

class MyWidget extends StatelessWidget {
  // Initializing RxCacheManager with a default cache storage
  final cache = RxCacheManager(CacheStorage("default"));

  // Retrieving PayoutSettings data
  Future<PayoutSettings?> getPayoutSettings() async {
    return await cache
        .from<PayoutSettings, Map>("payout-settings")
        .withAsync(() => Future.delayed(
              const Duration(seconds: 2),
              () => {
                    "id": "1234",
                    "interval": "monthly",
                    // Other properties...
                  },
            ))
        .withSerializer((json) => PayoutSettings.fromMap(json))
        .withStrategy(CacheOrAsyncStrategy())
        .withTtl(30000000)
        .execute();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<PayoutSettings>(
      future: getPayoutSettings(),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          PayoutSettings data = snapshot.data!;
          return ListTile(
            title: Text(data.id ?? 'N/A'),
            subtitle: Text(data.interval ?? 'N/A'),
          );
        } else if (snapshot.hasError) {
          return Text('Error: ${snapshot.error}');
        } else {
          return CircularProgressIndicator();
        }
      },
    );
  }
}

Breaking Down the Code

Initializing RxCacheManager

In this code snippet, we start by initializing RxCacheManager with a default cache storage using the CacheStorage("default") constructor. This sets up the caching environment for our application.

Retrieving PayoutSettings Data

We then proceed to retrieve PayoutSettings data. This is done through a series of chained method calls:

  1. cache.from<PayoutSettings, Map>("payout-settings") specifies that we want to work with a cache entry labeled as "payout-settings" and expect it to be of type PayoutSettings.

  2. .withAsync(...) defines an asynchronous data source. In this example, we simulate fetching data from an asynchronous source using Future.delayed.

  3. .withSerializer(...) specifies how to deserialize the data retrieved from the cache.

  4. .withStrategy(...) sets the caching strategy, in this case, CacheOrAsyncStrategy() tries to fetch data from the cache first and falls back to an asynchronous source if needed.

  5. .withTtl(...) sets the time-to-live (TTL) for the cached data. In this example, it's set to 30000000 milliseconds (approximately 8 hours).

Next Steps

Finally, this section will include any concluding remarks and suggest potential next steps or actions to take in your Flutter application.

This detailed breakdown provides a comprehensive understanding of the code's functionality and how it can be extended or customized to suit your specific needs.

JustCacheStrategy

The JustCacheStrategy fetches data from the cache and doesn't attempt to update it even if it's expired.

final request = await cache
    .from<PayoutSettings, Map>("payout-settings")
    .withSerializer((json) => PayoutSettings.fromMap(json))
    .withStrategy(JustCacheStrategy())
    .execute();

JustAsyncStrategy

The JustAsyncStrategy fetches data from the asynchronous source and doesn't attempt to cache it.

final request = await cache
    .from<PayoutSettings, Map>("payout-settings")
    .withAsync(() => fetchPayoutSettingsFromServer()) // Replace with your async function
    .withStrategy(JustAsyncStrategy())
    .execute();

CacheOrAsyncStrategy

The CacheOrAsyncStrategy first tries to fetch the data from the cache and, if not available or expired, falls back to an asynchronous data source.

final request = await cache
    .from<PayoutSettings, Map>("payout-settings")
    .withAsync(() => fetchPayoutSettingsFromServer()) // Replace with your async function
    .withSerializer((json) => PayoutSettings.fromMap(json))
    .withStrategy(CacheOrAsyncStrategy())
    .execute();

CacheAndAsyncStrategy

The CacheAndAsyncStrategy always returns the cached content and tries to update it if the cache expires.

final request = await cache
    .from<PayoutSettings, Map>("payout-settings")
    .withAsync(() => fetchPayoutSettingsFromServer()) // Replace with your async function
    .withSerializer((json) => PayoutSettings.fromMap(json))
    .withStrategy(CacheAndAsyncStrategy())
    .execute();

AsyncOrCacheStrategy

The AsyncOrCacheStrategy first tries to fetch the data from the asynchronous source and, if unsuccessful, falls back to the cache.

final request = await cache
    .from<PayoutSettings, Map>("payout-settings")
    .withAsync(() => fetchPayoutSettingsFromServer()) // Replace with your async function
    .withSerializer((json) => PayoutSettings.fromMap(json))
    .withStrategy(AsyncOrCacheStrategy())
    .execute();

Clearing the Cache

To clear the cache, you can use the clear method of the RxCacheManager instance. You can optionally specify a prefix to clear only specific cached items. Here's how you can clear the cache and retrieve the cache size:

void main() async {

  final cache = RxCacheManager(CacheStorage("default"));
  // Clear the entire cache
  await cache.clear();
  // Get the entire cache size
  int size = await cache.size();
  // Clear cache with a specific prefix
  await cache.clear(prefix: "payout-settings");
  // Other code...
}

Contributing to RxCacheManager

If you find issues, have suggestions for improvements, or would like to contribute to the RxCacheManager project, you can do so by opening an issue or submitting a pull request on the GitHub repository. FlutterNReact/rx_cache_manager/issues

Conclusion

RxCacheManager provides a powerful caching solution for your Flutter applications. By defining caching strategies and using serializers, you can efficiently cache and retrieve data, improving the performance and responsiveness of your app. For more information and advanced usage, check the full API documentation and examples.

Happy coding!

Did you find this article valuable?

Support Michael Piper by becoming a sponsor. Any amount is appreciated!