Compare commits

..

No commits in common. "aves" and "master" have entirely different histories.
aves ... master

54 changed files with 1468 additions and 703 deletions

View file

@ -1,47 +0,0 @@
include: package:flutter_lints/flutter.yaml
analyzer:
exclude:
- lib/generated_plugin_registrant.dart
# strong-mode:
# implicit-casts: false
# implicit-dynamic: false
# cf https://github.com/dart-lang/dart_style/wiki/Configuration
formatter:
page_width: 240
trailing_commas: preserve
linter:
rules:
# from 'flutter_lints', excluded
use_build_context_synchronously: false # no alternative
# from 'lints / recommended', excluded
no_leading_underscores_for_local_identifiers: false # useful for null checked variable variants
# from 'effective dart', excluded
avoid_classes_with_only_static_members: false # too strict
avoid_function_literals_in_foreach_calls: false # benefit?
lines_longer_than_80_chars: false # nope
public_member_api_docs: false # this project is not a library
# from 'effective dart', undecided
prefer_relative_imports: false # check IDE support (auto import, file move)
# from 'effective dart', included
avoid_types_on_closure_parameters: true
prefer_interpolation_to_compose_strings: true
unnecessary_lambdas: true
# from 'pedantic', included
always_declare_return_types: true
prefer_single_quotes: true
sort_child_properties_last: true
unawaited_futures: true
# `const` related, included
prefer_const_constructors: true
prefer_const_literals_to_create_immutables: true
prefer_const_declarations: true

20
example/.gitignore vendored
View file

@ -5,12 +5,9 @@
*.swp *.swp
.DS_Store .DS_Store
.atom/ .atom/
.build/
.buildlog/ .buildlog/
.history .history
.svn/ .svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related # IntelliJ related
*.iml *.iml
@ -25,21 +22,16 @@ migrate_working_dir/
# Flutter/Dart/Pub related # Flutter/Dart/Pub related
**/doc/api/ **/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/ .dart_tool/
.flutter-plugins
.flutter-plugins-dependencies .flutter-plugins-dependencies
.packages
.pub-cache/ .pub-cache/
.pub/ .pub/
/build/ /build/
/coverage/
# Symbolication related # Web related
app.*.symbols lib/generated_plugin_registrant.dart
# Obfuscation related # Exceptions to above rules.
app.*.map.json !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

View file

@ -5,10 +5,3 @@ gradle-wrapper.jar
/gradlew.bat /gradlew.bat
/local.properties /local.properties
GeneratedPluginRegistrant.java GeneratedPluginRegistrant.java
.cxx/
# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks

View file

@ -0,0 +1,67 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 28
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.example"
minSdkVersion 16
targetSdkVersion 28
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

View file

@ -1,42 +0,0 @@
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}
android {
namespace = "com.example.example"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
defaultConfig {
applicationId = "com.example.example"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
buildTypes {
release {
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
}
flutter {
source = "../.."
}

View file

@ -1,11 +1,16 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application <application
android:name="${applicationName}" android:name="io.flutter.app.FlutterApplication"
android:label="example" android:label="example"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity
android:name="io.flutter.embedding.android.FlutterActivity" android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"

View file

@ -0,0 +1,12 @@
package com.example.example
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
}
}

View file

@ -0,0 +1,31 @@
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View file

@ -1,24 +0,0 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
val newBuildDir: Directory =
rootProject.layout.buildDirectory
.dir("../../build")
.get()
rootProject.layout.buildDirectory.value(newBuildDir)
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}

View file

@ -1,2 +1,4 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true

View file

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip

View file

@ -0,0 +1,15 @@
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}

View file

@ -1,26 +0,0 @@
pluginManagement {
val flutterSdkPath =
run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.11.1" apply false
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
}
include(":app")

32
example/ios/.gitignore vendored Normal file
View file

@ -0,0 +1,32 @@
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>8.0</string>
</dict>
</plist>

View file

@ -0,0 +1,2 @@
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View file

@ -0,0 +1,2 @@
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

90
example/ios/Podfile Normal file
View file

@ -0,0 +1,90 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def parse_KV_file(file, separator='=')
file_abs_path = File.expand_path(file)
if !File.exists? file_abs_path
return [];
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
end
generated_key_values
end
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
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
end
end

View file

@ -0,0 +1,518 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
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 */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
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 = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B80C3931E831B6300D905FE /* App.framework */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEBA1CF902C7004384FC /* Flutter.framework */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
97C146F11CF9000F007C117D /* Supporting Files */ = {
isa = PBXGroup;
children = (
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View file

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View file

@ -0,0 +1,13 @@
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

View file

@ -0,0 +1,122 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View file

@ -0,0 +1,5 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>example</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>

View file

@ -0,0 +1 @@
#import "GeneratedPluginRegistrant.h"

View file

@ -1,31 +1,28 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:panorama/panorama.dart'; import 'package:panorama/panorama.dart';
import 'package:image_picker/image_picker.dart';
void main() => runApp(const MyApp()); void main() => runApp(MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
title: 'Flutter Panorama', title: 'Flutter Panorama',
theme: ThemeData.dark(), theme: ThemeData.dark(),
home: const MyHomePage(title: 'Flutter Panorama'), home: MyHomePage(title: 'Flutter Panorama'),
); );
} }
} }
class MyHomePage extends StatefulWidget { class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, this.title}); MyHomePage({Key? key, this.title}) : super(key: key);
final String? title; final String? title;
@override @override
State<MyHomePage> createState() => _MyHomePageState(); _MyHomePageState createState() => _MyHomePageState();
} }
class _MyHomePageState extends State<MyHomePage> { class _MyHomePageState extends State<MyHomePage> {
@ -40,7 +37,7 @@ class _MyHomePageState extends State<MyHomePage> {
]; ];
ImagePicker picker = ImagePicker(); ImagePicker picker = ImagePicker();
void onViewChanged(double longitude, double latitude, double tilt) { void onViewChanged(longitude, latitude, tilt) {
setState(() { setState(() {
_lon = longitude; _lon = longitude;
_lat = latitude; _lat = latitude;
@ -54,17 +51,17 @@ class _MyHomePageState extends State<MyHomePage> {
children: [ children: [
TextButton( TextButton(
style: ButtonStyle( style: ButtonStyle(
shape: WidgetStateProperty.all(const CircleBorder()), shape: MaterialStateProperty.all(CircleBorder()),
backgroundColor: WidgetStateProperty.all(Colors.black38), backgroundColor: MaterialStateProperty.all(Colors.black38),
foregroundColor: WidgetStateProperty.all(Colors.white), foregroundColor: MaterialStateProperty.all(Colors.white),
), ),
onPressed: onPressed,
child: Icon(icon), child: Icon(icon),
onPressed: onPressed,
), ),
text != null text != null
? Container( ? Container(
padding: const EdgeInsets.all(4.0), padding: EdgeInsets.all(4.0),
decoration: const BoxDecoration(color: Colors.black38, borderRadius: BorderRadius.all(Radius.circular(4))), decoration: BoxDecoration(color: Colors.black38, borderRadius: BorderRadius.all(Radius.circular(4))),
child: Center(child: Text(text)), child: Center(child: Text(text)),
) )
: Container(), : Container(),
@ -78,19 +75,21 @@ class _MyHomePageState extends State<MyHomePage> {
switch (_panoId % panoImages.length) { switch (_panoId % panoImages.length) {
case 0: case 0:
panorama = Panorama( panorama = Panorama(
sensorControl: SensorControl.orientation, animSpeed: 1.0,
sensorControl: SensorControl.Orientation,
onViewChanged: onViewChanged, onViewChanged: onViewChanged,
onTap: (longitude, latitude, tilt) => debugPrint('onTap: $longitude, $latitude, $tilt'), onTap: (longitude, latitude, tilt) => print('onTap: $longitude, $latitude, $tilt'),
onLongPressStart: (longitude, latitude, tilt) => debugPrint('onLongPressStart: $longitude, $latitude, $tilt'), onLongPressStart: (longitude, latitude, tilt) => print('onLongPressStart: $longitude, $latitude, $tilt'),
onLongPressMoveUpdate: (longitude, latitude, tilt) => debugPrint('onLongPressMoveUpdate: $longitude, $latitude, $tilt'), onLongPressMoveUpdate: (longitude, latitude, tilt) => print('onLongPressMoveUpdate: $longitude, $latitude, $tilt'),
onLongPressEnd: (longitude, latitude, tilt) => debugPrint('onLongPressEnd: $longitude, $latitude, $tilt'), onLongPressEnd: (longitude, latitude, tilt) => print('onLongPressEnd: $longitude, $latitude, $tilt'),
child: Image.asset('assets/panorama.jpg'),
hotspots: [ hotspots: [
Hotspot( Hotspot(
latitude: -15.0, latitude: -15.0,
longitude: -129.0, longitude: -129.0,
width: 90, width: 90,
height: 75, height: 75,
widget: hotspotButton(text: 'Next scene', icon: Icons.open_in_browser, onPressed: () => setState(() => _panoId++)), widget: hotspotButton(text: "Next scene", icon: Icons.open_in_browser, onPressed: () => setState(() => _panoId++)),
), ),
Hotspot( Hotspot(
latitude: -42.0, latitude: -42.0,
@ -107,42 +106,43 @@ class _MyHomePageState extends State<MyHomePage> {
widget: hotspotButton(icon: Icons.arrow_upward, onPressed: () {}), widget: hotspotButton(icon: Icons.arrow_upward, onPressed: () {}),
), ),
], ],
child: Image.asset('assets/panorama.jpg'),
); );
break; break;
case 2: case 2:
panorama = Panorama( panorama = Panorama(
sensorControl: SensorControl.orientation, animSpeed: 1.0,
sensorControl: SensorControl.Orientation,
onViewChanged: onViewChanged, onViewChanged: onViewChanged,
croppedArea: const Rect.fromLTWH(2533.0, 1265.0, 5065.0, 2533.0), croppedArea: Rect.fromLTWH(2533.0, 1265.0, 5065.0, 2533.0),
croppedFullWidth: 10132.0, croppedFullWidth: 10132.0,
croppedFullHeight: 5066.0, croppedFullHeight: 5066.0,
child: Image.asset('assets/panorama_cropped.jpg'),
hotspots: [ hotspots: [
Hotspot( Hotspot(
latitude: 0.0, latitude: 0.0,
longitude: -46.0, longitude: -46.0,
width: 90.0, width: 90.0,
height: 75.0, height: 75.0,
widget: hotspotButton(text: 'Next scene', icon: Icons.double_arrow, onPressed: () => setState(() => _panoId++)), widget: hotspotButton(text: "Next scene", icon: Icons.double_arrow, onPressed: () => setState(() => _panoId++)),
), ),
], ],
child: Image.asset('assets/panorama_cropped.jpg'),
); );
break; break;
default: default:
panorama = Panorama( panorama = Panorama(
sensorControl: SensorControl.orientation, animSpeed: 1.0,
sensorControl: SensorControl.Orientation,
onViewChanged: onViewChanged, onViewChanged: onViewChanged,
child: panoImages[_panoId % panoImages.length],
hotspots: [ hotspots: [
Hotspot( Hotspot(
latitude: 0.0, latitude: 0.0,
longitude: 160.0, longitude: 160.0,
width: 90.0, width: 90.0,
height: 75.0, height: 75.0,
widget: hotspotButton(text: 'Next scene', icon: Icons.double_arrow, onPressed: () => setState(() => _panoId++)), widget: hotspotButton(text: "Next scene", icon: Icons.double_arrow, onPressed: () => setState(() => _panoId++)),
), ),
], ],
child: panoImages[_panoId % panoImages.length],
); );
} }
return Scaffold( return Scaffold(
@ -158,7 +158,7 @@ class _MyHomePageState extends State<MyHomePage> {
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
mini: true, mini: true,
onPressed: () async { onPressed: () async {
final pickedFile = await picker.pickImage(source: ImageSource.gallery); final pickedFile = await picker.getImage(source: ImageSource.gallery);
setState(() { setState(() {
if (pickedFile != null) { if (pickedFile != null) {
panoImages.add(Image.file(File(pickedFile.path))); panoImages.add(Image.file(File(pickedFile.path)));
@ -166,7 +166,7 @@ class _MyHomePageState extends State<MyHomePage> {
} }
}); });
}, },
child: const Icon(Icons.panorama), child: Icon(Icons.panorama),
), ),
); );
} }

View file

@ -5,90 +5,51 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: async name: async
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.13.0" version: "2.5.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
name: boolean_selector name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.2" version: "2.1.0"
characters: characters:
dependency: transitive dependency: transitive
description: description:
name: characters name: characters
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.1" version: "1.1.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
clock: clock:
dependency: transitive dependency: transitive
description: description:
name: clock name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.2" version: "1.1.0"
collection: collection:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.19.1" version: "1.15.0"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937"
url: "https://pub.dev"
source: hosted
version: "0.3.5+2"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
name: fake_async name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.3" version: "1.2.0"
file_selector_linux:
dependency: transitive
description:
name: file_selector_linux
sha256: "2567f398e06ac72dcf2e98a0c95df2a9edd03c2c2e0cacd4780f20cdf56263a0"
url: "https://pub.dev"
source: hosted
version: "0.9.4"
file_selector_macos:
dependency: transitive
description:
name: file_selector_macos
sha256: "5e0bbe9c312416f1787a68259ea1505b52f258c587f12920422671807c4d618a"
url: "https://pub.dev"
source: hosted
version: "0.9.5"
file_selector_platform_interface:
dependency: transitive
description:
name: file_selector_platform_interface
sha256: "35e0bd61ebcdb91a3505813b055b09b79dfdc7d0aee9c09a7ba59ae4bb13dc85"
url: "https://pub.dev"
source: hosted
version: "2.7.0"
file_selector_windows:
dependency: transitive
description:
name: file_selector_windows
sha256: "62197474ae75893a62df75939c777763d39c2bc5f73ce5b88497208bc269abfd"
url: "https://pub.dev"
source: hosted
version: "0.9.3+5"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -98,172 +59,69 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_cube name: flutter_cube
sha256: "71cf679a251166eb97f86751c56582b09abdbf859485fbf60524948813914c3b" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.1" version: "0.1.1"
flutter_plugin_android_lifecycle: flutter_plugin_android_lifecycle:
dependency: transitive dependency: transitive
description: description:
name: flutter_plugin_android_lifecycle name: flutter_plugin_android_lifecycle
sha256: ee8068e0e1cd16c4a82714119918efdeed33b3ba7772c54b5d094ab53f9b7fd1 url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.33" version: "2.0.0"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
http: http:
dependency: transitive dependency: transitive
description: description:
name: http name: http
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.6.0" version: "0.13.0"
http_parser: http_parser:
dependency: transitive dependency: transitive
description: description:
name: http_parser name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "4.1.2" version: "4.0.0"
image_picker: image_picker:
dependency: "direct main" dependency: "direct main"
description: description:
name: image_picker name: image_picker
sha256: "784210112be18ea55f69d7076e2c656a4e24949fa9e76429fe53af0c0f4fa320" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.1" version: "0.7.2+1"
image_picker_android:
dependency: transitive
description:
name: image_picker_android
sha256: eda9b91b7e266d9041084a42d605a74937d996b87083395c5e47835916a86156
url: "https://pub.dev"
source: hosted
version: "0.8.13+14"
image_picker_for_web:
dependency: transitive
description:
name: image_picker_for_web
sha256: "66257a3191ab360d23a55c8241c91a6e329d31e94efa7be9cf7a212e65850214"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
image_picker_ios:
dependency: transitive
description:
name: image_picker_ios
sha256: b9c4a438a9ff4f60808c9cf0039b93a42bb6c2211ef6ebb647394b2b3fa84588
url: "https://pub.dev"
source: hosted
version: "0.8.13+6"
image_picker_linux:
dependency: transitive
description:
name: image_picker_linux
sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4"
url: "https://pub.dev"
source: hosted
version: "0.2.2"
image_picker_macos:
dependency: transitive
description:
name: image_picker_macos
sha256: "86f0f15a309de7e1a552c12df9ce5b59fe927e71385329355aec4776c6a8ec91"
url: "https://pub.dev"
source: hosted
version: "0.2.2+1"
image_picker_platform_interface: image_picker_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: image_picker_platform_interface name: image_picker_platform_interface
sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.11.1" version: "2.0.1"
image_picker_windows:
dependency: transitive
description:
name: image_picker_windows
sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae
url: "https://pub.dev"
source: hosted
version: "0.2.2"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev"
source: hosted
version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev"
source: hosted
version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.18" version: "0.12.10"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev"
source: hosted
version: "0.13.0"
meta: meta:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.17.0" version: "1.3.0"
mime:
dependency: transitive
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
motion_sensors: motion_sensors:
dependency: transitive dependency: transitive
description: description:
path: "." name: motion_sensors
ref: aves url: "https://pub.dartlang.org"
resolved-ref: "400fa42826e22a156b69a5d52926ce5681ab9b45" source: hosted
url: "https://github.com/deckerst/aves_panorama_motion_sensors.git"
source: git
version: "0.1.0" version: "0.1.0"
panorama: panorama:
dependency: "direct main" dependency: "direct main"
@ -271,108 +129,89 @@ packages:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "0.4.1" version: "0.4.0"
path: path:
dependency: transitive dependency: transitive
description: description:
name: path name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.8.0"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.0"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: plugin_platform_interface name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.8" version: "2.0.0"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.99"
source_span: source_span:
dependency: transitive dependency: transitive
description: description:
name: source_span name: source_span
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.2" version: "1.8.0"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.12.1" version: "1.10.0"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
name: stream_channel name: stream_channel
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.1.0"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
name: string_scanner name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.1" version: "1.1.0"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
name: term_glyph name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.2" version: "1.2.0"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.9" version: "0.2.19"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
name: typed_data name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "1.3.0"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
name: vector_math name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.1.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
url: "https://pub.dev"
source: hosted
version: "15.0.2"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
sdks: sdks:
dart: ">=3.11.0 <4.0.0" dart: ">=2.12.0 <3.0.0"
flutter: ">=3.38.0" flutter: ">=1.20.0"

View file

@ -1,23 +1,25 @@
name: example name: example
description: A new Flutter project. description: A new Flutter project.
version: 1.0.0+1 version: 1.0.0+1
publish_to: none
environment: environment:
sdk: ">=3.11.0 <4.0.0" sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
panorama: panorama:
path: ../ path: ../
image_picker: image_picker: ^0.7.2+1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter: flutter:
uses-material-design: true uses-material-design: true
assets: assets:
- assets/ - assets/
publish_to: none

View file

@ -1,25 +1,26 @@
import 'dart:async'; library panorama;
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'dart:async';
import 'dart:ui' as ui;
import 'dart:math' as math;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_cube/flutter_cube.dart'; import 'package:flutter_cube/flutter_cube.dart';
import 'package:motion_sensors/motion_sensors.dart'; import 'package:motion_sensors/motion_sensors.dart';
enum SensorControl { enum SensorControl {
/// No sensor used. /// No sensor used.
none, None,
/// Use gyroscope and accelerometer. /// Use gyroscope and accelerometer.
orientation, Orientation,
/// Use magnetometer and accelerometer. The longitude 0 points to north. /// Use magnetometer and accelerometer. The logitude 0 points to north.
absoluteOrientation, AbsoluteOrientation,
} }
class Panorama extends StatefulWidget { class Panorama extends StatefulWidget {
const Panorama({ Panorama({
super.key, Key? key,
this.latitude = 0, this.latitude = 0,
this.longitude = 0, this.longitude = 0,
this.zoom = 1.0, this.zoom = 1.0,
@ -29,12 +30,13 @@ class Panorama extends StatefulWidget {
this.maxLongitude = 180.0, this.maxLongitude = 180.0,
this.minZoom = 1.0, this.minZoom = 1.0,
this.maxZoom = 5.0, this.maxZoom = 5.0,
this.panInertia = 0.05, this.sensitivity = 1.0,
this.animSpeed = 0.0,
this.animReverse = true, this.animReverse = true,
this.latSegments = 32, this.latSegments = 32,
this.lonSegments = 64, this.lonSegments = 64,
this.interactive = true, this.interactive = true,
this.sensorControl = SensorControl.none, this.sensorControl = SensorControl.None,
this.croppedArea = const Rect.fromLTWH(0.0, 0.0, 1.0, 1.0), this.croppedArea = const Rect.fromLTWH(0.0, 0.0, 1.0, 1.0),
this.croppedFullWidth = 1.0, this.croppedFullWidth = 1.0,
this.croppedFullHeight = 1.0, this.croppedFullHeight = 1.0,
@ -46,7 +48,7 @@ class Panorama extends StatefulWidget {
this.onImageLoad, this.onImageLoad,
this.child, this.child,
this.hotspots, this.hotspots,
}); }) : super(key: key);
/// The initial latitude, in degrees, between -90 and 90. default to 0 (the vertical center of the image). /// The initial latitude, in degrees, between -90 and 90. default to 0 (the vertical center of the image).
final double latitude; final double latitude;
@ -69,14 +71,17 @@ class Panorama extends StatefulWidget {
/// The maximal longitude to show. default to 180.0 /// The maximal longitude to show. default to 180.0
final double maxLongitude; final double maxLongitude;
/// The minimal zoom. default to 1.0 /// The minimal zomm. default to 1.0
final double minZoom; final double minZoom;
/// The maximal zoom. default to 5.0 /// The maximal zomm. default to 5.0
final double maxZoom; final double maxZoom;
/// default to 0.05 /// The sensitivity of the gesture. default to 1.0
final double panInertia; final double sensitivity;
/// The Speed of rotation by animation. default to 0.0
final double animSpeed;
/// Reverse rotation when the current longitude reaches the minimal or maximum. default to true /// Reverse rotation when the current longitude reaches the minimal or maximum. default to true
final bool animReverse; final bool animReverse;
@ -127,34 +132,30 @@ class Panorama extends StatefulWidget {
final List<Hotspot>? hotspots; final List<Hotspot>? hotspots;
@override @override
State<Panorama> createState() => _PanoramaState(); _PanoramaState createState() => _PanoramaState();
} }
class _PanoramaState extends State<Panorama> with SingleTickerProviderStateMixin { class _PanoramaState extends State<Panorama> with SingleTickerProviderStateMixin {
Scene? scene; Scene? scene;
Object? surface; Object? surface;
late double latitudeRad; late double latitude;
late double longitudeRad; late double longitude;
double latitudeDelta = 0; double latitudeDelta = 0;
double longitudeDelta = 0; double longitudeDelta = 0;
double zoomDelta = 0; double zoomDelta = 0;
late Offset _lastFocalPoint; late Offset _lastFocalPoint;
double? _lastZoom; double? _lastZoom;
final double _animateDirection = 1.0; double _radius = 500;
double _dampingFactor = 0.05;
double _animateDirection = 1.0;
late AnimationController _controller; late AnimationController _controller;
double screenOrientationRad = 0.0; double screenOrientation = 0.0;
Vector3 orientation = Vector3(0, radians(90), 0); Vector3 orientation = Vector3(0, radians(90), 0);
StreamSubscription? _orientationSubscription; StreamSubscription? _orientationSubscription;
StreamSubscription? _screenOrientSubscription; StreamSubscription? _screenOrientSubscription;
late StreamController<Null> _streamController; late StreamController<Null> _streamController;
Stream<Null>? _stream; Stream<Null>? _stream;
ImageStream? _imageStream; ImageStream? _imageStream;
bool _scaling = false;
static const double _halfPi = math.pi * .5;
static const double _epsilon = .001;
static const double _radius = 500;
static const double _panReactivity = .8;
void _handleTapUp(TapUpDetails details) { void _handleTapUp(TapUpDetails details) {
final Vector3 o = positionToLatLon(details.localPosition.dx, details.localPosition.dy); final Vector3 o = positionToLatLon(details.localPosition.dx, details.localPosition.dy);
@ -179,71 +180,55 @@ class _PanoramaState extends State<Panorama> with SingleTickerProviderStateMixin
void _handleScaleStart(ScaleStartDetails details) { void _handleScaleStart(ScaleStartDetails details) {
_lastFocalPoint = details.localFocalPoint; _lastFocalPoint = details.localFocalPoint;
_lastZoom = null; _lastZoom = null;
_scaling = true;
} }
void _handleScaleUpdate(ScaleUpdateDetails details) { void _handleScaleUpdate(ScaleUpdateDetails details) {
final offset = details.localFocalPoint - _lastFocalPoint; final offset = details.localFocalPoint - _lastFocalPoint;
_lastFocalPoint = details.localFocalPoint; _lastFocalPoint = details.localFocalPoint;
_updatePositionDeltaForOffset(offset); latitudeDelta += widget.sensitivity * 0.5 * math.pi * offset.dy / scene!.camera.viewportHeight;
longitudeDelta -= widget.sensitivity * _animateDirection * 0.5 * math.pi * offset.dx / scene!.camera.viewportHeight;
final zoom = scene!.camera.zoom; if (_lastZoom == null) {
_lastZoom ??= zoom; _lastZoom = scene!.camera.zoom;
zoomDelta += _lastZoom! * details.scale - (zoom + zoomDelta); }
zoomDelta += _lastZoom! * details.scale - (scene!.camera.zoom + zoomDelta);
if (widget.sensorControl == SensorControl.none && !_controller.isAnimating) { if (widget.sensorControl == SensorControl.None && !_controller.isAnimating) {
_controller.reset(); _controller.reset();
_controller.forward(); if (widget.animSpeed != 0) {
_controller.repeat();
} else
_controller.forward();
} }
}
void _handleScaleEnd(ScaleEndDetails details) {
final offset = details.velocity.pixelsPerSecond / 10;
_updatePositionDeltaForOffset(offset);
_scaling = false;
}
void _updatePositionDeltaForOffset(ui.Offset offset) {
final camera = scene!.camera;
final sensitivity = 1 / camera.zoom;
final viewportHeight = camera.viewportHeight;
latitudeDelta += sensitivity * _halfPi * offset.dy / viewportHeight;
longitudeDelta -= sensitivity * _animateDirection * _halfPi * offset.dx / viewportHeight;
} }
void _updateView() { void _updateView() {
if (scene == null) return; if (scene == null) return;
// auto rotate
final camera = scene!.camera; longitudeDelta += 0.001 * widget.animSpeed;
final damping = _scaling ? _panReactivity : widget.panInertia;
// animate vertical rotating // animate vertical rotating
latitudeRad += latitudeDelta * damping; latitude += latitudeDelta * _dampingFactor * widget.sensitivity;
latitudeDelta *= 1 - damping; latitudeDelta *= 1 - _dampingFactor * widget.sensitivity;
// animate horizontal rotating // animate horizontal rotating
longitudeRad += _animateDirection * longitudeDelta * damping; longitude += _animateDirection * longitudeDelta * _dampingFactor * widget.sensitivity;
longitudeDelta *= 1 - damping; longitudeDelta *= 1 - _dampingFactor * widget.sensitivity;
// animate zomming
// animate zooming final double zoom = scene!.camera.zoom + zoomDelta * _dampingFactor;
final double zoom = camera.zoom + zoomDelta * damping; zoomDelta *= 1 - _dampingFactor;
zoomDelta *= 1 - damping; scene!.camera.zoom = zoom.clamp(widget.minZoom, widget.maxZoom);
camera.zoom = zoom.clamp(widget.minZoom, widget.maxZoom);
// stop animation if not needed // stop animation if not needed
if (latitudeDelta.abs() < _epsilon && longitudeDelta.abs() < _epsilon && zoomDelta.abs() < _epsilon) { if (latitudeDelta.abs() < 0.001 &&
if (widget.sensorControl == SensorControl.none && _controller.isAnimating) { longitudeDelta.abs() < 0.001 &&
_controller.stop(); zoomDelta.abs() < 0.001) {
} if (widget.sensorControl == SensorControl.None &&
widget.animSpeed == 0 &&
_controller.isAnimating) _controller.stop();
} }
// rotate for screen orientation // rotate for screen orientation
Quaternion q = Quaternion.axisAngle(Vector3(0, 0, 1), screenOrientationRad); Quaternion q = Quaternion.axisAngle(Vector3(0, 0, 1), screenOrientation);
// rotate for device orientation // rotate for device orientation
q *= Quaternion.euler(-orientation.z, -orientation.y, -orientation.x); q *= Quaternion.euler(-orientation.z, -orientation.y, -orientation.x);
// rotate to latitude zero // rotate to latitude zero
q *= Quaternion.axisAngle(Vector3(1, 0, 0), _halfPi); q *= Quaternion.axisAngle(Vector3(1, 0, 0), math.pi * 0.5);
// check and limit the rotation range // check and limit the rotation range
Vector3 o = quaternionToOrientation(q); Vector3 o = quaternionToOrientation(q);
@ -253,11 +238,18 @@ class _PanoramaState extends State<Panorama> with SingleTickerProviderStateMixin
final double maxLon = radians(widget.maxLongitude); final double maxLon = radians(widget.maxLongitude);
final double lat = (-o.y).clamp(minLat, maxLat); final double lat = (-o.y).clamp(minLat, maxLat);
final double lon = o.x.clamp(minLon, maxLon); final double lon = o.x.clamp(minLon, maxLon);
if (lat + latitudeRad < minLat) latitudeRad = minLat - lat; if (lat + latitude < minLat) latitude = minLat - lat;
if (lat + latitudeRad > maxLat) latitudeRad = maxLat - lat; if (lat + latitude > maxLat) latitude = maxLat - lat;
if (maxLon - minLon < math.pi * 2) { if (maxLon - minLon < math.pi * 2) {
if (lon + longitudeRad < minLon || lon + longitudeRad > maxLon) { if (lon + longitude < minLon || lon + longitude > maxLon) {
longitudeRad = (lon + longitudeRad < minLon ? minLon : maxLon) - lon; longitude = (lon + longitude < minLon ? minLon : maxLon) - lon;
// reverse rotation when reaching the boundary
if (widget.animSpeed != 0) {
if (widget.animReverse)
_animateDirection *= -1.0;
else
_controller.stop();
}
} }
} }
o.x = lon; o.x = lon;
@ -265,17 +257,17 @@ class _PanoramaState extends State<Panorama> with SingleTickerProviderStateMixin
q = orientationToQuaternion(o); q = orientationToQuaternion(o);
// rotate to longitude zero // rotate to longitude zero
q *= Quaternion.axisAngle(Vector3(0, 1, 0), -_halfPi); q *= Quaternion.axisAngle(Vector3(0, 1, 0), -math.pi * 0.5);
// rotate around the global Y axis // rotate around the global Y axis
q *= Quaternion.axisAngle(Vector3(0, 1, 0), longitudeRad); q *= Quaternion.axisAngle(Vector3(0, 1, 0), longitude);
// rotate around the local X axis // rotate around the local X axis
q = Quaternion.axisAngle(Vector3(1, 0, 0), -latitudeRad) * q; q = Quaternion.axisAngle(Vector3(1, 0, 0), -latitude) * q;
o = quaternionToOrientation(q * Quaternion.axisAngle(Vector3(0, 1, 0), _halfPi)); 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)); widget.onViewChanged?.call(degrees(o.x), degrees(-o.y), degrees(o.z));
q.rotate(camera.target..setFrom(Vector3(0, 0, -_radius))); q.rotate(scene!.camera.target..setFrom(Vector3(0, 0, -_radius)));
q.rotate(camera.up..setFrom(Vector3(0, 1, 0))); q.rotate(scene!.camera.up..setFrom(Vector3(0, 1, 0)));
scene!.update(); scene!.update();
_streamController.add(null); _streamController.add(null);
} }
@ -283,27 +275,25 @@ class _PanoramaState extends State<Panorama> with SingleTickerProviderStateMixin
void _updateSensorControl() { void _updateSensorControl() {
_orientationSubscription?.cancel(); _orientationSubscription?.cancel();
switch (widget.sensorControl) { switch (widget.sensorControl) {
case SensorControl.orientation: case SensorControl.Orientation:
motionSensors.orientationUpdateInterval = Duration.microsecondsPerSecond ~/ 60; motionSensors.orientationUpdateInterval = Duration.microsecondsPerSecond ~/ 60;
_orientationSubscription = motionSensors.orientation.listen((event) { _orientationSubscription = motionSensors.orientation.listen((OrientationEvent event) {
orientation.setValues(event.yaw, event.pitch, event.roll); orientation.setValues(event.yaw, event.pitch, event.roll);
_updateView();
}); });
break; break;
case SensorControl.absoluteOrientation: case SensorControl.AbsoluteOrientation:
motionSensors.absoluteOrientationUpdateInterval = Duration.microsecondsPerSecond ~/ 60; motionSensors.absoluteOrientationUpdateInterval = Duration.microsecondsPerSecond ~/ 60;
_orientationSubscription = motionSensors.absoluteOrientation.listen((event) { _orientationSubscription = motionSensors.absoluteOrientation.listen((AbsoluteOrientationEvent event) {
orientation.setValues(event.yaw, event.pitch, event.roll); orientation.setValues(event.yaw, event.pitch, event.roll);
_updateView();
}); });
break; break;
default: default:
} }
_screenOrientSubscription?.cancel(); _screenOrientSubscription?.cancel();
if (widget.sensorControl != SensorControl.none) { if (widget.sensorControl != SensorControl.None) {
_screenOrientSubscription = motionSensors.screenOrientation.listen((event) { _screenOrientSubscription = motionSensors.screenOrientation.listen((ScreenOrientationEvent event) {
screenOrientationRad = radians(event.angle!); screenOrientation = radians(event.angle!);
}); });
} }
} }
@ -319,28 +309,20 @@ class _PanoramaState extends State<Panorama> with SingleTickerProviderStateMixin
void _loadTexture(ImageProvider? provider) { void _loadTexture(ImageProvider? provider) {
if (provider == null) return; if (provider == null) return;
_imageStream?.removeListener(ImageStreamListener(_updateTexture)); _imageStream?.removeListener(ImageStreamListener(_updateTexture));
_imageStream = provider.resolve(const ImageConfiguration()); _imageStream = provider.resolve(ImageConfiguration());
ImageStreamListener listener = ImageStreamListener(_updateTexture); ImageStreamListener listener = ImageStreamListener(_updateTexture);
_imageStream!.addListener(listener); _imageStream!.addListener(listener);
} }
void _onSceneCreated(Scene scene) { void _onSceneCreated(Scene scene) {
this.scene = scene; this.scene = scene;
final camera = scene.camera; scene.camera.near = 1.0;
camera.near = 1.0; scene.camera.far = _radius + 1.0;
camera.far = _radius + 1.0; scene.camera.fov = 75;
camera.fov = 75; scene.camera.zoom = widget.zoom;
camera.zoom = widget.zoom; scene.camera.position.setFrom(Vector3(0, 0, 0.1));
camera.position.setFrom(Vector3(0, 0, 0.1));
if (widget.child != null) { if (widget.child != null) {
final Mesh mesh = generateSphereMesh( final Mesh mesh = generateSphereMesh(radius: _radius, latSegments: widget.latSegments, lonSegments: widget.lonSegments, croppedArea: widget.croppedArea, croppedFullWidth: widget.croppedFullWidth, croppedFullHeight: widget.croppedFullHeight);
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); surface = Object(name: 'surface', mesh: mesh, backfaceCulling: false);
_loadTexture(widget.child!.image); _loadTexture(widget.child!.image);
scene.world.add(surface!); scene.world.add(surface!);
@ -353,52 +335,48 @@ class _PanoramaState extends State<Panorama> with SingleTickerProviderStateMixin
} }
Vector3 positionToLatLon(double x, double y) { Vector3 positionToLatLon(double x, double y) {
final camera = scene!.camera;
// transform viewport coordinate to NDC, values between -1 and 1 // transform viewport coordinate to NDC, values between -1 and 1
final v = Vector4(2.0 * x / camera.viewportWidth - 1.0, 1.0 - 2.0 * y / camera.viewportHeight, 1.0, 1.0); final Vector4 v = Vector4(2.0 * x / scene!.camera.viewportWidth - 1.0, 1.0 - 2.0 * y / scene!.camera.viewportHeight, 1.0, 1.0);
// create projection matrix // create projection matrix
final m = camera.projectionMatrix * camera.lookAtMatrix; final Matrix4 m = scene!.camera.projectionMatrix * scene!.camera.lookAtMatrix;
// apply inverse projection matrix // apply inversed projection matrix
m.invert(); m.invert();
v.applyMatrix4(m); v.applyMatrix4(m);
// apply perspective division // apply perspective division
v.scale(1 / v.w); v.scale(1 / v.w);
// get rotation from two vectors // get rotation from two vectors
final q = Quaternion.fromTwoVectors(v.xyz, Vector3(0.0, 0.0, -_radius)); final Quaternion q = Quaternion.fromTwoVectors(v.xyz, Vector3(0.0, 0.0, -_radius));
// get euler angles from rotation // get euler angles from rotation
return quaternionToOrientation(q * Quaternion.axisAngle(Vector3(0, 1, 0), _halfPi)); return quaternionToOrientation(q * Quaternion.axisAngle(Vector3(0, 1, 0), math.pi * 0.5));
} }
Vector3 positionFromLatLon(double lat, double lon) { Vector3 positionFromLatLon(double lat, double lon) {
final camera = scene!.camera;
// create projection matrix // create projection matrix
final Matrix4 m = camera.projectionMatrix * camera.lookAtMatrix * matrixFromLatLon(lat, lon); final Matrix4 m = scene!.camera.projectionMatrix * scene!.camera.lookAtMatrix * matrixFromLatLon(lat, lon);
// apply projection matrix // apply projection matrix
final Vector4 v = Vector4(0.0, 0.0, -_radius, 1.0)..applyMatrix4(m); final Vector4 v = Vector4(0.0, 0.0, -_radius, 1.0)..applyMatrix4(m);
// apply perspective division and transform NDC to the viewport coordinate // apply perspective division and transform NDC to the viewport coordinate
return Vector3( return Vector3(
(1.0 + v.x / v.w) * camera.viewportWidth / 2, (1.0 + v.x / v.w) * scene!.camera.viewportWidth / 2,
(1.0 - v.y / v.w) * camera.viewportHeight / 2, (1.0 - v.y / v.w) * scene!.camera.viewportHeight / 2,
v.z, v.z,
); );
} }
Widget buildHotspotWidgets(List<Hotspot>? hotspots) { Widget buildHotspotWidgets(List<Hotspot>? hotspots) {
final widgets = <Widget>[]; final List<Widget> widgets = <Widget>[];
if (hotspots != null && scene != null) { if (hotspots != null && scene != null) {
for (Hotspot hotspot in hotspots) { for (Hotspot hotspot in hotspots) {
final pos = positionFromLatLon(hotspot.latitude, hotspot.longitude); final Vector3 pos = positionFromLatLon(hotspot.latitude, hotspot.longitude);
final origin = Offset(hotspot.width * hotspot.origin.dx, hotspot.height * hotspot.origin.dy); final Offset orgin = Offset(hotspot.width * hotspot.orgin.dx, hotspot.height * hotspot.orgin.dy);
final transform = scene!.camera.lookAtMatrix * matrixFromLatLon(hotspot.latitude, hotspot.longitude); final Matrix4 transform = scene!.camera.lookAtMatrix * matrixFromLatLon(hotspot.latitude, hotspot.longitude);
final child = Positioned( final Widget child = Positioned(
left: pos.x - origin.dx, left: pos.x - orgin.dx,
top: pos.y - origin.dy, top: pos.y - orgin.dy,
width: hotspot.width, width: hotspot.width,
height: hotspot.height, height: hotspot.height,
child: Transform( child: Transform(
origin: origin, origin: orgin,
transform: transform..invert(), transform: transform..invert(),
child: Offstage( child: Offstage(
offstage: pos.z < 0, offstage: pos.z < 0,
@ -415,15 +393,15 @@ class _PanoramaState extends State<Panorama> with SingleTickerProviderStateMixin
@override @override
void initState() { void initState() {
super.initState(); super.initState();
latitudeRad = radians(widget.latitude); latitude = degrees(widget.latitude);
longitudeRad = radians(widget.longitude); longitude = degrees(widget.longitude);
_streamController = StreamController<Null>.broadcast(); _streamController = StreamController<Null>.broadcast();
_stream = _streamController.stream; _stream = _streamController.stream;
_updateSensorControl(); _updateSensorControl();
_controller = AnimationController(duration: const Duration(milliseconds: 60000), vsync: this)..addListener(_updateView); _controller = AnimationController(duration: Duration(milliseconds: 60000), vsync: this)..addListener(_updateView);
if (widget.sensorControl != SensorControl.none) _controller.repeat(); if (widget.sensorControl != SensorControl.None || widget.animSpeed != 0) _controller.repeat();
} }
@override @override
@ -440,19 +418,8 @@ class _PanoramaState extends State<Panorama> with SingleTickerProviderStateMixin
void didUpdateWidget(Panorama oldWidget) { void didUpdateWidget(Panorama oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (surface == null) return; if (surface == null) return;
if (widget.latSegments != oldWidget.latSegments || if (widget.latSegments != oldWidget.latSegments || widget.lonSegments != oldWidget.lonSegments || widget.croppedArea != oldWidget.croppedArea || widget.croppedFullWidth != oldWidget.croppedFullWidth || widget.croppedFullHeight != oldWidget.croppedFullHeight) {
widget.lonSegments != oldWidget.lonSegments || surface!.mesh = generateSphereMesh(radius: _radius, latSegments: widget.latSegments, lonSegments: widget.lonSegments, croppedArea: widget.croppedArea, croppedFullWidth: widget.croppedFullWidth, croppedFullHeight: widget.croppedFullHeight);
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) { if (widget.child?.image != oldWidget.child?.image) {
_loadTexture(widget.child?.image); _loadTexture(widget.child?.image);
@ -469,7 +436,7 @@ class _PanoramaState extends State<Panorama> with SingleTickerProviderStateMixin
Cube(interactive: false, onSceneCreated: _onSceneCreated), Cube(interactive: false, onSceneCreated: _onSceneCreated),
StreamBuilder( StreamBuilder(
stream: _stream, stream: _stream,
builder: (context, snapshot) { builder: (BuildContext context, AsyncSnapshot snapshot) {
return buildHotspotWidgets(widget.hotspots); return buildHotspotWidgets(widget.hotspots);
}, },
), ),
@ -480,7 +447,6 @@ class _PanoramaState extends State<Panorama> with SingleTickerProviderStateMixin
? GestureDetector( ? GestureDetector(
onScaleStart: _handleScaleStart, onScaleStart: _handleScaleStart,
onScaleUpdate: _handleScaleUpdate, onScaleUpdate: _handleScaleUpdate,
onScaleEnd: _handleScaleEnd,
onTapUp: widget.onTap == null ? null : _handleTapUp, onTapUp: widget.onTap == null ? null : _handleTapUp,
onLongPressStart: widget.onLongPressStart == null ? null : _handleLongPressStart, onLongPressStart: widget.onLongPressStart == null ? null : _handleLongPressStart,
onLongPressMoveUpdate: widget.onLongPressMoveUpdate == null ? null : _handleLongPressMoveUpdate, onLongPressMoveUpdate: widget.onLongPressMoveUpdate == null ? null : _handleLongPressMoveUpdate,
@ -496,7 +462,7 @@ class Hotspot {
this.name, this.name,
this.latitude = 0.0, this.latitude = 0.0,
this.longitude = 0.0, this.longitude = 0.0,
this.origin = const Offset(0.5, 0.5), this.orgin = const Offset(0.5, 0.5),
this.width = 32.0, this.width = 32.0,
this.height = 32.0, this.height = 32.0,
this.widget, this.widget,
@ -511,8 +477,8 @@ class Hotspot {
/// The initial longitude, in degrees, between -180 and 180. /// The initial longitude, in degrees, between -180 and 180.
final double longitude; final double longitude;
/// The local origin of this hotspot. Default is Offset(0.5, 0.5). /// The local orgin of this hotspot. Default is Offset(0.5, 0.5).
final Offset origin; final Offset orgin;
// The width of widget. Default is 32.0 // The width of widget. Default is 32.0
double width; double width;

View file

@ -5,50 +5,51 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: async name: async
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.13.0" version: "2.5.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
name: boolean_selector name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.2" version: "2.1.0"
characters: characters:
dependency: transitive dependency: transitive
description: description:
name: characters name: characters
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.1" version: "1.1.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
clock: clock:
dependency: transitive dependency: transitive
description: description:
name: clock name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.2" version: "1.1.0"
collection: collection:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.19.1" version: "1.15.0"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
name: fake_async name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.3" version: "1.2.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -58,165 +59,103 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_cube name: flutter_cube
sha256: "71cf679a251166eb97f86751c56582b09abdbf859485fbf60524948813914c3b" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.1" version: "0.1.1"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
url: "https://pub.dev"
source: hosted
version: "6.0.0"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev"
source: hosted
version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev"
source: hosted
version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
lints:
dependency: transitive
description:
name: lints
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
url: "https://pub.dev"
source: hosted
version: "6.1.0"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.18" version: "0.12.10"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev"
source: hosted
version: "0.13.0"
meta: meta:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.17.0" version: "1.3.0"
motion_sensors: motion_sensors:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." name: motion_sensors
ref: aves url: "https://pub.dartlang.org"
resolved-ref: "400fa42826e22a156b69a5d52926ce5681ab9b45" source: hosted
url: "https://github.com/deckerst/aves_panorama_motion_sensors.git"
source: git
version: "0.1.0" version: "0.1.0"
path: path:
dependency: transitive dependency: transitive
description: description:
name: path name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.8.0"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.99"
source_span: source_span:
dependency: transitive dependency: transitive
description: description:
name: source_span name: source_span
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.2" version: "1.8.0"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.12.1" version: "1.10.0"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
name: stream_channel name: stream_channel
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.1.0"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
name: string_scanner name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.1" version: "1.1.0"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
name: term_glyph name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.2" version: "1.2.0"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.9" version: "0.2.19"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
name: vector_math name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dartlang.org"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.1.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
url: "https://pub.dev"
source: hosted
version: "15.0.2"
sdks: sdks:
dart: ">=3.11.0 <4.0.0" dart: ">=2.12.0 <3.0.0"
flutter: ">=3.18.0-18.0.pre.54" flutter: ">=1.10.0"

View file

@ -1,25 +1,19 @@
name: panorama name: panorama
description: Panorama -- A 360-degree panorama viewer. description: Panorama -- A 360-degree panorama viewer.
homepage: https://github.com/zesage/panorama
publish_to: none
version: 0.4.1 version: 0.4.1
homepage: https://github.com/zesage/panorama
environment: environment:
sdk: ">=3.11.0 <4.0.0" sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_cube: flutter_cube: ^0.1.1
motion_sensors: motion_sensors: ^0.1.0
git:
url: https://github.com/deckerst/aves_panorama_motion_sensors.git
ref: aves
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints:
flutter: flutter: