From f72614afa79ededfd7b351f9e65f63ec0604da57 Mon Sep 17 00:00:00 2001 From: Artiu <71273256+Artiu@users.noreply.github.com> Date: Mon, 7 Apr 2025 04:58:37 +0200 Subject: [PATCH] Fix large cache size (#437) * Add custom cache manager * Fix * Add cleanup * Remove prints for debugging * Remove unnecessary code * Change nr of max cache objects --- lib/cache_manager.dart | 44 +++ lib/main.dart | 3 + lib/models/irc.dart | 2 + lib/screens/settings/other_settings.dart | 5 +- lib/widgets/cached_image.dart | 2 + lib/widgets/photo_view.dart | 6 +- pubspec.lock | 2 +- pubspec.yaml | 367 ++++++++++++----------- 8 files changed, 244 insertions(+), 187 deletions(-) create mode 100644 lib/cache_manager.dart diff --git a/lib/cache_manager.dart b/lib/cache_manager.dart new file mode 100644 index 00000000..b3d61f48 --- /dev/null +++ b/lib/cache_manager.dart @@ -0,0 +1,44 @@ +import 'dart:io'; + +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:path_provider/path_provider.dart'; + +class CustomCacheManager { + static const key = 'libCachedImageData'; + static final _repo = CacheObjectProvider(databaseName: key); + static final instance = CacheManager( + Config( + key, + stalePeriod: const Duration(days: 30), + maxNrOfCacheObjects: 10000, + repo: _repo, + ), + ); + + static Future removeOrphanedCacheFiles() async { + final tempDir = await getTemporaryDirectory(); + final cacheDir = Directory('${tempDir.path}/$key'); + + if (!await cacheDir.exists()) { + return; + } + + final allFiles = cacheDir.listSync(recursive: true).toList(); + + await _repo.open(); + final cachedObjects = await _repo.getAllObjects(); + final dbFiles = cachedObjects.map((e) => e.relativePath).toSet(); + + final orphanedFiles = allFiles.where((file) { + final relativePath = file.path.split('$key/').last; + return !dbFiles.contains(relativePath); + }).toList(); + + final deletions = orphanedFiles.map((file) => file.delete()).toList(); + + try { + await Future.wait(deletions); + // ignore: empty_catches + } catch (e) {} + } +} diff --git a/lib/main.dart b/lib/main.dart index 3760fecf..02fc6b23 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,6 +11,7 @@ import 'package:frosty/apis/bttv_api.dart'; import 'package:frosty/apis/ffz_api.dart'; import 'package:frosty/apis/seventv_api.dart'; import 'package:frosty/apis/twitch_api.dart'; +import 'package:frosty/cache_manager.dart'; import 'package:frosty/firebase_options.dart'; import 'package:frosty/screens/home/home.dart'; import 'package:frosty/screens/onboarding/onboarding_intro.dart'; @@ -26,6 +27,8 @@ import 'package:shared_preferences/shared_preferences.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); + CustomCacheManager.removeOrphanedCacheFiles(); + await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); diff --git a/lib/models/irc.dart b/lib/models/irc.dart index 35f12b9a..ce027904 100644 --- a/lib/models/irc.dart +++ b/lib/models/irc.dart @@ -4,6 +4,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:frosty/cache_manager.dart'; import 'package:frosty/constants.dart'; import 'package:frosty/models/badges.dart'; import 'package:frosty/models/emotes.dart'; @@ -199,6 +200,7 @@ class IRCMessage { preferBelow: false, message: 'Sent from ${sourceChannelUser.displayName}', child: CachedNetworkImage( + cacheManager: CustomCacheManager.instance, imageUrl: sourceChannelUser.profileImageUrl, imageBuilder: (context, imageProvider) => Container( width: badgeSize, diff --git a/lib/screens/settings/other_settings.dart b/lib/screens/settings/other_settings.dart index e6e643ea..88e9a73c 100644 --- a/lib/screens/settings/other_settings.dart +++ b/lib/screens/settings/other_settings.dart @@ -3,9 +3,9 @@ import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:firebase_performance/firebase_performance.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:frosty/cache_manager.dart'; import 'package:frosty/screens/settings/stores/settings_store.dart'; import 'package:frosty/screens/settings/widgets/release_notes.dart'; import 'package:frosty/screens/settings/widgets/settings_list_switch.dart'; @@ -111,7 +111,8 @@ class _OtherSettingsState extends State { onTap: () async { HapticFeedback.mediumImpact(); - await DefaultCacheManager().emptyCache(); + await CustomCacheManager.instance.emptyCache(); + await CustomCacheManager.removeOrphanedCacheFiles(); if (!context.mounted) return; diff --git a/lib/widgets/cached_image.dart b/lib/widgets/cached_image.dart index 4e9e2eac..fb461be5 100644 --- a/lib/widgets/cached_image.dart +++ b/lib/widgets/cached_image.dart @@ -1,5 +1,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/widgets.dart'; +import 'package:frosty/cache_manager.dart'; /// A wrapper around [CachedNetworkImage] that adds custom defaults for Frosty. class FrostyCachedNetworkImage extends StatelessWidget { @@ -43,6 +44,7 @@ class FrostyCachedNetworkImage extends StatelessWidget { fadeInCurve: Curves.easeOut, fadeOutCurve: Curves.easeIn, fit: fit, + cacheManager: CustomCacheManager.instance, ); } } diff --git a/lib/widgets/photo_view.dart b/lib/widgets/photo_view.dart index b3b1cfd8..26dc1695 100644 --- a/lib/widgets/photo_view.dart +++ b/lib/widgets/photo_view.dart @@ -1,5 +1,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; +import 'package:frosty/cache_manager.dart'; import 'package:frosty/theme.dart'; import 'package:photo_view/photo_view.dart'; import 'package:provider/provider.dart'; @@ -28,7 +29,10 @@ class _FrostyPhotoViewDialogState extends State { : DismissDirection.none, onDismissed: Navigator.of(context).pop, child: PhotoView( - imageProvider: CachedNetworkImageProvider(widget.imageUrl), + imageProvider: CachedNetworkImageProvider( + widget.imageUrl, + cacheManager: CustomCacheManager.instance, + ), scaleStateChangedCallback: (value) => setState(() => photoViewScaleState = value), backgroundDecoration: diff --git a/pubspec.lock b/pubspec.lock index 0674e17a..975edae1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -806,7 +806,7 @@ packages: source: hosted version: "1.1.0" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" diff --git a/pubspec.yaml b/pubspec.yaml index 1745141d..3ed22400 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -20,7 +20,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 4.9.1+57 environment: - sdk: ^3.5.0 + sdk: ^3.5.0 # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -29,216 +29,217 @@ environment: # the latest version available on pub.dev. To see which dependencies have newer # versions available, run `flutter pub outdated`. dependencies: - flutter: - sdk: flutter - json_annotation: ^4.6.0 + flutter: + sdk: flutter + json_annotation: ^4.6.0 - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - http: ^1.1.0 - web_socket_channel: ^3.0.0 - flutter_secure_storage: ^9.0.0 - intl: ^0.20.1 - cached_network_image: ^3.1.0 - webview_flutter: ^4.2.0 - provider: ^6.0.1 - mobx: ^2.0.7+4 - flutter_mobx: ^2.0.0 - shared_preferences: ^2.0.8 - flutter_svg: ^2.0.5 - url_launcher: ^6.0.17 - package_info_plus: ^8.0.0 - collection: ^1.16.0 - flutter_cache_manager: ^3.3.0 - device_info_plus: ^11.1.1 - wakelock_plus: ^1.1.2 - simple_icons: ^14.6.1 - simple_pip_mode: - git: - url: https://github.com/tommyxchow/simple_pip_mode_flutter - firebase_core: ^3.7.0 - firebase_crashlytics: ^4.1.4 - firebase_performance: ^0.10.0+9 - firebase_analytics: ^11.3.4 - advanced_in_app_review: ^1.1.3 - webview_flutter_android: ^4.0.2 - webview_flutter_wkwebview: ^3.4.3 - photo_view: ^0.15.0 - flutter_markdown: ^0.7.3+1 - flutter_colorpicker: ^1.1.0 + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + http: ^1.1.0 + web_socket_channel: ^3.0.0 + flutter_secure_storage: ^9.0.0 + intl: ^0.20.1 + cached_network_image: ^3.1.0 + webview_flutter: ^4.2.0 + provider: ^6.0.1 + mobx: ^2.0.7+4 + flutter_mobx: ^2.0.0 + shared_preferences: ^2.0.8 + flutter_svg: ^2.0.5 + url_launcher: ^6.0.17 + package_info_plus: ^8.0.0 + collection: ^1.16.0 + flutter_cache_manager: ^3.3.0 + device_info_plus: ^11.1.1 + wakelock_plus: ^1.1.2 + simple_icons: ^14.6.1 + simple_pip_mode: + git: + url: https://github.com/tommyxchow/simple_pip_mode_flutter + firebase_core: ^3.7.0 + firebase_crashlytics: ^4.1.4 + firebase_performance: ^0.10.0+9 + firebase_analytics: ^11.3.4 + advanced_in_app_review: ^1.1.3 + webview_flutter_android: ^4.0.2 + webview_flutter_wkwebview: ^3.4.3 + photo_view: ^0.15.0 + flutter_markdown: ^0.7.3+1 + flutter_colorpicker: ^1.1.0 + path_provider: ^2.1.5 dev_dependencies: - flutter_test: - sdk: flutter + flutter_test: + sdk: flutter - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. - flutter_lints: ^5.0.0 - build_runner: ^2.1.1 - json_serializable: ^6.0.0 - mobx_codegen: ^2.0.6 - flutter_native_splash: ^2.0.5 - flutter_launcher_icons: ^0.14.1 + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^5.0.0 + build_runner: ^2.1.1 + json_serializable: ^6.0.0 + mobx_codegen: ^2.0.6 + flutter_native_splash: ^2.0.5 + flutter_launcher_icons: ^0.14.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter packages. flutter: - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true - fonts: - - family: Inter - fonts: - - asset: assets/fonts/Inter-Thin.ttf - - asset: assets/fonts/Inter-ExtraLight.ttf - - asset: assets/fonts/Inter-Light.ttf - - asset: assets/fonts/Inter-Regular.ttf - - asset: assets/fonts/Inter-Medium.ttf - - asset: assets/fonts/Inter-SemiBold.ttf - - asset: assets/fonts/Inter-Bold.ttf - - asset: assets/fonts/Inter-ExtraBold.ttf - - asset: assets/fonts/Inter-Black.ttf - - asset: assets/fonts/Inter-ThinItalic.ttf - - asset: assets/fonts/Inter-ExtraLightItalic.ttf - - asset: assets/fonts/Inter-LightItalic.ttf - - asset: assets/fonts/Inter-Italic.ttf - - asset: assets/fonts/Inter-MediumItalic.ttf - - asset: assets/fonts/Inter-SemiBoldItalic.ttf - - asset: assets/fonts/Inter-BoldItalic.ttf - - asset: assets/fonts/Inter-ExtraBoldItalic.ttf - - asset: assets/fonts/Inter-BlackItalic.ttf + fonts: + - family: Inter + fonts: + - asset: assets/fonts/Inter-Thin.ttf + - asset: assets/fonts/Inter-ExtraLight.ttf + - asset: assets/fonts/Inter-Light.ttf + - asset: assets/fonts/Inter-Regular.ttf + - asset: assets/fonts/Inter-Medium.ttf + - asset: assets/fonts/Inter-SemiBold.ttf + - asset: assets/fonts/Inter-Bold.ttf + - asset: assets/fonts/Inter-ExtraBold.ttf + - asset: assets/fonts/Inter-Black.ttf + - asset: assets/fonts/Inter-ThinItalic.ttf + - asset: assets/fonts/Inter-ExtraLightItalic.ttf + - asset: assets/fonts/Inter-LightItalic.ttf + - asset: assets/fonts/Inter-Italic.ttf + - asset: assets/fonts/Inter-MediumItalic.ttf + - asset: assets/fonts/Inter-SemiBoldItalic.ttf + - asset: assets/fonts/Inter-BoldItalic.ttf + - asset: assets/fonts/Inter-ExtraBoldItalic.ttf + - asset: assets/fonts/Inter-BlackItalic.ttf - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg - assets: - - assets/icons/logo.svg - - assets/fonts/ - - assets/release-notes.md + assets: + - assets/icons/logo.svg + - assets/fonts/ + - assets/release-notes.md - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/to/resolution-aware-images + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/to/asset-from-package + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/to/asset-from-package - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/to/font-from-package + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/to/font-from-package flutter_native_splash: - # This package generates native code to customize Flutter's default white native splash screen - # with background color and splash image. - # Customize the parameters below, and run the following command in the terminal: - # flutter pub run flutter_native_splash:create - # To restore Flutter's default white splash screen, run the following command in the terminal: - # flutter pub run flutter_native_splash:remove + # This package generates native code to customize Flutter's default white native splash screen + # with background color and splash image. + # Customize the parameters below, and run the following command in the terminal: + # flutter pub run flutter_native_splash:create + # To restore Flutter's default white splash screen, run the following command in the terminal: + # flutter pub run flutter_native_splash:remove - # color or background_image is the only required parameter. Use color to set the background - # of your splash screen to a solid color. Use background_image to set the background of your - # splash screen to a png image. This is useful for gradients. The image will be stretch to the - # size of the app. Only one parameter can be used, color and background_image cannot both be set. - # color: "#000000" - background_image: "assets/icons/bg.png" + # color or background_image is the only required parameter. Use color to set the background + # of your splash screen to a solid color. Use background_image to set the background of your + # splash screen to a png image. This is useful for gradients. The image will be stretch to the + # size of the app. Only one parameter can be used, color and background_image cannot both be set. + # color: "#000000" + background_image: "assets/icons/bg.png" - # Optional parameters are listed below. To enable a parameter, uncomment the line by removing - # the leading # character. + # Optional parameters are listed below. To enable a parameter, uncomment the line by removing + # the leading # character. - # The image parameter allows you to specify an image used in the splash screen. It must be a - # png file and should be sized for 4x pixel density. - image: assets/icons/logo.png + # The image parameter allows you to specify an image used in the splash screen. It must be a + # png file and should be sized for 4x pixel density. + image: assets/icons/logo.png - # This property allows you to specify an image used as branding in the splash screen. It must be - # a png file. Currently, it is only supported for Android and iOS. - # branding: assets/dart.png + # This property allows you to specify an image used as branding in the splash screen. It must be + # a png file. Currently, it is only supported for Android and iOS. + # branding: assets/dart.png - # Specify your branding image for dark mode. - #branding_dark: assets/dart_dark.png + # Specify your branding image for dark mode. + #branding_dark: assets/dart_dark.png - # To position the branding image at the bottom of the screen you can use bottom, bottomRight, - # and bottomLeft. The default values is bottom if not specified or specified something else. - # - # Make sure this content mode value should not be similar to android_gravity value and ios_content_mode - # value. - # branding_mode: bottom + # To position the branding image at the bottom of the screen you can use bottom, bottomRight, + # and bottomLeft. The default values is bottom if not specified or specified something else. + # + # Make sure this content mode value should not be similar to android_gravity value and ios_content_mode + # value. + # branding_mode: bottom - # The color_dark, background_image_dark, and image_dark are parameters that set the background - # and image when the device is in dark mode. If they are not specified, the app will use the - # parameters from above. If the image_dark parameter is specified, color_dark or - # background_image_dark must be specified. color_dark and background_image_dark cannot both be - # set. - #color_dark: "#042a49" - #background_image_dark: "assets/dark-background.png" - #image_dark: assets/splash-invert.png + # The color_dark, background_image_dark, and image_dark are parameters that set the background + # and image when the device is in dark mode. If they are not specified, the app will use the + # parameters from above. If the image_dark parameter is specified, color_dark or + # background_image_dark must be specified. color_dark and background_image_dark cannot both be + # set. + #color_dark: "#042a49" + #background_image_dark: "assets/dark-background.png" + #image_dark: assets/splash-invert.png - # The android, ios and web parameters can be used to disable generating a splash screen on a given - # platform. - #android: false - #ios: false - #web: false + # The android, ios and web parameters can be used to disable generating a splash screen on a given + # platform. + #android: false + #ios: false + #web: false - # The position of the splash image can be set with android_gravity, ios_content_mode, and - # web_image_mode parameters. All default to center. - # - # android_gravity can be one of the following Android Gravity (see - # https://developer.android.com/reference/android/view/Gravity): bottom, center, - # center_horizontal, center_vertical, clip_horizontal, clip_vertical, end, fill, fill_horizontal, - # fill_vertical, left, right, start, or top. - #android_gravity: center - # - # ios_content_mode can be one of the following iOS UIView.ContentMode (see - # https://developer.apple.com/documentation/uikit/uiview/contentmode): scaleToFill, - # scaleAspectFit, scaleAspectFill, center, top, bottom, left, right, topLeft, topRight, - # bottomLeft, or bottomRight. - #ios_content_mode: center - # - # web_image_mode can be one of the following modes: center, contain, stretch, and cover. - #web_image_mode: center + # The position of the splash image can be set with android_gravity, ios_content_mode, and + # web_image_mode parameters. All default to center. + # + # android_gravity can be one of the following Android Gravity (see + # https://developer.android.com/reference/android/view/Gravity): bottom, center, + # center_horizontal, center_vertical, clip_horizontal, clip_vertical, end, fill, fill_horizontal, + # fill_vertical, left, right, start, or top. + #android_gravity: center + # + # ios_content_mode can be one of the following iOS UIView.ContentMode (see + # https://developer.apple.com/documentation/uikit/uiview/contentmode): scaleToFill, + # scaleAspectFit, scaleAspectFill, center, top, bottom, left, right, topLeft, topRight, + # bottomLeft, or bottomRight. + #ios_content_mode: center + # + # web_image_mode can be one of the following modes: center, contain, stretch, and cover. + #web_image_mode: center - # To hide the notification bar, use the fullscreen parameter. Has no affect in web since web - # has no notification bar. Defaults to false. - # NOTE: Unlike Android, iOS will not automatically show the notification bar when the app loads. - # To show the notification bar, add the following code to your Flutter app: - # WidgetsFlutterBinding.ensureInitialized(); - # SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom, SystemUiOverlay.top]); - #fullscreen: true + # To hide the notification bar, use the fullscreen parameter. Has no affect in web since web + # has no notification bar. Defaults to false. + # NOTE: Unlike Android, iOS will not automatically show the notification bar when the app loads. + # To show the notification bar, add the following code to your Flutter app: + # WidgetsFlutterBinding.ensureInitialized(); + # SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom, SystemUiOverlay.top]); + #fullscreen: true - # If you have changed the name(s) of your info.plist file(s), you can specify the filename(s) - # with the info_plist_files parameter. Remove only the # characters in the three lines below, - # do not remove any spaces: - #info_plist_files: - # - 'ios/Runner/Info-Debug.plist' - # - 'ios/Runner/Info-Release.plist' + # If you have changed the name(s) of your info.plist file(s), you can specify the filename(s) + # with the info_plist_files parameter. Remove only the # characters in the three lines below, + # do not remove any spaces: + #info_plist_files: + # - 'ios/Runner/Info-Debug.plist' + # - 'ios/Runner/Info-Release.plist' flutter_launcher_icons: - image_path_android: "assets/icons/icon_android.png" - image_path_ios: "assets/icons/icon_ios.png" - android: true - ios: true - remove_alpha_ios: true + image_path_android: "assets/icons/icon_android.png" + image_path_ios: "assets/icons/icon_ios.png" + android: true + ios: true + remove_alpha_ios: true