From 220fc7365e0240206d92252a93fa94f7010efac5 Mon Sep 17 00:00:00 2001 From: Abdullah <89297042+AzazelSensei@users.noreply.github.com> Date: Thu, 13 Aug 2026 12:46:16 +0300 Subject: [PATCH] Await store.putFile so cache info is actually persisted putFile, putFileStream, and downloads started persist without waiting. After those calls returned, CacheObject.id could still be null, so removeFile skipped the entry and a process exit could lose the info. Fixes #492 --- flutter_cache_manager/CHANGELOG.md | 1 + .../lib/src/cache_manager.dart | 4 +-- .../lib/src/web/web_helper.dart | 9 +++-- .../test/cache_manager_test.dart | 29 ++++++++++++++++ .../test/web_helper_test.dart | 34 +++++++++++++++++++ 5 files changed, 70 insertions(+), 7 deletions(-) diff --git a/flutter_cache_manager/CHANGELOG.md b/flutter_cache_manager/CHANGELOG.md index eb4d067e..b19c3654 100644 --- a/flutter_cache_manager/CHANGELOG.md +++ b/flutter_cache_manager/CHANGELOG.md @@ -1,5 +1,6 @@ ## [Unreleased] +* Awaits cache-info persist in `putFile`, `putFileStream`, and downloads so the stored object has an id before those calls return * Modernizes GitHub Actions CI (combined quality job, pinned Flutter 3.44.4, Dependabot for actions) * Updates example Android project to AGP 9.0.1 / Gradle 9.1 / Kotlin 2.3.20 * Migrates example Android app to built-in Kotlin diff --git a/flutter_cache_manager/lib/src/cache_manager.dart b/flutter_cache_manager/lib/src/cache_manager.dart index c04b9e7a..b700e1c7 100644 --- a/flutter_cache_manager/lib/src/cache_manager.dart +++ b/flutter_cache_manager/lib/src/cache_manager.dart @@ -242,7 +242,7 @@ class CacheManager implements BaseCacheManager { final file = await _config.fileSystem.createFile(cacheObject.relativePath); await file.writeAsBytes(fileBytes); - _store.putFile(cacheObject); + await _store.putFile(cacheObject); return file; } @@ -287,7 +287,7 @@ class CacheManager implements BaseCacheManager { .map((event) => event) .pipe(sink); - _store.putFile(cacheObject); + await _store.putFile(cacheObject); return file; } diff --git a/flutter_cache_manager/lib/src/web/web_helper.dart b/flutter_cache_manager/lib/src/web/web_helper.dart index 126a2c8f..f2d1d5cb 100644 --- a/flutter_cache_manager/lib/src/web/web_helper.dart +++ b/flutter_cache_manager/lib/src/web/web_helper.dart @@ -156,11 +156,10 @@ class WebHelper { newCacheObject = newCacheObject.copyWith(length: savedBytes); } - _store.putFile(newCacheObject).then((_) { - if (newCacheObject.relativePath != oldCacheObject.relativePath) { - _removeOldFile(oldCacheObject.relativePath); - } - }); + await _store.putFile(newCacheObject); + if (newCacheObject.relativePath != oldCacheObject.relativePath) { + await _removeOldFile(oldCacheObject.relativePath); + } final file = await _store.fileSystem.createFile( newCacheObject.relativePath, diff --git a/flutter_cache_manager/test/cache_manager_test.dart b/flutter_cache_manager/test/cache_manager_test.dart index 95242aa0..0de94599 100644 --- a/flutter_cache_manager/test/cache_manager_test.dart +++ b/flutter_cache_manager/test/cache_manager_test.dart @@ -424,6 +424,35 @@ void main() { expect(arg.key, fileKey); expect(arg.url, fileUrl); }); + + test('putFile waits for store persist before returning', () async { + var persistDone = false; + var store = MockCacheStore(); + when(store.putFile(any)).thenAnswer((_) async { + await Future.delayed(const Duration(milliseconds: 40)); + persistDone = true; + }); + + var cacheManager = TestCacheManager(createTestConfig(), store: store); + await cacheManager.putFile('baseflow.com/test', Uint8List(8)); + expect(persistDone, isTrue); + }); + + test('putFileStream waits for store persist before returning', () async { + var persistDone = false; + var store = MockCacheStore(); + when(store.putFile(any)).thenAnswer((_) async { + await Future.delayed(const Duration(milliseconds: 40)); + persistDone = true; + }); + + var cacheManager = TestCacheManager(createTestConfig(), store: store); + await cacheManager.putFileStream( + 'baseflow.com/test', + Stream>.value([1, 2, 3]), + ); + expect(persistDone, isTrue); + }); }); group('Testing remove files from cache', () { diff --git a/flutter_cache_manager/test/web_helper_test.dart b/flutter_cache_manager/test/web_helper_test.dart index 07075600..39a329b4 100644 --- a/flutter_cache_manager/test/web_helper_test.dart +++ b/flutter_cache_manager/test/web_helper_test.dart @@ -248,6 +248,40 @@ void main() { verify(store.putFile(any)).called(1); }); + test('downloadFile waits for persist before yielding FileInfo', () async { + const imageUrl = 'baseflow.com/testimage'; + + var persistDone = false; + var config = createTestConfig(); + var store = _createStore(config); + when(store.putFile(any)).thenAnswer((_) async { + await Future.delayed(const Duration(milliseconds: 40)); + persistDone = true; + }); + + final fileService = MockFileService(); + when(fileService.get(imageUrl, headers: anyNamed('headers'))).thenAnswer(( + _, + ) { + return Future.value( + MockFileFetcherResponse( + Stream.value([0, 1, 2, 3, 4, 5]), + 6, + 'testv1', + '.jpg', + 200, + DateTime.now(), + ), + ); + }); + + var webHelper = WebHelper(store, fileService); + await webHelper + .downloadFile(imageUrl) + .firstWhere((r) => r is FileInfo, orElse: null); + expect(persistDone, isTrue); + }); + test('File should be removed if extension changed', () async { const imageUrl = 'baseflow.com/testimage'; var imageName = 'image.png';