diff --git a/assets/error.webp b/assets/error.webp
new file mode 100644
index 0000000..e33673c
Binary files /dev/null and b/assets/error.webp differ
diff --git a/example/assets/error.webp b/example/assets/error.webp
new file mode 100644
index 0000000..e33673c
Binary files /dev/null and b/example/assets/error.webp differ
diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist
index 6b4c0f7..f2872cf 100644
--- a/example/ios/Flutter/AppFrameworkInfo.plist
+++ b/example/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
CFBundleVersion
1.0
MinimumOSVersion
- 8.0
+ 9.0
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
new file mode 100644
index 0000000..0abd365
--- /dev/null
+++ b/example/ios/Podfile.lock
@@ -0,0 +1,28 @@
+PODS:
+ - Flutter (1.0.0)
+ - image_picker (0.0.1):
+ - Flutter
+ - motion_sensors (0.0.1):
+ - Flutter
+
+DEPENDENCIES:
+ - Flutter (from `Flutter`)
+ - image_picker (from `.symlinks/plugins/image_picker/ios`)
+ - motion_sensors (from `.symlinks/plugins/motion_sensors/ios`)
+
+EXTERNAL SOURCES:
+ Flutter:
+ :path: Flutter
+ image_picker:
+ :path: ".symlinks/plugins/image_picker/ios"
+ motion_sensors:
+ :path: ".symlinks/plugins/motion_sensors/ios"
+
+SPEC CHECKSUMS:
+ Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
+ image_picker: 50e7c7ff960e5f58faa4d1f4af84a771c671bc4a
+ motion_sensors: 03f55b7c637a7e365a0b5f9697a449f9059d5d91
+
+PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
+
+COCOAPODS: 1.11.3
diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj
index 0d45b98..c009112 100644
--- a/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/example/ios/Runner.xcodeproj/project.pbxproj
@@ -3,20 +3,17 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 46;
+ objectVersion = 50;
objects = {
/* 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, ); }; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
- 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, ); }; };
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 */; };
+ F5B2B2DDBE5A1EB79AED33C5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE12CC196C45E9329399C340 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -26,8 +23,6 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
- 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
- 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
@@ -38,18 +33,20 @@
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 = ""; };
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 = ""; };
+ 4B00D748B979A95B621AF051 /* 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 = ""; };
+ 52097B044775CCA165321119 /* 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 = ""; };
+ 5DE967DE2D339C44F7F49D7D /* 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 = ""; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; 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; };
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 = ""; };
+ CE12CC196C45E9329399C340 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -57,20 +54,28 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
- 3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
+ F5B2B2DDBE5A1EB79AED33C5 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 3759F0CC5FBF8365CB6FB97A /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 5DE967DE2D339C44F7F49D7D /* Pods-Runner.debug.xcconfig */,
+ 4B00D748B979A95B621AF051 /* Pods-Runner.release.xcconfig */,
+ 52097B044775CCA165321119 /* 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 */,
@@ -84,6 +89,8 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
+ 3759F0CC5FBF8365CB6FB97A /* Pods */,
+ DFBA8D68B797ED2E9B97087D /* Frameworks */,
);
sourceTree = "";
};
@@ -118,6 +125,14 @@
name = "Supporting Files";
sourceTree = "";
};
+ DFBA8D68B797ED2E9B97087D /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ CE12CC196C45E9329399C340 /* Pods_Runner.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -125,12 +140,14 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
+ E1FFB7B3999391033FBBD542 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ 9657E195AA11E30B37B4EA84 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -147,7 +164,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 1020;
+ LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
@@ -201,7 +218,27 @@
);
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";
+ };
+ 9657E195AA11E30B37B4EA84 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
+ "${BUILT_PRODUCTS_DIR}/image_picker/image_picker.framework",
+ "${BUILT_PRODUCTS_DIR}/motion_sensors/motion_sensors.framework",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputPaths = (
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_picker.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/motion_sensors.framework",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
@@ -217,6 +254,28 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
+ E1FFB7B3999391033FBBD542 /* [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;
+ };
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -253,7 +312,6 @@
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
@@ -293,7 +351,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -315,7 +373,10 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
@@ -330,7 +391,6 @@
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
@@ -376,7 +436,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -386,7 +446,6 @@
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
@@ -426,7 +485,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -449,7 +508,10 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
@@ -476,7 +538,10 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
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.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index a28140c..3db53b6 100644
--- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
+
+
diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist
index a060db6..1579fb3 100644
--- a/example/ios/Runner/Info.plist
+++ b/example/ios/Runner/Info.plist
@@ -41,5 +41,7 @@
UIViewControllerBasedStatusBarAppearance
+ CADisableMinimumFrameDurationOnPhone
+
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 3958947..5f81ceb 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -75,6 +75,20 @@ class _MyHomePageState extends State {
switch (_panoId % panoImages.length) {
case 0:
panorama = Panorama(
+ //errorImage: Image.asset("assets/error.webp"),
+ progressBuilder: (double? progress) {
+ return Center(
+ child: CircularProgressIndicator(value: progress, color: Colors.blue, backgroundColor: Colors.white),
+ );
+ },
+ errorBuilder: (Object? error) {
+ return Center(
+ child: Text(
+ error.toString(),
+ textAlign: TextAlign.center,
+ ),
+ );
+ },
animSpeed: 1.0,
sensorControl: SensorControl.Orientation,
onViewChanged: onViewChanged,
@@ -82,7 +96,8 @@ class _MyHomePageState extends State {
onLongPressStart: (longitude, latitude, tilt) => print('onLongPressStart: $longitude, $latitude, $tilt'),
onLongPressMoveUpdate: (longitude, latitude, tilt) => print('onLongPressMoveUpdate: $longitude, $latitude, $tilt'),
onLongPressEnd: (longitude, latitude, tilt) => print('onLongPressEnd: $longitude, $latitude, $tilt'),
- child: Image.asset('assets/panorama.jpg'),
+ child: Image.network(
+ 'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Image_created_with_a_mobile_phone.png/800px-Image_created_with_a_mobile_phone.png'),
hotspots: [
Hotspot(
latitude: -15.0,
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 8900253..e2a5a41 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -7,7 +7,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
- version: "2.5.0"
+ version: "2.8.2"
boolean_selector:
dependency: transitive
description:
@@ -21,14 +21,14 @@ packages:
name: characters
url: "https://pub.dartlang.org"
source: hosted
- version: "1.1.0"
+ version: "1.2.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
- version: "1.2.0"
+ version: "1.3.1"
clock:
dependency: transitive
description:
@@ -42,14 +42,14 @@ packages:
name: collection
url: "https://pub.dartlang.org"
source: hosted
- version: "1.15.0"
+ version: "1.16.0"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
- version: "1.2.0"
+ version: "1.3.0"
flutter:
dependency: "direct main"
description: flutter
@@ -108,14 +108,21 @@ packages:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
- version: "0.12.10"
+ version: "0.12.11"
+ material_color_utilities:
+ dependency: transitive
+ description:
+ name: material_color_utilities
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.1.4"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
- version: "1.3.0"
+ version: "1.7.0"
motion_sensors:
dependency: transitive
description:
@@ -129,14 +136,14 @@ packages:
path: ".."
relative: true
source: path
- version: "0.4.0"
+ version: "0.4.1"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
- version: "1.8.0"
+ version: "1.8.1"
pedantic:
dependency: transitive
description:
@@ -162,7 +169,7 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
- version: "1.8.0"
+ version: "1.8.2"
stack_trace:
dependency: transitive
description:
@@ -197,7 +204,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
- version: "0.2.19"
+ version: "0.4.9"
typed_data:
dependency: transitive
description:
@@ -211,7 +218,7 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
- version: "2.1.0"
+ version: "2.1.2"
sdks:
- dart: ">=2.12.0 <3.0.0"
+ dart: ">=2.17.0-0 <3.0.0"
flutter: ">=1.20.0"
diff --git a/lib/panorama.dart b/lib/panorama.dart
index a82d87d..f1f1094 100644
--- a/lib/panorama.dart
+++ b/lib/panorama.dart
@@ -4,8 +4,9 @@ import 'dart:async';
import 'dart:ui' as ui;
import 'dart:math' as math;
import 'package:flutter/material.dart';
-import 'package:flutter_cube/flutter_cube.dart';
+import 'package:flutter_cube/flutter_cube.dart' as cube;
import 'package:motion_sensors/motion_sensors.dart';
+import 'dart:core';
enum SensorControl {
/// No sensor used.
@@ -48,6 +49,9 @@ class Panorama extends StatefulWidget {
this.onImageLoad,
this.child,
this.hotspots,
+ this.progressBuilder,
+ this.errorImage,
+ this.errorBuilder,
}) : super(key: key);
/// The initial latitude, in degrees, between -90 and 90. default to 0 (the vertical center of the image).
@@ -121,13 +125,22 @@ class Panorama extends StatefulWidget {
/// This event will be called when the user has stopped a long presses, it contains latitude and longitude about where the user pressed.
final Function(double longitude, double latitude, double tilt)? onLongPressEnd;
-
+
/// This event will be called when provided image is loaded on texture.
final Function()? onImageLoad;
/// Specify an Image(equirectangular image) widget to the panorama.
final Image? child;
+ /// Builder to display custom progress indicators
+ final Widget Function(double?)? progressBuilder;
+
+ /// Builder to display widget in case of image load error. Takes priority over errorImage.
+ final Widget Function(Object error)? errorBuilder;
+
+ /// Image to be displayed instead of child in case of image load error if no errorBuilder is provided. Will be displayed in panorama.
+ final Image? errorImage;
+
/// Place widgets in the panorama.
final List? hotspots;
@@ -136,8 +149,8 @@ class Panorama extends StatefulWidget {
}
class _PanoramaState extends State with SingleTickerProviderStateMixin {
- Scene? scene;
- Object? surface;
+ cube.Scene? scene;
+ cube.Object? surface;
late double latitude;
late double longitude;
double latitudeDelta = 0;
@@ -150,31 +163,36 @@ class _PanoramaState extends State with SingleTickerProviderStateMixin
double _animateDirection = 1.0;
late AnimationController _controller;
double screenOrientation = 0.0;
- Vector3 orientation = Vector3(0, radians(90), 0);
+ cube.Vector3 orientation = cube.Vector3(0, cube.radians(90), 0);
StreamSubscription? _orientationSubscription;
StreamSubscription? _screenOrientSubscription;
late StreamController _streamController;
Stream? _stream;
ImageStream? _imageStream;
+ ImageStream? _errorImageStream;
+ bool isLoading = true;
+ bool hasError = false;
+ double? imageProgress;
+ Object imageError = {};
void _handleTapUp(TapUpDetails details) {
- final Vector3 o = positionToLatLon(details.localPosition.dx, details.localPosition.dy);
- widget.onTap!(degrees(o.x), degrees(-o.y), degrees(o.z));
+ final cube.Vector3 o = positionToLatLon(details.localPosition.dx, details.localPosition.dy);
+ widget.onTap!(cube.degrees(o.x), cube.degrees(-o.y), cube.degrees(o.z));
}
void _handleLongPressStart(LongPressStartDetails details) {
- final Vector3 o = positionToLatLon(details.localPosition.dx, details.localPosition.dy);
- widget.onLongPressStart!(degrees(o.x), degrees(-o.y), degrees(o.z));
+ final cube.Vector3 o = positionToLatLon(details.localPosition.dx, details.localPosition.dy);
+ widget.onLongPressStart!(cube.degrees(o.x), cube.degrees(-o.y), cube.degrees(o.z));
}
void _handleLongPressMoveUpdate(LongPressMoveUpdateDetails details) {
- final Vector3 o = positionToLatLon(details.localPosition.dx, details.localPosition.dy);
- widget.onLongPressMoveUpdate!(degrees(o.x), degrees(-o.y), degrees(o.z));
+ final cube.Vector3 o = positionToLatLon(details.localPosition.dx, details.localPosition.dy);
+ widget.onLongPressMoveUpdate!(cube.degrees(o.x), cube.degrees(-o.y), cube.degrees(o.z));
}
void _handleLongPressEnd(LongPressEndDetails details) {
- final Vector3 o = positionToLatLon(details.localPosition.dx, details.localPosition.dy);
- widget.onLongPressEnd!(degrees(o.x), degrees(-o.y), degrees(o.z));
+ final cube.Vector3 o = positionToLatLon(details.localPosition.dx, details.localPosition.dy);
+ widget.onLongPressEnd!(cube.degrees(o.x), cube.degrees(-o.y), cube.degrees(o.z));
}
void _handleScaleStart(ScaleStartDetails details) {
@@ -215,27 +233,23 @@ class _PanoramaState extends State with SingleTickerProviderStateMixin
zoomDelta *= 1 - _dampingFactor;
scene!.camera.zoom = zoom.clamp(widget.minZoom, widget.maxZoom);
// stop animation if not needed
- if (latitudeDelta.abs() < 0.001 &&
- longitudeDelta.abs() < 0.001 &&
- zoomDelta.abs() < 0.001) {
- if (widget.sensorControl == SensorControl.None &&
- widget.animSpeed == 0 &&
- _controller.isAnimating) _controller.stop();
+ if (latitudeDelta.abs() < 0.001 && longitudeDelta.abs() < 0.001 && zoomDelta.abs() < 0.001) {
+ if (widget.sensorControl == SensorControl.None && widget.animSpeed == 0 && _controller.isAnimating) _controller.stop();
}
// rotate for screen orientation
- Quaternion q = Quaternion.axisAngle(Vector3(0, 0, 1), screenOrientation);
+ cube.Quaternion q = cube.Quaternion.axisAngle(cube.Vector3(0, 0, 1), screenOrientation);
// rotate for device orientation
- q *= Quaternion.euler(-orientation.z, -orientation.y, -orientation.x);
+ q *= cube.Quaternion.euler(-orientation.z, -orientation.y, -orientation.x);
// rotate to latitude zero
- q *= Quaternion.axisAngle(Vector3(1, 0, 0), math.pi * 0.5);
+ q *= cube.Quaternion.axisAngle(cube.Vector3(1, 0, 0), math.pi * 0.5);
// check and limit the rotation range
- Vector3 o = quaternionToOrientation(q);
- final double minLat = radians(math.max(-89.9, widget.minLatitude));
- final double maxLat = radians(math.min(89.9, widget.maxLatitude));
- final double minLon = radians(widget.minLongitude);
- final double maxLon = radians(widget.maxLongitude);
+ cube.Vector3 o = quaternionToOrientation(q);
+ final double minLat = cube.radians(math.max(-89.9, widget.minLatitude));
+ final double maxLat = cube.radians(math.min(89.9, widget.maxLatitude));
+ final double minLon = cube.radians(widget.minLongitude);
+ final double maxLon = cube.radians(widget.maxLongitude);
final double lat = (-o.y).clamp(minLat, maxLat);
final double lon = o.x.clamp(minLon, maxLon);
if (lat + latitude < minLat) latitude = minLat - lat;
@@ -257,17 +271,17 @@ class _PanoramaState extends State with SingleTickerProviderStateMixin
q = orientationToQuaternion(o);
// rotate to longitude zero
- q *= Quaternion.axisAngle(Vector3(0, 1, 0), -math.pi * 0.5);
+ q *= cube.Quaternion.axisAngle(cube.Vector3(0, 1, 0), -math.pi * 0.5);
// rotate around the global Y axis
- q *= Quaternion.axisAngle(Vector3(0, 1, 0), longitude);
+ q *= cube.Quaternion.axisAngle(cube.Vector3(0, 1, 0), longitude);
// rotate around the local X axis
- q = Quaternion.axisAngle(Vector3(1, 0, 0), -latitude) * q;
+ q = cube.Quaternion.axisAngle(cube.Vector3(1, 0, 0), -latitude) * q;
- o = quaternionToOrientation(q * Quaternion.axisAngle(Vector3(0, 1, 0), math.pi * 0.5));
- widget.onViewChanged?.call(degrees(o.x), degrees(-o.y), degrees(o.z));
+ o = quaternionToOrientation(q * cube.Quaternion.axisAngle(cube.Vector3(0, 1, 0), math.pi * 0.5));
+ widget.onViewChanged?.call(cube.degrees(o.x), cube.degrees(-o.y), cube.degrees(o.z));
- q.rotate(scene!.camera.target..setFrom(Vector3(0, 0, -_radius)));
- q.rotate(scene!.camera.up..setFrom(Vector3(0, 1, 0)));
+ q.rotate(scene!.camera.target..setFrom(cube.Vector3(0, 0, -_radius)));
+ q.rotate(scene!.camera.up..setFrom(cube.Vector3(0, 1, 0)));
scene!.update();
_streamController.add(null);
}
@@ -293,12 +307,13 @@ class _PanoramaState extends State with SingleTickerProviderStateMixin
_screenOrientSubscription?.cancel();
if (widget.sensorControl != SensorControl.None) {
_screenOrientSubscription = motionSensors.screenOrientation.listen((ScreenOrientationEvent event) {
- screenOrientation = radians(event.angle!);
+ screenOrientation = cube.radians(event.angle!);
});
}
}
void _updateTexture(ImageInfo imageInfo, bool synchronousCall) {
+ setState(() => isLoading = false);
surface?.mesh.texture = imageInfo.image;
surface?.mesh.textureRect = Rect.fromLTWH(0, 0, imageInfo.image.width.toDouble(), imageInfo.image.height.toDouble());
scene!.texture = imageInfo.image;
@@ -306,24 +321,49 @@ class _PanoramaState extends State with SingleTickerProviderStateMixin
widget.onImageLoad?.call();
}
+ void _updateLoadProgress(ImageChunkEvent event) {
+ setState((() => imageProgress = (event.cumulativeBytesLoaded / event.expectedTotalBytes!)));
+ }
+
+ void _handleImageLoadError(Object object, StackTrace? stackTrace) {
+ if (widget.errorImage != null && widget.errorBuilder == null) {
+ _errorImageStream?.removeListener(ImageStreamListener(_updateTexture));
+ _errorImageStream = widget.errorImage!.image.resolve(ImageConfiguration());
+ ImageStreamListener listener = ImageStreamListener(_updateTexture);
+ _errorImageStream!.addListener(listener);
+ } else if (widget.errorBuilder != null) {
+ setState(() {
+ isLoading = false;
+ hasError = true;
+ imageError = object;
+ });
+ }
+ }
+
void _loadTexture(ImageProvider? provider) {
if (provider == null) return;
- _imageStream?.removeListener(ImageStreamListener(_updateTexture));
+ _imageStream?.removeListener(ImageStreamListener(_updateTexture, onChunk: _updateLoadProgress, onError: _handleImageLoadError));
_imageStream = provider.resolve(ImageConfiguration());
- ImageStreamListener listener = ImageStreamListener(_updateTexture);
+ ImageStreamListener listener = ImageStreamListener(_updateTexture, onChunk: _updateLoadProgress, onError: _handleImageLoadError);
_imageStream!.addListener(listener);
}
- void _onSceneCreated(Scene scene) {
+ void _onSceneCreated(cube.Scene scene) {
this.scene = scene;
scene.camera.near = 1.0;
scene.camera.far = _radius + 1.0;
scene.camera.fov = 75;
scene.camera.zoom = widget.zoom;
- scene.camera.position.setFrom(Vector3(0, 0, 0.1));
+ scene.camera.position.setFrom(cube.Vector3(0, 0, 0.1));
if (widget.child != null) {
- final Mesh mesh = generateSphereMesh(radius: _radius, latSegments: widget.latSegments, lonSegments: widget.lonSegments, croppedArea: widget.croppedArea, croppedFullWidth: widget.croppedFullWidth, croppedFullHeight: widget.croppedFullHeight);
- surface = Object(name: 'surface', mesh: mesh, backfaceCulling: false);
+ final cube.Mesh mesh = generateSphereMesh(
+ radius: _radius,
+ latSegments: widget.latSegments,
+ lonSegments: widget.lonSegments,
+ croppedArea: widget.croppedArea,
+ croppedFullWidth: widget.croppedFullWidth,
+ croppedFullHeight: widget.croppedFullHeight);
+ surface = cube.Object(name: 'surface', mesh: mesh, backfaceCulling: false);
_loadTexture(widget.child!.image);
scene.world.add(surface!);
_updateView();
@@ -331,12 +371,12 @@ class _PanoramaState extends State with SingleTickerProviderStateMixin
}
Matrix4 matrixFromLatLon(double lat, double lon) {
- return Matrix4.rotationY(radians(90.0 - lon))..rotateX(radians(lat));
+ return Matrix4.rotationY(cube.radians(90.0 - lon))..rotateX(cube.radians(lat));
}
- Vector3 positionToLatLon(double x, double y) {
+ cube.Vector3 positionToLatLon(double x, double y) {
// transform viewport coordinate to NDC, values between -1 and 1
- final Vector4 v = Vector4(2.0 * x / scene!.camera.viewportWidth - 1.0, 1.0 - 2.0 * y / scene!.camera.viewportHeight, 1.0, 1.0);
+ final cube.Vector4 v = cube.Vector4(2.0 * x / scene!.camera.viewportWidth - 1.0, 1.0 - 2.0 * y / scene!.camera.viewportHeight, 1.0, 1.0);
// create projection matrix
final Matrix4 m = scene!.camera.projectionMatrix * scene!.camera.lookAtMatrix;
// apply inversed projection matrix
@@ -345,18 +385,18 @@ class _PanoramaState extends State with SingleTickerProviderStateMixin
// apply perspective division
v.scale(1 / v.w);
// get rotation from two vectors
- final Quaternion q = Quaternion.fromTwoVectors(v.xyz, Vector3(0.0, 0.0, -_radius));
+ final cube.Quaternion q = cube.Quaternion.fromTwoVectors(v.xyz, cube.Vector3(0.0, 0.0, -_radius));
// get euler angles from rotation
- return quaternionToOrientation(q * Quaternion.axisAngle(Vector3(0, 1, 0), math.pi * 0.5));
+ return quaternionToOrientation(q * cube.Quaternion.axisAngle(cube.Vector3(0, 1, 0), math.pi * 0.5));
}
- Vector3 positionFromLatLon(double lat, double lon) {
+ cube.Vector3 positionFromLatLon(double lat, double lon) {
// create projection matrix
final Matrix4 m = scene!.camera.projectionMatrix * scene!.camera.lookAtMatrix * matrixFromLatLon(lat, lon);
// apply projection matrix
- final Vector4 v = Vector4(0.0, 0.0, -_radius, 1.0)..applyMatrix4(m);
+ final cube.Vector4 v = cube.Vector4(0.0, 0.0, -_radius, 1.0)..applyMatrix4(m);
// apply perspective division and transform NDC to the viewport coordinate
- return Vector3(
+ return cube.Vector3(
(1.0 + v.x / v.w) * scene!.camera.viewportWidth / 2,
(1.0 - v.y / v.w) * scene!.camera.viewportHeight / 2,
v.z,
@@ -367,7 +407,7 @@ class _PanoramaState extends State with SingleTickerProviderStateMixin
final List widgets = [];
if (hotspots != null && scene != null) {
for (Hotspot hotspot in hotspots) {
- final Vector3 pos = positionFromLatLon(hotspot.latitude, hotspot.longitude);
+ final cube.Vector3 pos = positionFromLatLon(hotspot.latitude, hotspot.longitude);
final Offset orgin = Offset(hotspot.width * hotspot.orgin.dx, hotspot.height * hotspot.orgin.dy);
final Matrix4 transform = scene!.camera.lookAtMatrix * matrixFromLatLon(hotspot.latitude, hotspot.longitude);
final Widget child = Positioned(
@@ -393,8 +433,8 @@ class _PanoramaState extends State with SingleTickerProviderStateMixin
@override
void initState() {
super.initState();
- latitude = degrees(widget.latitude);
- longitude = degrees(widget.longitude);
+ latitude = cube.degrees(widget.latitude);
+ longitude = cube.degrees(widget.longitude);
_streamController = StreamController.broadcast();
_stream = _streamController.stream;
@@ -406,9 +446,11 @@ class _PanoramaState extends State with SingleTickerProviderStateMixin
@override
void dispose() {
- _imageStream?.removeListener(ImageStreamListener(_updateTexture));
+ _imageStream?.removeListener(ImageStreamListener(_updateTexture, onChunk: _updateLoadProgress, onError: _handleImageLoadError));
+ _errorImageStream?.removeListener(ImageStreamListener(_updateTexture));
_orientationSubscription?.cancel();
_screenOrientSubscription?.cancel();
+ scene = null ;
_controller.dispose();
_streamController.close();
super.dispose();
@@ -418,8 +460,18 @@ class _PanoramaState extends State with SingleTickerProviderStateMixin
void didUpdateWidget(Panorama oldWidget) {
super.didUpdateWidget(oldWidget);
if (surface == null) return;
- if (widget.latSegments != oldWidget.latSegments || widget.lonSegments != oldWidget.lonSegments || widget.croppedArea != oldWidget.croppedArea || widget.croppedFullWidth != oldWidget.croppedFullWidth || widget.croppedFullHeight != oldWidget.croppedFullHeight) {
- surface!.mesh = generateSphereMesh(radius: _radius, latSegments: widget.latSegments, lonSegments: widget.lonSegments, croppedArea: widget.croppedArea, croppedFullWidth: widget.croppedFullWidth, croppedFullHeight: widget.croppedFullHeight);
+ if (widget.latSegments != oldWidget.latSegments ||
+ widget.lonSegments != oldWidget.lonSegments ||
+ widget.croppedArea != oldWidget.croppedArea ||
+ widget.croppedFullWidth != oldWidget.croppedFullWidth ||
+ widget.croppedFullHeight != oldWidget.croppedFullHeight) {
+ surface!.mesh = generateSphereMesh(
+ radius: _radius,
+ latSegments: widget.latSegments,
+ lonSegments: widget.lonSegments,
+ croppedArea: widget.croppedArea,
+ croppedFullWidth: widget.croppedFullWidth,
+ croppedFullHeight: widget.croppedFullHeight);
}
if (widget.child?.image != oldWidget.child?.image) {
_loadTexture(widget.child?.image);
@@ -433,7 +485,7 @@ class _PanoramaState extends State with SingleTickerProviderStateMixin
Widget build(BuildContext context) {
Widget pano = Stack(
children: [
- Cube(interactive: false, onSceneCreated: _onSceneCreated),
+ cube.Cube(interactive: false, onSceneCreated: _onSceneCreated),
StreamBuilder(
stream: _stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
@@ -443,17 +495,21 @@ class _PanoramaState extends State with SingleTickerProviderStateMixin
],
);
- return widget.interactive
- ? GestureDetector(
- onScaleStart: _handleScaleStart,
- onScaleUpdate: _handleScaleUpdate,
- onTapUp: widget.onTap == null ? null : _handleTapUp,
- onLongPressStart: widget.onLongPressStart == null ? null : _handleLongPressStart,
- onLongPressMoveUpdate: widget.onLongPressMoveUpdate == null ? null : _handleLongPressMoveUpdate,
- onLongPressEnd: widget.onLongPressEnd == null ? null : _handleLongPressEnd,
- child: pano,
- )
- : pano;
+ return Stack(children: [
+ widget.interactive
+ ? GestureDetector(
+ onScaleStart: _handleScaleStart,
+ onScaleUpdate: _handleScaleUpdate,
+ onTapUp: widget.onTap == null ? null : _handleTapUp,
+ onLongPressStart: widget.onLongPressStart == null ? null : _handleLongPressStart,
+ onLongPressMoveUpdate: widget.onLongPressMoveUpdate == null ? null : _handleLongPressMoveUpdate,
+ onLongPressEnd: widget.onLongPressEnd == null ? null : _handleLongPressEnd,
+ child: pano,
+ )
+ : pano,
+ if (isLoading && widget.progressBuilder != null) widget.progressBuilder!(imageProgress),
+ if (hasError && widget.errorBuilder != null) widget.errorBuilder!(imageError)
+ ]);
}
}
@@ -489,11 +545,18 @@ class Hotspot {
Widget? widget;
}
-Mesh generateSphereMesh({num radius = 1.0, int latSegments = 16, int lonSegments = 16, ui.Image? texture, Rect croppedArea = const Rect.fromLTWH(0.0, 0.0, 1.0, 1.0), double croppedFullWidth = 1.0, double croppedFullHeight = 1.0}) {
+cube.Mesh generateSphereMesh(
+ {num radius = 1.0,
+ int latSegments = 16,
+ int lonSegments = 16,
+ ui.Image? texture,
+ Rect croppedArea = const Rect.fromLTWH(0.0, 0.0, 1.0, 1.0),
+ double croppedFullWidth = 1.0,
+ double croppedFullHeight = 1.0}) {
int count = (latSegments + 1) * (lonSegments + 1);
- List vertices = List.filled(count, Vector3.zero());
+ List vertices = List.filled(count, cube.Vector3.zero());
List texcoords = List.filled(count, Offset.zero);
- List indices = List.filled(latSegments * lonSegments * 2, Polygon(0, 0, 0));
+ List indices = List.filled(latSegments * lonSegments * 2, cube.Polygon(0, 0, 0));
int i = 0;
for (int y = 0; y <= latSegments; ++y) {
@@ -504,7 +567,7 @@ Mesh generateSphereMesh({num radius = 1.0, int latSegments = 16, int lonSegments
for (int x = 0; x <= lonSegments; ++x) {
final double tu = x / lonSegments;
final double u = (croppedArea.left + croppedArea.width * tu) / croppedFullWidth;
- vertices[i] = Vector3(radius * math.cos(u * math.pi * 2.0) * sv, radius * cv, radius * math.sin(u * math.pi * 2.0) * sv);
+ vertices[i] = cube.Vector3(radius * math.cos(u * math.pi * 2.0) * sv, radius * cv, radius * math.sin(u * math.pi * 2.0) * sv);
texcoords[i] = Offset(tu, 1.0 - tv);
i++;
}
@@ -515,16 +578,16 @@ Mesh generateSphereMesh({num radius = 1.0, int latSegments = 16, int lonSegments
final int base1 = (lonSegments + 1) * y;
final int base2 = (lonSegments + 1) * (y + 1);
for (int x = 0; x < lonSegments; ++x) {
- indices[i++] = Polygon(base1 + x, base1 + x + 1, base2 + x);
- indices[i++] = Polygon(base1 + x + 1, base2 + x + 1, base2 + x);
+ indices[i++] = cube.Polygon(base1 + x, base1 + x + 1, base2 + x);
+ indices[i++] = cube.Polygon(base1 + x + 1, base2 + x + 1, base2 + x);
}
}
- final Mesh mesh = Mesh(vertices: vertices, texcoords: texcoords, indices: indices, texture: texture);
+ final cube.Mesh mesh = cube.Mesh(vertices: vertices, texcoords: texcoords, indices: indices, texture: texture);
return mesh;
}
-Vector3 quaternionToOrientation(Quaternion q) {
+cube.Vector3 quaternionToOrientation(cube.Quaternion q) {
// final Matrix4 m = Matrix4.compose(Vector3.zero(), q, Vector3.all(1.0));
// final Vector v = motionSensors.getOrientation(m);
// return Vector3(v.z, v.y, v.x);
@@ -536,13 +599,13 @@ Vector3 quaternionToOrientation(Quaternion q) {
final double roll = math.atan2(-2 * (x * y - w * z), 1.0 - 2 * (x * x + z * z));
final double pitch = math.asin(2 * (y * z + w * x));
final double yaw = math.atan2(-2 * (x * z - w * y), 1.0 - 2 * (x * x + y * y));
- return Vector3(yaw, pitch, roll);
+ return cube.Vector3(yaw, pitch, roll);
}
-Quaternion orientationToQuaternion(Vector3 v) {
+cube.Quaternion orientationToQuaternion(cube.Vector3 v) {
final Matrix4 m = Matrix4.identity();
m.rotateZ(v.z);
m.rotateX(v.y);
m.rotateY(v.x);
- return Quaternion.fromRotation(m.getRotation());
+ return cube.Quaternion.fromRotation(m.getRotation());
}
diff --git a/pubspec.lock b/pubspec.lock
index ae41e5e..ef542c7 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -7,7 +7,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
- version: "2.5.0"
+ version: "2.8.2"
boolean_selector:
dependency: transitive
description:
@@ -21,14 +21,14 @@ packages:
name: characters
url: "https://pub.dartlang.org"
source: hosted
- version: "1.1.0"
+ version: "1.2.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
- version: "1.2.0"
+ version: "1.3.1"
clock:
dependency: transitive
description:
@@ -42,14 +42,14 @@ packages:
name: collection
url: "https://pub.dartlang.org"
source: hosted
- version: "1.15.0"
+ version: "1.16.0"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
- version: "1.2.0"
+ version: "1.3.0"
flutter:
dependency: "direct main"
description: flutter
@@ -73,14 +73,21 @@ packages:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
- version: "0.12.10"
+ version: "0.12.11"
+ material_color_utilities:
+ dependency: transitive
+ description:
+ name: material_color_utilities
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.1.4"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
- version: "1.3.0"
+ version: "1.7.0"
motion_sensors:
dependency: "direct main"
description:
@@ -94,7 +101,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
- version: "1.8.0"
+ version: "1.8.1"
sky_engine:
dependency: transitive
description: flutter
@@ -106,7 +113,7 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
- version: "1.8.0"
+ version: "1.8.2"
stack_trace:
dependency: transitive
description:
@@ -141,21 +148,14 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
- version: "0.2.19"
- typed_data:
- dependency: transitive
- description:
- name: typed_data
- url: "https://pub.dartlang.org"
- source: hosted
- version: "1.3.0"
+ version: "0.4.9"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
- version: "2.1.0"
+ version: "2.1.2"
sdks:
- dart: ">=2.12.0 <3.0.0"
+ dart: ">=2.17.0-0 <3.0.0"
flutter: ">=1.10.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index c734ac5..488ff36 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -17,3 +17,6 @@ dev_dependencies:
sdk: flutter
flutter:
+ assets:
+ - assets/
+ - resource/