diff --git a/CHANGELOG.md b/CHANGELOG.md index 74c6952..0ec33b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [0.3.0-nullsafety.0] - 2021-03-23 +* Migrated to nullsatefy with Dart 2.12 +* Updated compatibility woth Dio 4 interceptors + ## [0.2.11] - 2020-09-24 * Support for custom disk storage. diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 2a56e99..53db008 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,32 +1,30 @@ - + + - + android:name="io.flutter.embedding.android.SplashScreenDrawable" + android:resource="@drawable/launch_background" /> + + - - + + diff --git a/example/android/app/src/main/java/com/example/example/MainActivity.java b/example/android/app/src/main/java/com/example/example/MainActivity.java deleted file mode 100644 index 84f8920..0000000 --- a/example/android/app/src/main/java/com/example/example/MainActivity.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.example.example; - -import android.os.Bundle; -import io.flutter.app.FlutterActivity; -import io.flutter.plugins.GeneratedPluginRegistrant; - -public class MainActivity extends FlutterActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - GeneratedPluginRegistrant.registerWith(this); - } -} diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml index 00fa441..b2c85a4 100644 --- a/example/android/app/src/main/res/values/styles.xml +++ b/example/android/app/src/main/res/values/styles.xml @@ -1,8 +1,8 @@ + diff --git a/example/ios/Flutter/Flutter.podspec b/example/ios/Flutter/Flutter.podspec new file mode 100644 index 0000000..2c4421c --- /dev/null +++ b/example/ios/Flutter/Flutter.podspec @@ -0,0 +1,18 @@ +# +# NOTE: This podspec is NOT to be published. It is only used as a local source! +# This is a generated file; do not edit or check into version control. +# + +Pod::Spec.new do |s| + s.name = 'Flutter' + s.version = '1.0.0' + s.summary = 'High-performance, high-fidelity mobile apps.' + s.homepage = 'https://flutter.io' + s.license = { :type => 'MIT' } + s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } + s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } + s.ios.deployment_target = '8.0' + # Framework linking is handled by Flutter tooling, not CocoaPods. + # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs. + s.vendored_frameworks = 'path/to/nothing' +end diff --git a/example/ios/Podfile b/example/ios/Podfile index d077b08..83bb542 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,4 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +platform :ios, '9.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -10,60 +9,38 @@ 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 - pods_ary = [] - skip_line_start_symbols = ["#", "/"] - File.foreach(file_abs_path) { |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) - pods_ary.push({:name => podname, :path => podpath}); - else - puts "Invalid plugin specification: #{line}" - end - } - return pods_ary + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" end -target 'Runner' do - # 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') +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - # Flutter Pods - generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') - if generated_xcode_build_settings.empty? - puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." - end - generated_xcode_build_settings.map { |p| - if p[:name] == 'FLUTTER_FRAMEWORK_DIR' - symlink = File.join('.symlinks', 'flutter') - File.symlink(File.dirname(p[:path]), symlink) - pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) - end - } +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! - # Plugin Pods - plugin_pods = parse_KV_file('../.flutter-plugins') - plugin_pods.map { |p| - symlink = File.join('.symlinks', 'plugins', p[:name]) - File.symlink(p[:path], symlink) - pod p[:name], :path => File.join(symlink, 'ios') - } + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end post_install do |installer| installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + # Do not build arm64 when building for iOS Simulator, which xcodebuild will attempt from XCode 12 onwards + # https://stackoverflow.com/q/63607158/243165 target.build_configurations.each do |config| - config.build_settings['ENABLE_BITCODE'] = 'NO' + config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64" + config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET' end end end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock new file mode 100644 index 0000000..7b3fd81 --- /dev/null +++ b/example/ios/Podfile.lock @@ -0,0 +1,31 @@ +PODS: + - Flutter (1.0.0) + - FMDB (2.7.5): + - FMDB/standard (= 2.7.5) + - FMDB/standard (2.7.5) + - sqflite (0.0.2): + - Flutter + - FMDB (>= 2.7.5) + +DEPENDENCIES: + - Flutter (from `Flutter`) + - sqflite (from `.symlinks/plugins/sqflite/ios`) + +SPEC REPOS: + trunk: + - FMDB + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + sqflite: + :path: ".symlinks/plugins/sqflite/ios" + +SPEC CHECKSUMS: + Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c + FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + +PODFILE CHECKSUM: 4f075935f8d51cabd6114ffcdb57f90115d955bd + +COCOAPODS: 1.10.1 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index a427f10..b069340 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,16 +9,13 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + CA4C2C696631918E536392D1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD1639E663229C8892201C31 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -28,8 +25,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -39,20 +34,22 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 36BA2DDDDB4578813647A2E5 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; + 4EBC5C03CEC34C3508869607 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 4FD47B8D06E5A66BA1345F59 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AD1639E663229C8892201C31 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -60,20 +57,28 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, + CA4C2C696631918E536392D1 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 897AC2EF7496BBE14CACB1FC /* Pods */ = { + isa = PBXGroup; + children = ( + 36BA2DDDDB4578813647A2E5 /* Pods-Runner.debug.xcconfig */, + 4EBC5C03CEC34C3508869607 /* Pods-Runner.release.xcconfig */, + 4FD47B8D06E5A66BA1345F59 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -87,7 +92,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, + 897AC2EF7496BBE14CACB1FC /* Pods */, + BA668DB416733BED75D9CC3B /* Frameworks */, ); sourceTree = ""; }; @@ -123,6 +129,14 @@ name = "Supporting Files"; sourceTree = ""; }; + BA668DB416733BED75D9CC3B /* Frameworks */ = { + isa = PBXGroup; + children = ( + AD1639E663229C8892201C31 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -130,12 +144,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 3D99DA4A42EA2C2488C3D9C8 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 071B788AA00F0838DA756471 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -194,6 +210,26 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 071B788AA00F0838DA756471 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework", + "${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FMDB.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -206,7 +242,29 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 3D99DA4A42EA2C2488C3D9C8 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; @@ -259,7 +317,6 @@ /* Begin XCBuildConfiguration section */ 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -331,7 +388,6 @@ }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -385,7 +441,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; 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/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/example/lib/dio_helper.dart b/example/lib/dio_helper.dart index e30d082..74c002a 100644 --- a/example/lib/dio_helper.dart +++ b/example/lib/dio_helper.dart @@ -5,8 +5,8 @@ import 'package:dio/dio.dart'; import 'package:dio_http_cache/dio_http_cache.dart'; class DioHelper { - static Dio _dio; - static DioCacheManager _manager; + static Dio? _dio; + static DioCacheManager? _manager; static final baseUrl = "https://www.wanandroid.com/"; static Dio getDio() { @@ -18,7 +18,7 @@ class DioHelper { ..interceptors.add(getCacheManager().interceptor) ..interceptors.add(LogInterceptor(responseBody: true)); } - return _dio; + return _dio!; } static DioCacheManager getCacheManager() { @@ -26,7 +26,7 @@ class DioHelper { _manager = DioCacheManager(CacheConfig(baseUrl: "https://www.wanandroid.com/")); } - return _manager; + return _manager!; } // set proxy diff --git a/example/lib/main.dart b/example/lib/main.dart index 98a9f70..f82d716 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -22,8 +22,8 @@ class MyApp extends StatelessWidget { } class MyHomePage extends StatefulWidget { - MyHomePage({Key key, this.title}) : super(key: key); - final String title; + MyHomePage({Key? key, this.title}) : super(key: key); + final String? title; @override _MyHomePageState createState() => _MyHomePageState(); @@ -38,12 +38,12 @@ class _MyHomePageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text(widget.title), + title: Text(widget.title!), actions: [_buildHomePageActionButtons(context)]), body: getPanel()); } - Widget getPanel() { + Widget? getPanel() { switch (panel) { case Panel.CACHE_MANAGER: return CacheManagerPanel(); @@ -58,7 +58,6 @@ class _MyHomePageState extends State { case Panel.GET_BYTES: return GetBytesPanel(); } - return null; } Widget _buildHomePageActionButtons(BuildContext context) { @@ -72,13 +71,13 @@ class _MyHomePageState extends State { Pair("GET byte array", () => setState(() => panel = Panel.GET_BYTES)), ]; return PopupMenuButton>( - onSelected: (p) => p.i1(), + onSelected: (p) => p.i1!(), child: Padding( padding: EdgeInsets.all(10), child: Icon(Icons.menu, color: Colors.white)), itemBuilder: (BuildContext context) => choices .map((choice) => PopupMenuItem>( - value: choice, child: Text(choice.i0))) + value: choice, child: Text(choice.i0!))) .toList()); } } diff --git a/example/lib/panels/cache_manage.dart b/example/lib/panels/cache_manage.dart index 2fe1366..bd81217 100644 --- a/example/lib/panels/cache_manage.dart +++ b/example/lib/panels/cache_manage.dart @@ -25,13 +25,13 @@ class MyDiskStore implements ICacheStore { } @override - Future delete(String key, {String subKey}) { + Future delete(String key, {String? subKey}) { // TODO: implement delete throw UnimplementedError(); } @override - Future getCacheObj(String key, {String subKey}) { + Future getCacheObj(String key, {String? subKey}) { // TODO: implement getCacheObj throw UnimplementedError(); } @@ -45,7 +45,7 @@ class MyDiskStore implements ICacheStore { class _CacheManagerPanelState extends State { - _Mode _mode = _Mode.clearAll; + _Mode? _mode = _Mode.clearAll; var _url = "article/query/0/json"; var _keyController = TextEditingController(); var _requestMethodController = TextEditingController(); @@ -61,13 +61,13 @@ class _CacheManagerPanelState extends State { Text("Cache Manager", style: Theme.of(context) .textTheme - .title + .headline6! .copyWith(color: Theme.of(context).accentColor)), Container(height: 50), Text("1. Choose mode:", style: Theme.of(context) .textTheme - .subtitle + .subtitle2! .copyWith(color: Theme.of(context).accentColor)), DropdownButton<_Mode>( value: _mode, @@ -91,7 +91,7 @@ class _CacheManagerPanelState extends State { Text("${getLabel()}. to clear", style: Theme.of(context) .textTheme - .subtitle + .subtitle2! .copyWith(color: Theme.of(context).accentColor)), Padding( padding: EdgeInsets.all(10), @@ -99,7 +99,7 @@ class _CacheManagerPanelState extends State { child: Text("Clear", style: Theme.of(context) .textTheme - .subtitle + .subtitle2! .copyWith(color: Colors.white)), onPressed: () => _clear())) ])); @@ -124,7 +124,7 @@ class _CacheManagerPanelState extends State { } void showSnackBar(String msg) { - Scaffold.of(context).showSnackBar(SnackBar(content: Text(msg))); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg))); } List getRequestMethodViews(BuildContext context) { @@ -134,11 +134,11 @@ class _CacheManagerPanelState extends State { Text("2. RequestMethod:", style: Theme.of(context) .textTheme - .subtitle + .subtitle2! .copyWith(color: Theme.of(context).accentColor)), TextField( controller: _requestMethodController, - style: Theme.of(context).textTheme.body2), + style: Theme.of(context).textTheme.bodyText1), Container(height: 20), ]; } @@ -150,10 +150,10 @@ class _CacheManagerPanelState extends State { Text("3. Key:", style: Theme.of(context) .textTheme - .subtitle + .subtitle2! .copyWith(color: Theme.of(context).accentColor)), TextField( - controller: _keyController, style: Theme.of(context).textTheme.body2), + controller: _keyController, style: Theme.of(context).textTheme.bodyText1), Container(height: 20), ]; } @@ -165,11 +165,11 @@ class _CacheManagerPanelState extends State { Text("4. Subkey:", style: Theme.of(context) .textTheme - .subtitle + .subtitle2! .copyWith(color: Theme.of(context).accentColor)), TextField( controller: _subKeyController, - style: Theme.of(context).textTheme.body2), + style: Theme.of(context).textTheme.bodyText1), Container(height: 20), ]; } diff --git a/example/lib/panels/helper.dart b/example/lib/panels/helper.dart index 072fc27..ed0f5a0 100644 --- a/example/lib/panels/helper.dart +++ b/example/lib/panels/helper.dart @@ -8,7 +8,7 @@ import '../dio_helper.dart'; class PanelHelper { static Map getPrintContent( - String key, String subKey, Response response) { + String key, String? subKey, Response response) { var result = { "Cached": "", "Key": @@ -41,9 +41,9 @@ class PanelHelper { static Widget buildNormalPanel( String title, - TextEditingController _urlController, - TextEditingController _paramsController, - Map txt, + TextEditingController? _urlController, + TextEditingController? _paramsController, + Map txt, Function() request) { return Builder( builder: (context) => Padding( @@ -54,18 +54,18 @@ class PanelHelper { Text("NOTE: $title", style: Theme.of(context) .textTheme - .subtitle + .subtitle2! .copyWith(color: Theme.of(context).primaryColor)), Container(height: 20), Text("Base url:", style: Theme.of(context) .textTheme - .subtitle + .subtitle2! .copyWith(color: Theme.of(context).accentColor)), Text(DioHelper.baseUrl, style: Theme.of(context) .textTheme - .body1 + .bodyText2! .copyWith(color: Colors.grey)), for (var w in _buildInput( context, @@ -86,34 +86,34 @@ class PanelHelper { } static List _buildConsoleOutput( - BuildContext context, Map map) { + BuildContext context, Map map) { List widgets = []; map.forEach((k, v) { widgets.add(TextSpan( text: "$k: ", style: Theme.of(context) .textTheme - .subtitle + .subtitle2! .copyWith(color: Colors.teal))); widgets.add(TextSpan( text: "$v\n\n", style: Theme.of(context) .textTheme - .body1 + .bodyText2! .copyWith(color: Theme.of(context).disabledColor))); }); return widgets; } static List _buildInput(BuildContext context, - TextEditingController controller, String title, Function() request) { + TextEditingController? controller, String title, Function()? request) { if (null == controller) return []; return [ Container(height: 20), Text("$title:", style: Theme.of(context) .textTheme - .subtitle + .subtitle2! .copyWith(color: Theme.of(context).accentColor)), Row(children: [ Expanded(child: TextField(controller: controller)), @@ -123,7 +123,7 @@ class PanelHelper { } static List _buildRequestButton( - BuildContext context, Function() request) { + BuildContext context, Function()? request) { if (null != request) return [ Container(width: 10), @@ -131,7 +131,7 @@ class PanelHelper { child: Text("GO", style: Theme.of(context) .textTheme - .subtitle + .subtitle2! .copyWith(color: Colors.white)), onPressed: () => request()) ]; diff --git a/example/lib/panels/panel_post.dart b/example/lib/panels/panel_post.dart index 925825f..b3ec198 100644 --- a/example/lib/panels/panel_post.dart +++ b/example/lib/panels/panel_post.dart @@ -10,7 +10,7 @@ class PostPanel extends StatefulWidget { } class _PostPanelState extends State { - Map _content = {"Hello ~": ""}; + Map _content = {"Hello ~": ""}; var _url = "article/query/0/json"; var _paramsController; var _urlController; diff --git a/example/lib/tuple.dart b/example/lib/tuple.dart index bf928e6..69f2c96 100644 --- a/example/lib/tuple.dart +++ b/example/lib/tuple.dart @@ -1,10 +1,10 @@ /// Represents a 2-tuple or pair. class Pair { /// First item of the tuple - T0 i0; + T0? i0; /// Second item of the tuple - T1 i1; + T1? i1; /// Create a new tuple value with the specified items. Pair([this.i0, this.i1]); @@ -22,9 +22,9 @@ class Pair { /// Represents a 3-tuple or pair. class Triple { - T0 i0; - T1 i1; - T2 i2; + T0? i0; + T1? i1; + T2? i2; Triple([this.i0, this.i1, this.i2]); diff --git a/example/pubspec.yaml b/example/pubspec.yaml index ae515ff..de6050c 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,28 +1,14 @@ -name: DioHttpCacheExample -description: A new Flutter project. - -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +name: dio_http_cache_example +description: A demo of the dio_http_cache package. version: 1.0.0+1 +publish_to: none environment: - sdk: ">=2.5.2 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: flutter: sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.2 dio_http_cache: path: ../ @@ -30,45 +16,5 @@ dev_dependencies: flutter_test: sdk: flutter - -# For information on the generic Dart part of this file, see the -# following page: https://www.dartlang.org/tools/pub/pubspec - -# The following section is specific to Flutter. 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 - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # 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/custom-fonts/#from-packages diff --git a/lib/src/builder_dio.dart b/lib/src/builder_dio.dart index 976b2fb..e62885a 100644 --- a/lib/src/builder_dio.dart +++ b/lib/src/builder_dio.dart @@ -4,11 +4,11 @@ import 'package:dio_http_cache/src/manager_dio.dart'; /// try to get maxAge and maxStale from response headers. /// local settings will always overview the value get from service. Options buildServiceCacheOptions( - {Options options, - Duration maxStale, - String primaryKey, - String subKey, - bool forceRefresh}) => + {Options? options, + Duration? maxStale, + String? primaryKey, + String? subKey, + bool? forceRefresh}) => buildConfigurableCacheOptions( options: options, maxStale: maxStale, @@ -18,11 +18,11 @@ Options buildServiceCacheOptions( /// build a normal cache options Options buildCacheOptions(Duration maxAge, - {Duration maxStale, - String primaryKey, - String subKey, - Options options, - bool forceRefresh}) => + {Duration? maxStale, + String? primaryKey, + String? subKey, + Options? options, + bool? forceRefresh}) => buildConfigurableCacheOptions( maxAge: maxAge, options: options, @@ -34,32 +34,35 @@ Options buildCacheOptions(Duration maxAge, /// if null==maxAge, will try to get maxAge and maxStale from response headers. /// local settings will always overview the value get from service. Options buildConfigurableCacheOptions( - {Options options, - Duration maxAge, - Duration maxStale, - String primaryKey, - String subKey, - bool forceRefresh}) { + {Options? options, + Duration? maxAge, + Duration? maxStale, + String? primaryKey, + String? subKey, + bool? forceRefresh}) { if (null == options) { options = Options(); + options.extra = {}; } else if (options.responseType == ResponseType.stream) { throw Exception("ResponseType.stream is not supported"); + } else if (options.extra == null) { + options.extra = {}; } - options.extra.addAll({DIO_CACHE_KEY_TRY_CACHE: true}); + options.extra!.addAll({DIO_CACHE_KEY_TRY_CACHE: true}); if (null != maxAge) { - options.extra.addAll({DIO_CACHE_KEY_MAX_AGE: maxAge}); + options.extra!.addAll({DIO_CACHE_KEY_MAX_AGE: maxAge}); } if (null != maxStale) { - options.extra.addAll({DIO_CACHE_KEY_MAX_STALE: maxStale}); + options.extra!.addAll({DIO_CACHE_KEY_MAX_STALE: maxStale}); } if (null != primaryKey) { - options.extra.addAll({DIO_CACHE_KEY_PRIMARY_KEY: primaryKey}); + options.extra!.addAll({DIO_CACHE_KEY_PRIMARY_KEY: primaryKey}); } if (null != subKey) { - options.extra.addAll({DIO_CACHE_KEY_SUB_KEY: subKey}); + options.extra!.addAll({DIO_CACHE_KEY_SUB_KEY: subKey}); } if (null != forceRefresh) { - options.extra.addAll({DIO_CACHE_KEY_FORCE_REFRESH: forceRefresh}); + options.extra!.addAll({DIO_CACHE_KEY_FORCE_REFRESH: forceRefresh}); } return options; } diff --git a/lib/src/core/config.dart b/lib/src/core/config.dart index a4a87cb..706d7bb 100644 --- a/lib/src/core/config.dart +++ b/lib/src/core/config.dart @@ -5,10 +5,10 @@ typedef Future> Decrypt(List str); class CacheConfig { final Duration defaultMaxAge; - final Duration defaultMaxStale; - final String databasePath; + final Duration? defaultMaxStale; + final String? databasePath; final String databaseName; - final String baseUrl; + final String? baseUrl; final String defaultRequestMethod; final bool skipMemoryCache; @@ -16,9 +16,9 @@ class CacheConfig { final int maxMemoryCacheCount; - final Encrypt encrypt; - final Decrypt decrypt; - final ICacheStore diskStore; + final Encrypt? encrypt; + final Decrypt? decrypt; + final ICacheStore? diskStore; CacheConfig( {this.defaultMaxAge = const Duration(days: 7), diff --git a/lib/src/core/manager.dart b/lib/src/core/manager.dart index bd1d4e1..f512149 100644 --- a/lib/src/core/manager.dart +++ b/lib/src/core/manager.dart @@ -10,13 +10,11 @@ import 'package:sqflite/utils/utils.dart'; class CacheManager { CacheConfig _config; - ICacheStore _diskCacheStore; - ICacheStore _memoryCacheStore; - MD5 _md5; - Utf8Encoder _utf8encoder; + ICacheStore? _diskCacheStore; + ICacheStore? _memoryCacheStore; + late Utf8Encoder _utf8encoder; CacheManager(this._config) { - _md5 = md5; _utf8encoder = const Utf8Encoder(); if (!_config.skipDiskCache) _diskCacheStore = _config.diskStore ?? @@ -26,7 +24,7 @@ class CacheManager { _memoryCacheStore = MemoryCacheStore(_config.maxMemoryCacheCount); } - Future _pullFromCache(String key, {String subKey}) async { + Future _pullFromCache(String key, {String? subKey}) async { key = _convertMd5(key); if (null != subKey) subKey = _convertMd5(subKey); var obj = await _memoryCacheStore?.getCacheObj(key, subKey: subKey); @@ -36,15 +34,15 @@ class CacheManager { } if (null != obj) { var now = DateTime.now().millisecondsSinceEpoch; - if (null != obj.maxStaleDate && obj.maxStaleDate > 0) { + if (null != obj.maxStaleDate && obj.maxStaleDate! > 0) { //if maxStaleDate exist, Remove it if maxStaleDate expired. - if (obj.maxStaleDate < now) { + if (obj.maxStaleDate! < now) { await delete(key, subKey: subKey); return null; } } else { //if maxStaleDate NOT exist, Remove it if maxAgeDate expired. - if (obj.maxAgeDate < now) { + if (obj.maxAgeDate! < now) { await delete(key, subKey: subKey); return null; } @@ -53,33 +51,33 @@ class CacheManager { return obj; } - Future pullFromCacheBeforeMaxAge(String key, - {String subKey}) async { + Future pullFromCacheBeforeMaxAge(String key, + {String? subKey}) async { var obj = await _pullFromCache(key, subKey: subKey); if (null != obj && null != obj.maxAgeDate && - obj.maxAgeDate < DateTime.now().millisecondsSinceEpoch) { + obj.maxAgeDate! < DateTime.now().millisecondsSinceEpoch) { return null; } return obj; } - Future pullFromCacheBeforeMaxStale(String key, - {String subKey}) async { + Future pullFromCacheBeforeMaxStale(String key, + {String? subKey}) async { return await _pullFromCache(key, subKey: subKey); } Future pushToCache(CacheObj obj) { obj.key = _convertMd5(obj.key); - if (null != obj.subKey) obj.subKey = _convertMd5(obj.subKey); + if (null != obj.subKey) obj.subKey = _convertMd5(obj.subKey!); - if (null == obj.maxAgeDate || obj.maxAgeDate <= 0) { + if (null == obj.maxAgeDate || obj.maxAgeDate! <= 0) { obj.maxAge = _config.defaultMaxAge; } - if (null == obj.maxAgeDate || obj.maxAgeDate <= 0) { + if (null == obj.maxAgeDate || obj.maxAgeDate! <= 0) { return Future.value(false); } - if ((null == obj.maxStaleDate || obj.maxStaleDate <= 0) && + if ((null == obj.maxStaleDate || obj.maxStaleDate! <= 0) && null != _config.defaultMaxStale) { obj.maxStale = _config.defaultMaxStale; } @@ -88,7 +86,7 @@ class CacheManager { _memoryCacheStore?.setCacheObj(obj), _diskCacheStore?.setCacheObj(obj)); } - Future delete(String key, {String subKey}) { + Future delete(String key, {String? subKey}) { key = _convertMd5(key); if (null != subKey) subKey = _convertMd5(subKey); @@ -110,16 +108,16 @@ class CacheManager { } String _convertMd5(String str) { - return hex(_md5.convert(_utf8encoder.convert(str)).bytes); + return hex(md5.convert(_utf8encoder.convert(str)).bytes); } Future _getCacheFutureResult( - ICacheStore memoryCacheStore, - ICacheStore diskCacheStore, - Future memoryCacheFuture, - Future diskCacheFuture) async { - var result1 = (null == memoryCacheStore) ? true : (await memoryCacheFuture); - var result2 = (null == diskCacheStore) ? true : (await diskCacheFuture); + ICacheStore? memoryCacheStore, + ICacheStore? diskCacheStore, + Future? memoryCacheFuture, + Future? diskCacheFuture) async { + var result1 = (null == memoryCacheStore) ? true : (await memoryCacheFuture!); + var result2 = (null == diskCacheStore) ? true : (await diskCacheFuture!); return result1 && result2; } } diff --git a/lib/src/core/obj.dart b/lib/src/core/obj.dart index 6527f89..184a2d0 100644 --- a/lib/src/core/obj.dart +++ b/lib/src/core/obj.dart @@ -5,34 +5,34 @@ part 'obj.g.dart'; @JsonSerializable() class CacheObj { String key; - String subKey; + String? subKey; @JsonKey(name: "max_age_date") - int maxAgeDate; + int? maxAgeDate; @JsonKey(name: "max_stale_date") - int maxStaleDate; - List content; - int statusCode; - List headers; + int? maxStaleDate; + List? content; + int? statusCode; + List? headers; CacheObj._( this.key, this.subKey, this.content, this.statusCode, this.headers); factory CacheObj(String key, List content, - {String subKey = "", - Duration maxAge, - Duration maxStale, - int statusCode = 200, - List headers}) { + {String? subKey = "", + Duration? maxAge, + Duration? maxStale, + int? statusCode = 200, + List? headers}) { return CacheObj._(key, subKey, content, statusCode, headers) ..maxAge = maxAge ..maxStale = maxStale; } - set maxAge(Duration duration) { + set maxAge(Duration? duration) { if (null != duration) this.maxAgeDate = _convertDuration(duration); } - set maxStale(Duration duration) { + set maxStale(Duration? duration) { if (null != duration) this.maxStaleDate = _convertDuration(duration); } diff --git a/lib/src/core/obj.g.dart b/lib/src/core/obj.g.dart index 74a2ae4..dea7ea8 100644 --- a/lib/src/core/obj.g.dart +++ b/lib/src/core/obj.g.dart @@ -6,16 +6,16 @@ part of 'obj.dart'; // JsonSerializableGenerator // ************************************************************************** -CacheObj _$CacheObjFromJson(Map json) { +CacheObj _$CacheObjFromJson(Map json) { return CacheObj( json['key'] as String, - (json['content'] as List)?.map((e) => e as int)?.toList(), - subKey: json['subKey'] as String, - statusCode: json['statusCode'] as int, - headers: (json['headers'] as List)?.map((e) => e as int)?.toList(), + (json['content'] as List).map((e) => e as int).toList(), + subKey: json['subKey'] as String?, + statusCode: json['statusCode'] as int?, + headers: (json['headers'] as List?)?.map((e) => e as int).toList(), ) - ..maxAgeDate = json['max_age_date'] as int - ..maxStaleDate = json['max_stale_date'] as int; + ..maxAgeDate = json['max_age_date'] as int? + ..maxStaleDate = json['max_stale_date'] as int?; } Map _$CacheObjToJson(CacheObj instance) => { diff --git a/lib/src/manager_dio.dart b/lib/src/manager_dio.dart index 33e8af7..3645f86 100644 --- a/lib/src/manager_dio.dart +++ b/lib/src/manager_dio.dart @@ -15,13 +15,13 @@ const DIO_CACHE_KEY_FORCE_REFRESH = "dio_cache_force_refresh"; const DIO_CACHE_HEADER_KEY_DATA_SOURCE = "dio_cache_header_key_data_source"; typedef _ParseHeadCallback = void Function( - Duration _maxAge, Duration _maxStale); + Duration? _maxAge, Duration? _maxStale); class DioCacheManager { - CacheManager _manager; - InterceptorsWrapper _interceptor; - String _baseUrl; - String _defaultRequestMethod; + late CacheManager _manager; + InterceptorsWrapper? _interceptor; + late String? _baseUrl; + late String _defaultRequestMethod; DioCacheManager(CacheConfig config) { _manager = CacheManager(config); @@ -38,51 +38,55 @@ class DioCacheManager { return _interceptor; } - _onRequest(RequestOptions options) async { + _onRequest(RequestOptions options, RequestInterceptorHandler handler) async { if ((options.extra[DIO_CACHE_KEY_TRY_CACHE] ?? false) != true) { - return options; + return handler.next(options); } if (true == options.extra[DIO_CACHE_KEY_FORCE_REFRESH]) { - return options; + return handler.next(options); } var responseDataFromCache = await _pullFromCacheBeforeMaxAge(options); if (null != responseDataFromCache) { - return _buildResponse( - responseDataFromCache, responseDataFromCache?.statusCode, options); + return handler.resolve(_buildResponse( + responseDataFromCache, responseDataFromCache.statusCode, options), true); } - return options; + return handler.next(options); } - _onResponse(Response response) async { - if ((response.request.extra[DIO_CACHE_KEY_TRY_CACHE] ?? false) == true && - response.statusCode >= 200 && - response.statusCode < 300) { + _onResponse(Response response, ResponseInterceptorHandler handler) async { + if ((response.requestOptions.extra[DIO_CACHE_KEY_TRY_CACHE] ?? false) == true && + response.statusCode != null && + response.statusCode! >= 200 && + response.statusCode! < 300) { await _pushToCache(response); } - return response; + return handler.next(response); } - _onError(DioError e) async { - if ((e.request.extra[DIO_CACHE_KEY_TRY_CACHE] ?? false) == true) { - var responseDataFromCache = await _pullFromCacheBeforeMaxStale(e.request); - if (null != responseDataFromCache) - return _buildResponse(responseDataFromCache, - responseDataFromCache?.statusCode, e.request); + _onError(DioError e, ErrorInterceptorHandler handler) async { + if ((e.requestOptions.extra[DIO_CACHE_KEY_TRY_CACHE] ?? false) == true) { + var responseDataFromCache = await _pullFromCacheBeforeMaxStale(e.requestOptions); + if (null != responseDataFromCache) { + var response = _buildResponse(responseDataFromCache, + responseDataFromCache.statusCode, e.requestOptions); + + return handler.resolve(response); + } } - return e; + return handler.next(e); } Response _buildResponse( - CacheObj obj, int statusCode, RequestOptions options) { - Headers headers; + CacheObj obj, int? statusCode, RequestOptions options) { + Headers? headers; if (null != obj.headers) { headers = Headers.fromMap((Map>.from( - jsonDecode(utf8.decode(obj.headers)))) + jsonDecode(utf8.decode(obj.headers!)))) .map((k, v) => MapEntry(k, List.from(v)))); } if (null == headers) { headers = Headers(); - options.headers.forEach((k, v) => headers.add(k, v ?? "")); + options.headers.forEach((k, v) => headers!.add(k, v ?? "")); } // add flag headers.add(DIO_CACHE_HEADER_KEY_DATA_SOURCE, "from_cache"); @@ -93,77 +97,75 @@ class DioCacheManager { return Response( data: data, headers: headers, - extra: options.extra..remove(DIO_CACHE_KEY_TRY_CACHE), + requestOptions: options.copyWith(extra: options.extra..remove(DIO_CACHE_KEY_TRY_CACHE)), statusCode: statusCode ?? 200); } - Future _pullFromCacheBeforeMaxAge(RequestOptions options) { - return _manager?.pullFromCacheBeforeMaxAge( + Future _pullFromCacheBeforeMaxAge(RequestOptions options) { + return _manager.pullFromCacheBeforeMaxAge( _getPrimaryKeyFromOptions(options), subKey: _getSubKeyFromOptions(options)); } - Future _pullFromCacheBeforeMaxStale(RequestOptions options) { - return _manager?.pullFromCacheBeforeMaxStale( + Future _pullFromCacheBeforeMaxStale(RequestOptions options) { + return _manager.pullFromCacheBeforeMaxStale( _getPrimaryKeyFromOptions(options), subKey: _getSubKeyFromOptions(options)); } Future _pushToCache(Response response) { - RequestOptions options = response.request; - Duration maxAge = options.extra[DIO_CACHE_KEY_MAX_AGE]; - Duration maxStale = options.extra[DIO_CACHE_KEY_MAX_STALE]; + RequestOptions options = response.requestOptions; + Duration? maxAge = options.extra[DIO_CACHE_KEY_MAX_AGE]; + Duration? maxStale = options.extra[DIO_CACHE_KEY_MAX_STALE]; if (null == maxAge) { _tryParseHead(response, maxStale, (_maxAge, _maxStale) { maxAge = _maxAge; maxStale = _maxStale; }); } - List data; + List? data; if (options.responseType == ResponseType.bytes) { data = response.data; } else { data = utf8.encode(jsonEncode(response.data)); } - var obj = CacheObj(_getPrimaryKeyFromOptions(options), data, + var obj = CacheObj(_getPrimaryKeyFromOptions(options), data!, subKey: _getSubKeyFromOptions(options), maxAge: maxAge, maxStale: maxStale, statusCode: response.statusCode, headers: utf8.encode(jsonEncode(response.headers.map))); - return _manager?.pushToCache(obj); + return _manager.pushToCache(obj); } // try to get maxAge and maxStale from http headers void _tryParseHead( - Response response, Duration maxStale, _ParseHeadCallback callback) { - Duration _maxAge; + Response response, Duration? maxStale, _ParseHeadCallback callback) { + Duration? _maxAge; var cacheControl = response.headers.value(HttpHeaders.cacheControlHeader); if (null != cacheControl) { // try to get maxAge and maxStale from cacheControl - var parameters; + Map parameters; try { - parameters = HeaderValue.parse( - "${HttpHeaders.cacheControlHeader}: $cacheControl", - parameterSeparator: ",", - valueSeparator: "=") + parameters = HeaderValue.parse("${HttpHeaders.cacheControlHeader}: $cacheControl", + parameterSeparator: ",", valueSeparator: "=") .parameters; + _maxAge = _tryGetDurationFromMap(parameters, "s-maxage"); + if (null == _maxAge) { + _maxAge = _tryGetDurationFromMap(parameters, "max-age"); + } + // if maxStale has valued, don't get max-stale anymore. + if (null == maxStale) { + maxStale = _tryGetDurationFromMap(parameters, "max-stale"); + } } catch (e) { print(e); } - _maxAge = _tryGetDurationFromMap(parameters, "s-maxage"); - if (null == _maxAge) { - _maxAge = _tryGetDurationFromMap(parameters, "max-age"); - } - // if maxStale has valued, don't get max-stale anymore. - if (null == maxStale) { - maxStale = _tryGetDurationFromMap(parameters, "max-stale"); - } } else { // try to get maxAge from expires var expires = response.headers.value(HttpHeaders.expiresHeader); if (null != expires && expires.length > 4) { - DateTime endTime; + DateTime? endTime; try { endTime = HttpDate.parse(expires).toLocal(); } catch (e) { @@ -177,9 +179,9 @@ class DioCacheManager { callback(_maxAge, maxStale); } - Duration _tryGetDurationFromMap(Map parameters, String key) { - if (null != parameters && parameters.containsKey(key)) { - var value = int.tryParse(parameters[key]); + Duration? _tryGetDurationFromMap(Map parameters, String key) { + if (parameters.containsKey(key)) { + var value = int.tryParse(parameters[key]!); if (null != value && value >= 0) { return Duration(seconds: value); } @@ -195,52 +197,49 @@ class DioCacheManager { return "${_getRequestMethod(options.method)}-$primaryKey"; } - String _getRequestMethod(String requestMethod) { + String _getRequestMethod(String? requestMethod) { if (null != requestMethod && requestMethod.length > 0) { return requestMethod.toUpperCase(); } - if (null != _defaultRequestMethod && _defaultRequestMethod.length > 0) { - return _defaultRequestMethod.toUpperCase(); - } - return "DEFAULT_METHOD"; + return _defaultRequestMethod.toUpperCase(); } - String _getSubKeyFromOptions(RequestOptions options) { + String? _getSubKeyFromOptions(RequestOptions options) { return options.extra.containsKey(DIO_CACHE_KEY_SUB_KEY) ? options.extra[DIO_CACHE_KEY_SUB_KEY] : _getSubKeyFromUri(options.uri, data: options.data); } - String _getPrimaryKeyFromUri(Uri uri) => "${uri?.host}${uri?.path}"; + String _getPrimaryKeyFromUri(Uri uri) => "${uri.host}${uri.path}"; String _getSubKeyFromUri(Uri uri, {dynamic data}) => - "${data?.toString()}_${uri?.query}"; + "${data?.toString()}_${uri.query}"; /// delete local cache by primaryKey and optional subKey Future delete(String primaryKey, - {String requestMethod, String subKey}) => - _manager?.delete("${_getRequestMethod(requestMethod)}-$primaryKey", + {String? requestMethod, String? subKey}) => + _manager.delete("${_getRequestMethod(requestMethod)}-$primaryKey", subKey: subKey); /// no matter what subKey is, delete local cache if primary matched. - Future deleteByPrimaryKeyWithUri(Uri uri, {String requestMethod}) => + Future deleteByPrimaryKeyWithUri(Uri uri, {String? requestMethod}) => delete(_getPrimaryKeyFromUri(uri), requestMethod: requestMethod); - Future deleteByPrimaryKey(String path, {String requestMethod}) => + Future deleteByPrimaryKey(String path, {String? requestMethod}) => deleteByPrimaryKeyWithUri(_getUriByPath(_baseUrl, path), requestMethod: requestMethod); /// delete local cache when both primaryKey and subKey matched. Future deleteByPrimaryKeyAndSubKeyWithUri(Uri uri, - {String requestMethod, String subKey, dynamic data}) => + {String? requestMethod, String? subKey, dynamic data}) => delete(_getPrimaryKeyFromUri(uri), requestMethod: requestMethod, subKey: subKey ?? _getSubKeyFromUri(uri, data: data)); Future deleteByPrimaryKeyAndSubKey(String path, - {String requestMethod, - Map queryParameters, - String subKey, + {String? requestMethod, + Map? queryParameters, + String? subKey, dynamic data}) => deleteByPrimaryKeyAndSubKeyWithUri( _getUriByPath(_baseUrl, path, @@ -250,15 +249,15 @@ class DioCacheManager { data: data); /// clear all expired cache. - Future clearExpired() => _manager?.clearExpired(); + Future clearExpired() => _manager.clearExpired(); /// empty local cache. - Future clearAll() => _manager?.clearAll(); + Future clearAll() => _manager.clearAll(); - Uri _getUriByPath(String baseUrl, String path, - {dynamic data, Map queryParameters}) { + Uri _getUriByPath(String? baseUrl, String path, + {dynamic data, Map? queryParameters}) { if (!path.startsWith(RegExp(r"https?:"))) { - assert(null != baseUrl && baseUrl.length > 0); + assert(baseUrl != null && baseUrl.length > 0); } return RequestOptions( baseUrl: baseUrl, diff --git a/lib/src/store/store_disk.dart b/lib/src/store/store_disk.dart index d81ea53..67d8248 100644 --- a/lib/src/store/store_disk.dart +++ b/lib/src/store/store_disk.dart @@ -7,10 +7,10 @@ import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; class DiskCacheStore extends ICacheStore { - final String _databasePath; + final String? _databasePath; final String _databaseName; - final Encrypt _encrypt; - final Decrypt _decrypt; + final Encrypt? _encrypt; + final Decrypt? _decrypt; final String _tableCacheObject = "cache_dio"; final String _columnKey = "key"; final String _columnSubKey = "subKey"; @@ -20,10 +20,10 @@ class DiskCacheStore extends ICacheStore { final String _columnStatusCode = "statusCode"; final String _columnHeaders = "headers"; - Database _db; + Database? _db; static const int _curDBVersion = 3; - Future get _database async { + Future get _database async { if (null == _db) { var path = _databasePath; if (null == path || path.length <= 0) { @@ -33,7 +33,7 @@ class DiskCacheStore extends ICacheStore { path = join(path, "$_databaseName.db"); _db = await openDatabase(path, version: _curDBVersion, - onConfigure: (db) => _tryFixDbNoVersionBug(db, path), + onConfigure: (db) => _tryFixDbNoVersionBug(db, path!), onCreate: _onCreate, onUpgrade: _onUpgrade); await _clearExpired(_db); @@ -46,7 +46,7 @@ class DiskCacheStore extends ICacheStore { var isTableUserLogExist = await db .rawQuery( "select DISTINCT tbl_name from sqlite_master where tbl_name = '$_tableCacheObject'") - .then((v) => (null != v && v.length > 0)); + .then((v) => (v.length > 0)); if (isTableUserLogExist) { await db.setVersion(1); } @@ -70,7 +70,7 @@ class DiskCacheStore extends ICacheStore { await db.execute(_getCreateTableSql()); } - List> _dbUpgradeList() => [ + List?> _dbUpgradeList() => [ // 0 -> 1 null, // 1 -> 2 @@ -92,7 +92,7 @@ class DiskCacheStore extends ICacheStore { if (null != sqlList && sqlList.length > 0) { sqlList.forEach((sql) async { sql = sql.trim(); - if (null != sql && sql.length > 0) { + if (sql.length > 0) { await txn.execute(sql); } }); @@ -108,13 +108,13 @@ class DiskCacheStore extends ICacheStore { : super(); @override - Future getCacheObj(String key, {String subKey}) async { + Future getCacheObj(String key, {String? subKey}) async { var db = await _database; if (null == db) return null; var where = "$_columnKey=\"$key\""; if (null != subKey) where += " and $_columnSubKey=\"$subKey\""; var resultList = await db.query(_tableCacheObject, where: where); - if (null == resultList || resultList.length <= 0) return null; + if (resultList.isEmpty) return null; return await _decryptCacheObj(CacheObj.fromJson(resultList[0])); } @@ -140,7 +140,7 @@ class DiskCacheStore extends ICacheStore { } @override - Future delete(String key, {String subKey}) async { + Future delete(String key, {String? subKey}) async { var db = await _database; if (null == db) return false; var where = "$_columnKey=\"$key\""; @@ -154,7 +154,7 @@ class DiskCacheStore extends ICacheStore { return _clearExpired(db); } - Future _clearExpired(Database db) async { + Future _clearExpired(Database? db) async { if (null == db) return false; var now = DateTime.now().millisecondsSinceEpoch; var where1 = "$_columnMaxStaleDate > 0 and $_columnMaxStaleDate < $now"; @@ -176,18 +176,18 @@ class DiskCacheStore extends ICacheStore { return obj; } - Future> _decryptCacheStr(List bytes) async { + Future?> _decryptCacheStr(List? bytes) async { if (null == bytes) return null; if (null != _decrypt) { - bytes = await _decrypt(bytes); + bytes = await _decrypt!(bytes); } return bytes; } - Future> _encryptCacheStr(List bytes) async { + Future?> _encryptCacheStr(List? bytes) async { if (null == bytes) return null; if (null != _encrypt) { - bytes = await _encrypt(bytes); + bytes = await _encrypt!(bytes); } return bytes; } diff --git a/lib/src/store/store_impl.dart b/lib/src/store/store_impl.dart index 35c7e2f..d3e29bd 100644 --- a/lib/src/store/store_impl.dart +++ b/lib/src/store/store_impl.dart @@ -3,11 +3,11 @@ import 'package:dio_http_cache/src/core/obj.dart'; abstract class ICacheStore { ICacheStore(); - Future getCacheObj(String key, {String subKey}); + Future getCacheObj(String key, {String? subKey}); Future setCacheObj(CacheObj obj); - Future delete(String key, {String subKey}); + Future delete(String key, {String? subKey}); Future clearExpired(); diff --git a/lib/src/store/store_memory.dart b/lib/src/store/store_memory.dart index cb353b0..ca958cf 100644 --- a/lib/src/store/store_memory.dart +++ b/lib/src/store/store_memory.dart @@ -6,8 +6,8 @@ import 'package:quiver/cache.dart'; class MemoryCacheStore extends ICacheStore { final int _maxMemoryCacheCount; - MapCache _mapCache; - Map> _keys; + late MapCache _mapCache; + late Map> _keys; MemoryCacheStore(this._maxMemoryCacheCount) : super() { _initMap(); @@ -19,7 +19,7 @@ class MemoryCacheStore extends ICacheStore { } @override - Future getCacheObj(String key, {String subKey = ""}) async => + Future getCacheObj(String key, {String? subKey = ""}) async => _mapCache.get("${key}_$subKey"); @override @@ -30,8 +30,7 @@ class MemoryCacheStore extends ICacheStore { } @override - Future delete(String key, {String subKey}) async { -// _mapCache.invalidate("${key}_${subKey ?? ""}"); + Future delete(String key, {String? subKey}) async { _removeKey(key, subKey: subKey).forEach((key) => _mapCache.invalidate(key)); return true; } @@ -43,21 +42,19 @@ class MemoryCacheStore extends ICacheStore { @override Future clearAll() async { - _mapCache = null; - _keys = null; _initMap(); return true; } _storeKey(CacheObj obj) { - List subKeyList = _keys[obj.key]; - if (null == subKeyList) subKeyList = List(); + List? subKeyList = _keys[obj.key]; + if (null == subKeyList) subKeyList = []; subKeyList.add(obj.subKey ?? ""); _keys[obj.key] = subKeyList; } - List _removeKey(String key, {String subKey}) { - List subKeyList = _keys[key]; + List _removeKey(String key, {String? subKey}) { + List? subKeyList = _keys[key]; if (null == subKeyList || subKeyList.length <= 0) return []; if (null == subKey) { _keys.remove(key); diff --git a/pubspec.yaml b/pubspec.yaml index ce92ccf..9fa5126 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,28 +1,28 @@ name: dio_http_cache description: http cache lib for Flutter dio like RxCache.It use sqflite as disk cache,and google/quiver-dart/LRU strategy as memory cache. -version: 0.2.11 +version: 0.3.0-nullsafety.0 authors: - Hurshi + - Eric Kok homepage: https://github.com/hurshi/dio-http-cache environment: - sdk: ">=2.1.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: + crypto: ^3.0.0 + dio: ^4.0.0 flutter: sdk: flutter - quiver: ^2.0.3 - json_serializable: ^3.0.0 - json_annotation: ^3.0.0 - dio: ^3.0.1 - sqflite: ^1.1.6+3 - path: ^1.6.2 - crypto: ^2.1.1+1 + json_serializable: ^4.0.3 + path: ^1.8.0 + quiver: ^3.0.0 + sqflite: ^2.0.0+3 dev_dependencies: + build_runner: ^1.12.2 flutter_test: sdk: flutter - build_runner: ^1.6.6 flutter: module: