diff --git a/CHANGELOG.md b/CHANGELOG.md index 3581d0b..6176d82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,15 @@ +## 3.0.0 + +* Null safety and interim gif support. + ## 2.0.2-alpha * Limited gif support. Gifs are compressed into webp and rendered as of now. ## 2.0.1 -* Update to null safety package dependencies. OCI still needs to migrate code to respect null safety. Additionally fix issue with hero widgets +* Update to null safety package dependencies. OCI still needs to migrate code to respect null safety. Additionally fix + issue with hero widgets ## 2.0.0 @@ -28,7 +33,8 @@ ## 1.0.0-beta -* Prevent unnecessary downloads from happening by caching the image from the original url and resizing it for different sizes. +* Prevent unnecessary downloads from happening by caching the image from the original url and resizing it for different + sizes. ## 0.1.15 @@ -62,25 +68,24 @@ * Fix dependency version breaking change in flutter cache library. - ## 0.1.7 - * Add style fixes ## 0.1.6 -* Add experimental support for streamed downloading via `useHttpStream` flag which further -reduces the memory footprint. +* Add experimental support for streamed downloading via `useHttpStream` flag which further reduces the memory footprint. ## 0.1.5 * Minor lint issues and formatting patched. ## 0.1.4 + * Fixed issue faced while specifying custom width and height. ## 0.1.3 + * Readme updated. ## 0.1.2 diff --git a/example/ios/Podfile b/example/ios/Podfile index b30a428..1e8c3c9 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -10,81 +10,32 @@ project 'Runner', { 'Release' => :release, } -def parse_KV_file(file, separator='=') - file_abs_path = File.expand_path(file) - if !File.exists? file_abs_path - return []; +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" end - generated_key_values = {} - skip_line_start_symbols = ["#", "/"] - File.foreach(file_abs_path) do |line| - next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } - plugin = line.split(pattern=separator) - if plugin.length == 2 - podname = plugin[0].strip() - path = plugin[1].strip() - podpath = File.expand_path("#{path}", file_abs_path) - generated_key_values[podname] = podpath - else - puts "Invalid plugin specification: #{line}" - end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches end - generated_key_values + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" end +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + target 'Runner' do use_frameworks! use_modular_headers! - - # Flutter Pod - copied_flutter_dir = File.join(__dir__, 'Flutter') - copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') - copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') - unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) - # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. - # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. - # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. - - generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') - unless File.exist?(generated_xcode_build_settings_path) - raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) - cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; - - unless File.exist?(copied_framework_path) - FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) - end - unless File.exist?(copied_podspec_path) - FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) - end - end - - # Keep pod path relative so it can be checked into Podfile.lock. - pod 'Flutter', :path => 'Flutter' - - # Plugin Pods - - # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock - # referring to absolute paths on developers' machines. - system('rm -rf .symlinks') - system('mkdir -p .symlinks/plugins') - plugin_pods = parse_KV_file('../.flutter-plugins') - plugin_pods.each do |name, path| - symlink = File.join('.symlinks', 'plugins', name) - File.symlink(path, symlink) - pod name, :path => File.join(symlink, 'ios') - end + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end -# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. -install! 'cocoapods', :disable_input_output_paths => true - post_install do |installer| installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['ENABLE_BITCODE'] = 'NO' - end + flutter_additional_ios_build_settings(target) end end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index fd3b40f..d126ca5 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -21,25 +21,22 @@ PODS: - Mantle/extobjc (2.1.1) - path_provider (0.0.1): - Flutter - - path_provider_linux (0.0.1): - - Flutter - - path_provider_macos (0.0.1): - - Flutter - SDWebImage/Core (5.5.2) - SDWebImageWebPCoder (0.5.0): - libwebp (~> 1.0) - SDWebImage/Core (~> 5.5) - - sqflite (0.0.1): + - sqflite (0.0.2): + - Flutter + - FMDB (>= 2.7.5) + - url_launcher (0.0.1): - Flutter - - FMDB (~> 2.7.2) DEPENDENCIES: - Flutter (from `Flutter`) - flutter_image_compress (from `.symlinks/plugins/flutter_image_compress/ios`) - path_provider (from `.symlinks/plugins/path_provider/ios`) - - path_provider_linux (from `.symlinks/plugins/path_provider_linux/ios`) - - path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) + - url_launcher (from `.symlinks/plugins/url_launcher/ios`) SPEC REPOS: trunk: @@ -56,26 +53,23 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_image_compress/ios" path_provider: :path: ".symlinks/plugins/path_provider/ios" - path_provider_linux: - :path: ".symlinks/plugins/path_provider_linux/ios" - path_provider_macos: - :path: ".symlinks/plugins/path_provider_macos/ios" sqflite: :path: ".symlinks/plugins/sqflite/ios" + url_launcher: + :path: ".symlinks/plugins/url_launcher/ios" SPEC CHECKSUMS: - Flutter: 0e3d915762c693b495b44d77113d4970485de6ec + Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c flutter_image_compress: 082f8daaf6c1b0c9fe798251c750ef0ecd98d7ae FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3 Mantle: 35238ae6f2e2b2d474fa7b67fee82a59fea71915 path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c - path_provider_linux: 4d630dc393e1f20364f3e3b4a2ff41d9674a84e4 - path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0 SDWebImage: 4d5c027c935438f341ed33dbac53ff9f479922ca SDWebImageWebPCoder: e7ae855f058e3dcae99696920b6a5d134e9dcddf - sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0 + sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef -PODFILE CHECKSUM: 1b66dae606f75376c5f2135a8290850eeb09ae83 +PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c -COCOAPODS: 1.9.1 +COCOAPODS: 1.10.1 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index babb62a..9e89b83 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -240,9 +240,28 @@ files = ( ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework", + "${BUILT_PRODUCTS_DIR}/Mantle/Mantle.framework", + "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework", + "${BUILT_PRODUCTS_DIR}/SDWebImageWebPCoder/SDWebImageWebPCoder.framework", + "${BUILT_PRODUCTS_DIR}/flutter_image_compress/flutter_image_compress.framework", + "${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework", + "${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework", + "${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework", + "${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FMDB.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mantle.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageWebPCoder.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_image_compress.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 1d526a1..919434a 100644 --- a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/example/lib/main.dart b/example/lib/main.dart index 23463a8..912fbc5 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -74,18 +74,18 @@ class HumblerookiePluginExample extends StatelessWidget { 1, ); } - return MaterialColor(color.value, swatch); + return MaterialColor(color.value, swatch as Map); } } /// A Flutter example demonstrating how the [pluginName] plugin could be used class AppHome extends StatefulWidget { /// Constructs the [AppHome] class - AppHome({Key key, this.title}) : super(key: key); + AppHome({Key? key, this.title}) : super(key: key); /// The [title] of the application, which is shown in the application's /// title bar. - final String title; + final String? title; @override _AppHomeState createState() => _AppHomeState(); diff --git a/example/pubspec.lock b/example/pubspec.lock index 2ffbc26..b6330aa 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -14,7 +14,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0" + version: "2.6.1" boolean_selector: dependency: transitive description: @@ -56,14 +56,14 @@ packages: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "3.0.1" cupertino_icons: dependency: "direct main" description: name: cupertino_icons url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "1.0.3" fake_async: dependency: transitive description: @@ -77,14 +77,14 @@ packages: name: ffi url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.1.2" file: dependency: "direct main" description: name: file url: "https://pub.dartlang.org" source: hosted - version: "6.1.0" + version: "6.1.2" flutter: dependency: "direct main" description: flutter @@ -103,7 +103,7 @@ packages: name: flutter_cache_manager url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "3.1.2" flutter_image_compress: dependency: transitive description: @@ -127,7 +127,7 @@ packages: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.13.1" + version: "0.13.3" http_parser: dependency: transitive description: @@ -169,7 +169,7 @@ packages: name: octo_image url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.0+1" optimized_cached_image: dependency: "direct main" description: @@ -190,7 +190,7 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" path_provider_linux: dependency: transitive description: @@ -211,28 +211,28 @@ packages: name: path_provider_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" path_provider_windows: dependency: transitive description: name: path_provider_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" pedantic: dependency: transitive description: name: pedantic url: "https://pub.dartlang.org" source: hosted - version: "1.11.0" + version: "1.11.1" petitparser: dependency: transitive description: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "4.0.2" + version: "4.1.0" platform: dependency: transitive description: @@ -246,21 +246,21 @@ packages: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "2.0.1" process: dependency: transitive description: name: process url: "https://pub.dartlang.org" source: hosted - version: "4.1.0" + version: "4.2.1" rxdart: dependency: transitive description: name: rxdart url: "https://pub.dartlang.org" source: hosted - version: "0.26.0" + version: "0.27.1" sky_engine: dependency: transitive description: flutter @@ -272,7 +272,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" sprintf: dependency: transitive description: @@ -335,7 +335,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19" + version: "0.3.0" typed_data: dependency: transitive description: @@ -349,49 +349,49 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "5.7.10" + version: "6.0.9" url_launcher_linux: dependency: transitive description: name: url_launcher_linux url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+4" + version: "2.0.0" url_launcher_macos: dependency: transitive description: name: url_launcher_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+9" + version: "2.0.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.9" + version: "2.0.4" url_launcher_web: dependency: transitive description: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.5+3" + version: "2.0.1" url_launcher_windows: dependency: transitive description: name: url_launcher_windows url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+3" + version: "2.0.0" uuid: dependency: transitive description: name: uuid url: "https://pub.dartlang.org" source: hosted - version: "3.0.3" + version: "3.0.4" vector_math: dependency: transitive description: @@ -405,7 +405,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.2.5" xdg_directories: dependency: transitive description: @@ -419,7 +419,7 @@ packages: name: xml url: "https://pub.dartlang.org" source: hosted - version: "5.0.2" + version: "5.1.2" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.13.0 <3.0.0" flutter: ">=2.0.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index f4d3050..d48d38a 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -3,7 +3,7 @@ description: Demonstrates how to use the optimized_cached_image plugin. publish_to: 'none' environment: - sdk: ">=2.1.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: flutter: @@ -11,11 +11,11 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.2 - url_launcher: ^5.4.11 + cupertino_icons: ^1.0.3 + url_launcher: ^6.0.9 optimized_cached_image: path: ../ - file: ^6.1.0 + file: ^6.1.2 dev_dependencies: flutter_test: diff --git a/lib/src/cache/default_image_cache_manager.dart b/lib/src/cache/default_image_cache_manager.dart index 6f5b03d..4921bc6 100644 --- a/lib/src/cache/default_image_cache_manager.dart +++ b/lib/src/cache/default_image_cache_manager.dart @@ -7,10 +7,10 @@ import 'package:optimized_cached_image/src/cache/image_cache_manager.dart'; class DefaultImageCacheManager extends CacheManager with OicImageCacheManager { static const key = 'libCachedImageData'; - static DefaultImageCacheManager _instance; + static DefaultImageCacheManager? _instance; factory DefaultImageCacheManager() { _instance ??= DefaultImageCacheManager._(); - return _instance; + return _instance!; } DefaultImageCacheManager._() : super(Config(key)); } diff --git a/lib/src/cache/image_cache_manager.dart b/lib/src/cache/image_cache_manager.dart index 34712e0..bd9809c 100644 --- a/lib/src/cache/image_cache_manager.dart +++ b/lib/src/cache/image_cache_manager.dart @@ -1,7 +1,11 @@ +import 'dart:async'; import 'dart:math'; +import 'package:flutter/widgets.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:image/image.dart'; +import 'package:file/file.dart'; import '../transformer/image_transformer.dart'; +import 'dart:ui' as ui; const supportedFileNames = ['jpg', 'jpeg', 'png', 'tga', 'gif', 'cur', 'ico']; mixin OicImageCacheManager on BaseCacheManager { @@ -19,11 +23,11 @@ mixin OicImageCacheManager on BaseCacheManager { Stream getImageFile( String url, { - String key, - Map headers, - bool withProgress, - int maxHeight, - int maxWidth, + String? key, + Map? headers, + bool withProgress = false, + int? maxHeight, + int? maxWidth, }) async* { if (maxHeight == null && maxWidth == null) { yield* getFileStream(url, @@ -55,7 +59,7 @@ mixin OicImageCacheManager on BaseCacheManager { maxHeight: maxHeight, ); } - yield* _runningResizes[resizedKey]; + yield* _runningResizes[resizedKey]!; _runningResizes.remove(resizedKey); } @@ -63,8 +67,8 @@ mixin OicImageCacheManager on BaseCacheManager { Future _resizeImageFile( FileInfo originalFile, String key, - int maxWidth, - int maxHeight, + int? maxWidth, + int? maxHeight, ) async { var originalFileName = originalFile.file.path; var fileExtension = originalFileName.split('.').last; @@ -72,7 +76,15 @@ mixin OicImageCacheManager on BaseCacheManager { return originalFile; } - var image = decodeImage(await originalFile.file.readAsBytes()); + var image = await _decodeImage(originalFile.file); + var shouldResize = maxWidth != null + ? image.width > maxWidth + : false || maxHeight != null + ? image.height > maxHeight + : false; + + if (!shouldResize) return originalFile; + if (maxWidth != null && maxHeight != null) { var resizeFactorWidth = image.width / maxWidth; var resizeFactorHeight = image.height / maxHeight; @@ -106,10 +118,10 @@ mixin OicImageCacheManager on BaseCacheManager { String url, String originalKey, String resizedKey, - Map headers, + Map? headers, bool withProgress, { - int maxWidth, - int maxHeight, + int? maxWidth, + int? maxHeight, }) async* { await for (var response in getFileStream( url, @@ -131,3 +143,21 @@ mixin OicImageCacheManager on BaseCacheManager { } } } + +Future _decodeImage(File file, + {int? width, int? height, bool allowUpscaling = false}) { + var shouldResize = width != null || height != null; + var fileImage = FileImage(file); + final image = shouldResize + ? ResizeImage(fileImage, + width: width, height: height, allowUpscaling: allowUpscaling) + : fileImage as ImageProvider; + final completer = Completer(); + image + .resolve(const ImageConfiguration()) + .addListener(ImageStreamListener((info, _) { + completer.complete(info.image); + image.evict(); + })); + return completer.future; +} diff --git a/lib/src/image_provider/_image_provider_io.dart b/lib/src/image_provider/_image_provider_io.dart index 04249c9..d2567a5 100644 --- a/lib/src/image_provider/_image_provider_io.dart +++ b/lib/src/image_provider/_image_provider_io.dart @@ -29,12 +29,11 @@ class OptimizedCacheImageProvider this.cacheManager, this.cacheKey, //ignore: avoid_unused_constructor_parameters - ImageRenderMethodForWeb imageRenderMethodForWeb, - }) : assert(url != null), - assert(scale != null); + ImageRenderMethodForWeb? imageRenderMethodForWeb, + }); @override - final BaseCacheManager cacheManager; + final BaseCacheManager? cacheManager; /// Web url of the image to load @override @@ -42,7 +41,7 @@ class OptimizedCacheImageProvider /// Cache key of the image to cache @override - final String cacheKey; + final String? cacheKey; /// Scale of the image @override @@ -50,17 +49,17 @@ class OptimizedCacheImageProvider /// Listener to be called when images fails to load. @override - final image_provider.ErrorListener errorListener; + final image_provider.ErrorListener? errorListener; /// Set headers for the image provider, for example for authentication @override - final Map headers; + final Map? headers; @override - final int maxHeight; + final int? maxHeight; @override - final int maxWidth; + final int? maxWidth; @override Future obtainKey( @@ -87,7 +86,7 @@ class OptimizedCacheImageProvider } Stream _loadAsync( - OptimizedCacheImageProvider key, + image_provider.OptimizedCacheImageProvider key, StreamController chunkEvents, DecoderCallback decode, ) async* { @@ -130,7 +129,7 @@ class OptimizedCacheImageProvider // have had a chance to track the key in the cache at all. // Schedule a microtask to give the cache a chance to add the key. scheduleMicrotask(() { - PaintingBinding.instance.imageCache.evict(key); + PaintingBinding.instance?.imageCache?.evict(key); }); errorListener?.call(); diff --git a/lib/src/image_provider/_image_provider_web.dart b/lib/src/image_provider/_image_provider_web.dart index 02436ff..d8fafdb 100644 --- a/lib/src/image_provider/_image_provider_web.dart +++ b/lib/src/image_provider/_image_provider_web.dart @@ -26,36 +26,34 @@ class OptimizedCacheImageProvider this.headers, this.cacheManager, this.cacheKey, - ImageRenderMethodForWeb imageRenderMethodForWeb, - }) : _imageRenderMethodForWeb = - imageRenderMethodForWeb ?? ImageRenderMethodForWeb.HtmlImage, - assert(url != null), - assert(scale != null); + ImageRenderMethodForWeb? imageRenderMethodForWeb, + }) : _imageRenderMethodForWeb = + imageRenderMethodForWeb ?? ImageRenderMethodForWeb.HtmlImage; @override - final BaseCacheManager cacheManager; + final BaseCacheManager? cacheManager; @override final String url; @override - final String cacheKey; + final String? cacheKey; @override final double scale; /// Listener to be called when images fails to load. @override - final image_provider.ErrorListener errorListener; + final image_provider.ErrorListener? errorListener; @override - final Map headers; + final Map? headers; @override - final int maxHeight; + final int? maxHeight; @override - final int maxWidth; + final int? maxWidth; final ImageRenderMethodForWeb _imageRenderMethodForWeb; @@ -78,9 +76,9 @@ class OptimizedCacheImageProvider informationCollector: _imageStreamInformationCollector(key)); } - InformationCollector _imageStreamInformationCollector( + InformationCollector? _imageStreamInformationCollector( image_provider.OptimizedCacheImageProvider key) { - InformationCollector collector; + InformationCollector? collector; assert(() { collector = () { return [ @@ -137,7 +135,7 @@ class OptimizedCacheImageProvider // have had a chance to track the key in the cache at all. // Schedule a microtask to give the cache a chance to add the key. scheduleMicrotask(() { - PaintingBinding.instance.imageCache.evict(key); + PaintingBinding.instance?.imageCache?.evict(key); }); errorListener?.call(); diff --git a/lib/src/image_provider/multi_image_stream_completer.dart b/lib/src/image_provider/multi_image_stream_completer.dart index 58fbe82..63fa5ea 100644 --- a/lib/src/image_provider/multi_image_stream_completer.dart +++ b/lib/src/image_provider/multi_image_stream_completer.dart @@ -16,12 +16,11 @@ class MultiImageStreamCompleter extends ImageStreamCompleter { /// [chunkEvents] should indicate the [ImageChunkEvent]s of the first image /// to show. MultiImageStreamCompleter({ - @required Stream codec, - @required double scale, - Stream chunkEvents, - InformationCollector informationCollector, - }) : assert(codec != null), - _informationCollector = informationCollector, + required Stream codec, + required double scale, + Stream? chunkEvents, + InformationCollector? informationCollector, + }) : _informationCollector = informationCollector, _scale = scale { codec.listen((event) { if (_timer != null) { @@ -54,18 +53,18 @@ class MultiImageStreamCompleter extends ImageStreamCompleter { } } - ui.Codec _codec; - ui.Codec _nextImageCodec; + ui.Codec? _codec; + ui.Codec? _nextImageCodec; final double _scale; - final InformationCollector _informationCollector; - ui.FrameInfo _nextFrame; + final InformationCollector? _informationCollector; + ui.FrameInfo? _nextFrame; // When the current was first shown. - Duration _shownTimestamp; + Duration? _shownTimestamp; // The requested duration for the current frame; - Duration _frameDuration; + Duration? _frameDuration; // How many frames have been emitted so far. int _framesEmitted = 0; - Timer _timer; + Timer? _timer; // Used to guard against registering multiple _handleAppFrame callbacks for the same frame. bool _frameCallbackScheduled = false; @@ -73,7 +72,7 @@ class MultiImageStreamCompleter extends ImageStreamCompleter { void _switchToNewCodec() { _framesEmitted = 0; _timer = null; - _handleCodecReady(_nextImageCodec); + _handleCodecReady(_nextImageCodec!); _nextImageCodec = null; } @@ -90,22 +89,22 @@ class MultiImageStreamCompleter extends ImageStreamCompleter { _frameCallbackScheduled = false; if (!hasListeners) return; if (_isFirstFrame() || _hasFrameDurationPassed(timestamp)) { - _emitFrame(ImageInfo(image: _nextFrame.image, scale: _scale)); + _emitFrame(ImageInfo(image: _nextFrame!.image, scale: _scale)); _shownTimestamp = timestamp; - _frameDuration = _nextFrame.duration; + _frameDuration = _nextFrame!.duration; _nextFrame = null; - if (_framesEmitted % _codec.frameCount == 0 && _nextImageCodec != null) { + if (_framesEmitted % _codec!.frameCount == 0 && _nextImageCodec != null) { _switchToNewCodec(); } else { - final completedCycles = _framesEmitted ~/ _codec.frameCount; - if (_codec.repetitionCount == -1 || - completedCycles <= _codec.repetitionCount) { + final completedCycles = _framesEmitted ~/ _codec!.frameCount; + if (_codec!.repetitionCount == -1 || + completedCycles <= _codec!.repetitionCount) { _decodeNextFrameAndSchedule(); } } return; } - final delay = _frameDuration - (timestamp - _shownTimestamp); + final delay = _frameDuration! - (timestamp - _shownTimestamp!); _timer = Timer(delay * timeDilation, _scheduleAppFrame); } @@ -115,12 +114,12 @@ class MultiImageStreamCompleter extends ImageStreamCompleter { bool _hasFrameDurationPassed(Duration timestamp) { assert(_shownTimestamp != null); - return timestamp - _shownTimestamp >= _frameDuration; + return timestamp - _shownTimestamp! >= _frameDuration!; } Future _decodeNextFrameAndSchedule() async { try { - _nextFrame = await _codec.getNextFrame(); + _nextFrame = await _codec!.getNextFrame(); } catch (exception, stack) { reportError( context: ErrorDescription('resolving an image frame'), @@ -131,7 +130,7 @@ class MultiImageStreamCompleter extends ImageStreamCompleter { ); return; } - if (_codec.frameCount == 1) { + if (_codec!.frameCount == 1) { // ImageStreamCompleter listeners removed while waiting for next frame to // be decoded. // There's no reason to emit the frame without active listeners. @@ -141,7 +140,7 @@ class MultiImageStreamCompleter extends ImageStreamCompleter { // This is not an animated image, just return it and don't schedule more // frames. - _emitFrame(ImageInfo(image: _nextFrame.image, scale: _scale)); + _emitFrame(ImageInfo(image: _nextFrame!.image, scale: _scale)); return; } _scheduleAppFrame(); @@ -152,7 +151,7 @@ class MultiImageStreamCompleter extends ImageStreamCompleter { return; } _frameCallbackScheduled = true; - SchedulerBinding.instance.scheduleFrameCallback(_handleAppFrame); + SchedulerBinding.instance?.scheduleFrameCallback(_handleAppFrame); } void _emitFrame(ImageInfo imageInfo) { diff --git a/lib/src/image_provider/optimized_cached_image_provider.dart b/lib/src/image_provider/optimized_cached_image_provider.dart index f93c8f4..08aaae5 100644 --- a/lib/src/image_provider/optimized_cached_image_provider.dart +++ b/lib/src/image_provider/optimized_cached_image_provider.dart @@ -40,47 +40,47 @@ abstract class OptimizedCacheImageProvider /// for the benefits of each method. const factory OptimizedCacheImageProvider( String url, { - int maxHeight, - int maxWidth, - String cacheKey, + int? maxHeight, + int? maxWidth, + String? cacheKey, double scale, @Deprecated('ErrorListener is deprecated, use listeners on the imagestream') - ErrorListener errorListener, - Map headers, - BaseCacheManager cacheManager, - ImageRenderMethodForWeb imageRenderMethodForWeb, + ErrorListener? errorListener, + Map? headers, + BaseCacheManager? cacheManager, + ImageRenderMethodForWeb? imageRenderMethodForWeb, }) = image_provider.OptimizedCacheImageProvider; /// Optional cache manager. If no cache manager is defined DefaultCacheManager() /// will be used. /// /// When running flutter on the web, the cacheManager is not used. - BaseCacheManager get cacheManager; + BaseCacheManager? get cacheManager; /// The errorListener is called when the ImageProvider failed loading the /// image. Deprecated in favor of [ImageStreamListener.onError]. @deprecated - ErrorListener get errorListener; + ErrorListener? get errorListener; /// The URL from which the image will be fetched. String get url; /// The Key from image for cache - String get cacheKey; + String? get cacheKey; /// The scale to place in the [ImageInfo] object of the image. double get scale; /// The HTTP headers that will be used to fetch image from network. - Map get headers; + Map? get headers; /// Max height in pixels for the image. When set the resized image is /// stored in the cache. - int get maxHeight; + int? get maxHeight; /// Max width in pixels for the image. When set the resized image is /// stored in the cache. - int get maxWidth; + int? get maxWidth; @override ImageStreamCompleter load( diff --git a/lib/src/oci_widget.dart b/lib/src/oci_widget.dart index 7b8a820..5f3ff8c 100644 --- a/lib/src/oci_widget.dart +++ b/lib/src/oci_widget.dart @@ -45,8 +45,8 @@ class OptimizedCacheImage extends StatelessWidget { /// to clear the image from the [ImageCache]. static Future evictFromCache( String url, { - String cacheKey, - BaseCacheManager cacheManager, + String? cacheKey, + BaseCacheManager? cacheManager, double scale = 1.0, }) async { cacheManager = cacheManager ?? DefaultCacheManager(); @@ -54,34 +54,34 @@ class OptimizedCacheImage extends StatelessWidget { return OptimizedCacheImageProvider(url, scale: scale).evict(); } - OptimizedCacheImageProvider _image; + OptimizedCacheImageProvider? _image; /// Option to use cachemanager with other settings - final BaseCacheManager cacheManager; + final BaseCacheManager? cacheManager; /// The target image that is displayed. final String imageUrl; /// The target image's cache key. - final String cacheKey; + final String? cacheKey; /// Optional builder to further customize the display of the image. - final ImageWidgetBuilder imageBuilder; + final ImageWidgetBuilder? imageBuilder; /// Widget displayed while the target [imageUrl] is loading. - final PlaceholderWidgetBuilder placeholder; + final PlaceholderWidgetBuilder? placeholder; /// Widget displayed while the target [imageUrl] is loading. - final ProgressIndicatorBuilder progressIndicatorBuilder; + final ProgressIndicatorBuilder? progressIndicatorBuilder; /// Widget displayed while the target [imageUrl] failed loading. - final LoadingErrorWidgetBuilder errorWidget; + final LoadingErrorWidgetBuilder? errorWidget; /// The duration of the fade-in animation for the [placeholder]. - final Duration placeholderFadeInDuration; + final Duration? placeholderFadeInDuration; /// The duration of the fade-out animation for the [placeholder]. - final Duration fadeOutDuration; + final Duration? fadeOutDuration; /// The curve of the fade-out animation for the [placeholder]. final Curve fadeOutCurve; @@ -98,7 +98,7 @@ class OptimizedCacheImage extends StatelessWidget { /// aspect ratio. This may result in a sudden change if the size of the /// placeholder widget does not match that of the target image. The size is /// also affected by the scale factor. - final double width; + final double? width; /// If non-null, require the image to have this height. /// @@ -106,13 +106,13 @@ class OptimizedCacheImage extends StatelessWidget { /// aspect ratio. This may result in a sudden change if the size of the /// placeholder widget does not match that of the target image. The size is /// also affected by the scale factor. - final double height; + final double? height; /// How to inscribe the image into the space allocated during layout. /// /// The default varies based on the other fields. See the discussion at /// [paintImage]. - final BoxFit fit; + final BoxFit? fit; /// How to align the image within its bounds. /// @@ -159,14 +159,14 @@ class OptimizedCacheImage extends StatelessWidget { final bool matchTextDirection; /// Optional headers for the http request of the image url - final Map httpHeaders; + final Map? httpHeaders; /// When set to true it will animate from the old image to the new image /// if the url changes. final bool useOldImageOnUrlChange; /// If non-null, this color is blended with each image pixel using [colorBlendMode]. - final Color color; + final Color? color; /// Used to combine [color] with this image. /// @@ -176,7 +176,7 @@ class OptimizedCacheImage extends StatelessWidget { /// See also: /// /// * [BlendMode], which includes an illustration of the effect of each blend mode. - final BlendMode colorBlendMode; + final BlendMode? colorBlendMode; /// Target the interpolation quality for image scaling. /// @@ -184,26 +184,26 @@ class OptimizedCacheImage extends StatelessWidget { final FilterQuality filterQuality; /// Will resize the image in memory to have a certain width using [ResizeImage] - final int memCacheWidth; + final int? memCacheWidth; /// Will resize the image in memory to have a certain height using [ResizeImage] - final int memCacheHeight; + final int? memCacheHeight; /// Will resize the image and store the resized image in the disk cache. - final int maxWidthDiskCache; + final int? maxWidthDiskCache; /// Will resize the image and store the resized image in the disk cache. - final int maxHeightDiskCache; + final int? maxHeightDiskCache; - final ImageRenderMethodForWeb imageRenderMethodForWeb; + final ImageRenderMethodForWeb? imageRenderMethodForWeb; /// OptimizedCacheImage shows a network image using a caching mechanism. It also /// provides support for a placeholder, showing an error and fading into the /// loaded image. Next to that it supports most features of a default Image /// widget. OptimizedCacheImage({ - Key key, - @required this.imageUrl, + Key? key, + required this.imageUrl, this.httpHeaders, this.imageBuilder, this.placeholder, @@ -231,16 +231,7 @@ class OptimizedCacheImage extends StatelessWidget { this.maxWidthDiskCache, this.maxHeightDiskCache, this.imageRenderMethodForWeb, - }) : assert(imageUrl != null), - assert(fadeOutDuration != null), - assert(fadeOutCurve != null), - assert(fadeInDuration != null), - assert(fadeInCurve != null), - assert(alignment != null), - assert(filterQuality != null), - assert(repeat != null), - assert(matchTextDirection != null), - super(key: key); + }) : super(key: key); @override Widget build(BuildContext context) { @@ -271,8 +262,8 @@ class OptimizedCacheImage extends StatelessWidget { : null; } if (_image == null || - _image.maxHeight != _constrainHeight || - _image.maxWidth != _constrainHeight) { + _image?.maxHeight != _constrainHeight || + _image?.maxWidth != _constrainHeight) { _image = OptimizedCacheImageProvider( imageUrl, headers: httpHeaders, @@ -284,7 +275,7 @@ class OptimizedCacheImage extends StatelessWidget { ); } return OctoImage( - image: _image, + image: _image!, imageBuilder: imageBuilder != null ? _octoImageBuilder : null, placeholderBuilder: octoPlaceholderBuilder, progressIndicatorBuilder: octoProgressIndicatorBuilder, @@ -296,7 +287,7 @@ class OptimizedCacheImage extends StatelessWidget { width: width, height: height, fit: fit, - alignment: alignment, + alignment: alignment as Alignment?, repeat: repeat, matchTextDirection: matchTextDirection, color: color, @@ -309,32 +300,32 @@ class OptimizedCacheImage extends StatelessWidget { } Widget _octoImageBuilder(BuildContext context, Widget child) { - return imageBuilder(context, _image); + return imageBuilder!(context, _image!); } Widget _octoPlaceholderBuilder(BuildContext context) { - return placeholder(context, imageUrl); + return placeholder!(context, imageUrl); } Widget _octoProgressIndicatorBuilder( BuildContext context, - ImageChunkEvent progress, + ImageChunkEvent? progress, ) { - int totalSize; + int? totalSize; var downloaded = 0; if (progress != null) { totalSize = progress.expectedTotalBytes; downloaded = progress.cumulativeBytesLoaded; } - return progressIndicatorBuilder( + return progressIndicatorBuilder!( context, imageUrl, DownloadProgress(imageUrl, totalSize, downloaded)); } Widget _octoErrorBuilder( BuildContext context, Object error, - StackTrace stackTrace, + StackTrace? stackTrace, ) { - return errorWidget(context, imageUrl, error); + return errorWidget!(context, imageUrl, error); } } diff --git a/lib/src/transformer/image_transformer.dart b/lib/src/transformer/image_transformer.dart index 7c6647c..b1844ad 100644 --- a/lib/src/transformer/image_transformer.dart +++ b/lib/src/transformer/image_transformer.dart @@ -31,12 +31,13 @@ class DefaultImageTransformer extends ImageTransformer { }; @override - Future transform(FileInfo info, int width, int height) async { + Future transform(FileInfo info, int? width, int? height) async { final value = await _scaleImageFile(info, width, height); return value; } - Future _scaleImageFile(FileInfo info, int width, int height) async { + Future _scaleImageFile( + FileInfo info, int? width, int? height) async { FileInfo fileInfo = info; final file = fileInfo.file; log("Scaling file.. ${fileInfo.originalUrl}"); @@ -55,7 +56,7 @@ class DefaultImageTransformer extends ImageTransformer { final localFileSystem = fileIo.LocalFileSystem(); resizedFile = localFileSystem.file(scaleInfo.file.path); - if (resizedFile != null && resizedFile.existsSync()) { + if (resizedFile.existsSync()) { if (resizedFile.lengthSync() < srcSize) { log("Resized success ${fileInfo.originalUrl}"); } else { @@ -74,7 +75,7 @@ class DefaultImageTransformer extends ImageTransformer { } @override - ScaleInfo getScaledFileInfo(File file, int width, int height) { + ScaleInfo getScaledFileInfo(File file, int? width, int? height) { final format = _getCompressionFormat(file); final directory = file.parent; @@ -82,18 +83,18 @@ class DefaultImageTransformer extends ImageTransformer { "/" + p.basenameWithoutExtension(file.path) + sprintf(tmpFileSuffix, [width ?? 1, height ?? 1]) + - _extensionFormats[format]; + _extensionFormats[format]!; final scaleFile = File(destPath); return ScaleInfo(scaleFile, width ?? 1, height ?? 1, format); } CompressFormat _getCompressionFormat(File file) { - String extension = p.extension(file.path) ?? ''; + String extension = p.extension(file.path); return _compressionFormats[extension] ?? CompressFormat.png; } } abstract class ImageTransformer { - Future transform(FileInfo info, int width, int height); + Future transform(FileInfo info, int? width, int? height); ScaleInfo getScaledFileInfo(File file, int width, int height); } diff --git a/pubspec.yaml b/pubspec.yaml index a18377e..660f8b8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,10 +1,10 @@ name: optimized_cached_image description: A library for loading images from network, resizing as per container size and caching while being memory sensitive. -version: 2.0.2-alpha +version: 3.0.0 homepage: https://github.com/humblerookie/optimized_cached_image environment: - sdk: ">=2.6.2 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: flutter: sdk: flutter