From 3d1995f175087475e6c2d8f78fd59a0683e773f7 Mon Sep 17 00:00:00 2001
From: Jorge Sardina <jorge@skilldevs.com>
Date: Mon, 9 Dec 2024 13:01:41 +0100
Subject: [PATCH 1/2] Also delete the cache filesystem when emptying the store
 cache

---
 .../lib/src/cache_store.dart                  | 26 ++++++++++++++++---
 .../src/storage/file_system/file_system.dart  |  4 +++
 .../storage/file_system/file_system_io.dart   |  9 +++++++
 .../storage/file_system/file_system_web.dart  |  8 ++++++
 .../test/cache_store_test.dart                | 22 ++++++++++++++--
 .../test/helpers/test_configuration.dart      |  8 ++++++
 6 files changed, 71 insertions(+), 6 deletions(-)

diff --git a/flutter_cache_manager/lib/src/cache_store.dart b/flutter_cache_manager/lib/src/cache_store.dart
index ca6e9188..d1521ecc 100644
--- a/flutter_cache_manager/lib/src/cache_store.dart
+++ b/flutter_cache_manager/lib/src/cache_store.dart
@@ -151,13 +151,19 @@ class CacheStore {
 
   Future<void> emptyCache() async {
     final provider = await _cacheInfoRepository;
-    final toRemove = <int>[];
     final allObjects = await provider.getAllObjects();
-    var futures = <Future>[];
+
+    // Remove the cache files from the filesystem
+    await _removeCacheDir();
+
+    // Delete objects from store
+    final toRemove = <int>{};
     for (final cacheObject in allObjects) {
-      futures.add(_removeCachedFile(cacheObject, toRemove));
+      if (cacheObject.id != null) {
+        toRemove.add(cacheObject.id!);
+      }
     }
-    await Future.wait(futures);
+
     await provider.deleteAll(toRemove);
   }
 
@@ -195,6 +201,18 @@ class CacheStore {
     }
   }
 
+  Future<void> _removeCacheDir() async {
+    _memCache.clear();
+    _futureCache.clear();
+
+    try {
+      await fileSystem.deleteCacheDir();
+    } on FileSystemException {
+      // If the filesystem implementation doesn't check if the file
+      // exists before deleting it could fail
+    }
+  }
+
   bool memoryCacheContainsKey(String key) {
     return _memCache.containsKey(key);
   }
diff --git a/flutter_cache_manager/lib/src/storage/file_system/file_system.dart b/flutter_cache_manager/lib/src/storage/file_system/file_system.dart
index 2cd9764b..3f7a77a6 100644
--- a/flutter_cache_manager/lib/src/storage/file_system/file_system.dart
+++ b/flutter_cache_manager/lib/src/storage/file_system/file_system.dart
@@ -4,6 +4,10 @@ export 'file_system_web.dart';
 
 import 'package:file/file.dart';
 
+/// FileSystem works in the context of a directory where filenames are stored.
 abstract class FileSystem {
   Future<File> createFile(String name);
+
+  /// Deletes the directory that contains all the cached filed in the current context.
+  Future<void> deleteCacheDir();
 }
diff --git a/flutter_cache_manager/lib/src/storage/file_system/file_system_io.dart b/flutter_cache_manager/lib/src/storage/file_system/file_system_io.dart
index 15ab3901..861aeb60 100644
--- a/flutter_cache_manager/lib/src/storage/file_system/file_system_io.dart
+++ b/flutter_cache_manager/lib/src/storage/file_system/file_system_io.dart
@@ -28,4 +28,13 @@ class IOFileSystem implements FileSystem {
     }
     return directory.childFile(name);
   }
+
+  @override
+  Future<void> deleteCacheDir() async {
+    final directory = await _fileDir;
+
+    if (await directory.exists()) {
+      await directory.delete(recursive: true);
+    }
+  }
 }
diff --git a/flutter_cache_manager/lib/src/storage/file_system/file_system_web.dart b/flutter_cache_manager/lib/src/storage/file_system/file_system_web.dart
index ac080452..10400444 100644
--- a/flutter_cache_manager/lib/src/storage/file_system/file_system_web.dart
+++ b/flutter_cache_manager/lib/src/storage/file_system/file_system_web.dart
@@ -9,4 +9,12 @@ class MemoryCacheSystem implements FileSystem {
   Future<File> createFile(String name) async {
     return (await directory).childFile(name);
   }
+
+  @override
+  Future<void> deleteCacheDir() async {
+    final dir = await directory;
+    if (await dir.exists()) {
+      await dir.delete(recursive: true);
+    }
+  }
 }
diff --git a/flutter_cache_manager/test/cache_store_test.dart b/flutter_cache_manager/test/cache_store_test.dart
index ddc693a9..ac480975 100644
--- a/flutter_cache_manager/test/cache_store_test.dart
+++ b/flutter_cache_manager/test/cache_store_test.dart
@@ -373,7 +373,6 @@ void main() {
       var config = createTestConfig();
       var store = CacheStore(config);
       store.cleanupRunMinInterval = const Duration(milliseconds: 1);
-      await config.returnsFile('testimage.png');
 
       var co1 = CacheObject(
         'baseflow.com/test.png',
@@ -394,11 +393,30 @@ void main() {
         validTill: clock.now().add(const Duration(days: 7)),
       );
 
+      final cacheObjects = [co1, co2, co3];
+
+      // Write the cache files to the filesystem
+      final List<File> cacheFiles = [];
+      for (var cacheObject in cacheObjects) {
+        final f = await config.returnsFile(cacheObject.relativePath);
+        cacheFiles.add(f);
+      }
+
+      // Include an extra file
+      // This could be a desync between the store and the filesystem
+      cacheFiles
+          .add(await config.returnsFile("non-existent-file-in-store.png"));
+
       when(config.mockRepo.getAllObjects())
-          .thenAnswer((_) => Future.value([co1, co2, co3]));
+          .thenAnswer((_) => Future.value(cacheObjects));
 
       await store.emptyCache();
 
+      // make sure that all cached files in the filesystem are deleted
+      for (var cacheFile in cacheFiles) {
+        expect(await cacheFile.exists(), isFalse);
+      }
+
       verify(config.mockRepo
           .deleteAll(argThat(containsAll([co1.id, co2.id, co3.id])))).called(1);
     });
diff --git a/flutter_cache_manager/test/helpers/test_configuration.dart b/flutter_cache_manager/test/helpers/test_configuration.dart
index 151fbd2a..8da959b1 100644
--- a/flutter_cache_manager/test/helpers/test_configuration.dart
+++ b/flutter_cache_manager/test/helpers/test_configuration.dart
@@ -25,4 +25,12 @@ class TestFileSystem extends FileSystem {
     await dir.create(recursive: true);
     return dir.childFile(name);
   }
+
+  @override
+  Future<void> deleteCacheDir() async {
+    var dir = await directoryFuture;
+    if (await dir.exists()) {
+      await dir.delete(recursive: true);
+    }
+  }
 }

From 020c6ced4622b0c01d715ee05ba7744cc5c21eab Mon Sep 17 00:00:00 2001
From: Jorge Sardina <jorge@skilldevs.com>
Date: Mon, 9 Dec 2024 13:22:46 +0100
Subject: [PATCH 2/2] fix web implementation

---
 .../storage/file_system/file_system_web.dart  | 21 +++++++++++++++----
 1 file changed, 17 insertions(+), 4 deletions(-)

diff --git a/flutter_cache_manager/lib/src/storage/file_system/file_system_web.dart b/flutter_cache_manager/lib/src/storage/file_system/file_system_web.dart
index 10400444..b660f006 100644
--- a/flutter_cache_manager/lib/src/storage/file_system/file_system_web.dart
+++ b/flutter_cache_manager/lib/src/storage/file_system/file_system_web.dart
@@ -1,18 +1,31 @@
-import 'package:file/file.dart' show File;
+import 'package:file/file.dart' show File, Directory;
 import 'package:file/memory.dart';
 import 'package:flutter_cache_manager/src/storage/file_system/file_system.dart';
 
 class MemoryCacheSystem implements FileSystem {
-  final directory = MemoryFileSystem().systemTempDirectory.createTemp('cache');
+  Future<Directory> _fileDir;
+
+  MemoryCacheSystem() : _fileDir = _createTempDirectory();
 
   @override
   Future<File> createFile(String name) async {
-    return (await directory).childFile(name);
+    Directory directory = await _fileDir;
+    if (!(await directory.exists())) {
+      // Because _createTempDirectory assigns a new name we need to reassing the future
+      _fileDir = _createTempDirectory();
+      directory = await _fileDir;
+    }
+
+    return directory.childFile(name);
+  }
+
+  static Future<Directory> _createTempDirectory() async {
+    return MemoryFileSystem().systemTempDirectory.createTemp('cache');
   }
 
   @override
   Future<void> deleteCacheDir() async {
-    final dir = await directory;
+    final dir = await _fileDir;
     if (await dir.exists()) {
       await dir.delete(recursive: true);
     }