diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..7abac96
Binary files /dev/null and b/.DS_Store differ
diff --git a/README.md b/README.md
index 32e4825..58de294 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,70 @@
-# watermelon
-Flutter版合成大西瓜
+
+
+
+
+合成大瓜
+
+
+
+
+
+# 简介
+
+合成大瓜是使用[Flutter](https://flutter.dev/)+[Flame](https://flame-engine.org/)+[Forge2D](https://github.com/flame-engine/forge2d)开发的一个开源小游戏。
+
+我在[微伞小游戏](http://www.wesane.com/)开发的[《合成大西瓜》](http://www.wesane.com/game/654/)的基础上将其移植到 Flutter 平台,并添加了以下新特性:
+
+- 支持自定义背景图
+- 支持重力感应操控
+- 支持修改图片素材
+- 支持反向合成小瓜
+- 支持只生成小/大瓜
+- 内置多套游戏主题(水果/表情/校徽)
+
+# 预览
+
+| ![](screenshots/play.jpg) | ![](screenshots/win.jpg) | ![](screenshots/inner.jpg) | ![](screenshots/img.jpg) |
+| :------------: | :------------: | :------------: | :------------: |
+
+
+# 下载地址
+
+网页版:http://v.idoo.top/mix
+
+安卓/iOS:https://www.pgyer.com/Dagua
+
+***PS:iOS版安装包需要自签才能使用***
+
+
+# 开源地址
+
+本项目开源地址 https://github.com/idootop/fallDown , 欢迎Star/PR ^^
+
+Web端生成
+```shell
+flutter build web --release
+```
+
+Android端生成
+```shell
+flutter build apk --split-per-abi
+```
+
+PS:亦可支持iOS,Mac,Windows,Linux端,请自行打包
+
+# 免责声明
+
+合成大瓜为免费开源软件,仅供学习交流,你可以非商业性地下载、安装、复制和散发本软件产品。
+
+本游戏的创意玩法来自[《合成大西瓜》](http://www.wesane.com/game/654/),应用内的素材收集自互联网,部分图片、音频素材版权归[微伞小游戏](http://www.wesane.com/)所有,侵删。
+
+# 鸣谢
+
+Flutter https://flutter.dev/
+
+Flame https://flame-engine.org/
+
+Forge2D https://github.com/flame-engine/forge2d
+
+合成大西瓜 http://www.wesane.com/game/654/
+
diff --git a/analysis_options.yaml b/analysis_options.yaml
new file mode 100644
index 0000000..108d105
--- /dev/null
+++ b/analysis_options.yaml
@@ -0,0 +1 @@
+include: package:pedantic/analysis_options.yaml
diff --git a/android/.gitignore b/android/.gitignore
new file mode 100644
index 0000000..0a741cb
--- /dev/null
+++ b/android/.gitignore
@@ -0,0 +1,11 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+key.properties
diff --git a/android/app/build.gradle b/android/app/build.gradle
new file mode 100644
index 0000000..9f55989
--- /dev/null
+++ b/android/app/build.gradle
@@ -0,0 +1,62 @@
+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 30
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ lintOptions {
+ disable 'InvalidPackage'
+ }
+
+ defaultConfig {
+ applicationId "cat.love.mix" //bundle id
+ minSdkVersion 16
+ targetSdkVersion 30
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ 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"
+}
diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro
new file mode 100644
index 0000000..bccd023
--- /dev/null
+++ b/android/app/proguard-rules.pro
@@ -0,0 +1,7 @@
+#Flutter Wrapper
+-keep class io.flutter.app.** { *; }
+-keep class io.flutter.plugin.** { *; }
+-keep class io.flutter.util.** { *; }
+-keep class io.flutter.view.** { *; }
+-keep class io.flutter.** { *; }
+-keep class io.flutter.plugins.** { *; }
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..61e728a
--- /dev/null
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..635adf5
--- /dev/null
+++ b/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/kotlin/com/example/watermelon/MainActivity.kt b/android/app/src/main/kotlin/com/example/watermelon/MainActivity.kt
new file mode 100644
index 0000000..ed51515
--- /dev/null
+++ b/android/app/src/main/kotlin/com/example/watermelon/MainActivity.kt
@@ -0,0 +1,6 @@
+package cat.love.mix
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 0000000..f74085f
--- /dev/null
+++ b/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..304732f
--- /dev/null
+++ b/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..1e4baa3
Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..13f0678
Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..b518014
Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..2d898d9
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..3c992b0
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000..449a9f9
--- /dev/null
+++ b/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..d74aa35
--- /dev/null
+++ b/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000..61e728a
--- /dev/null
+++ b/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 0000000..3100ad2
--- /dev/null
+++ b/android/build.gradle
@@ -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
+}
diff --git a/android/gradle.properties b/android/gradle.properties
new file mode 100644
index 0000000..a673820
--- /dev/null
+++ b/android/gradle.properties
@@ -0,0 +1,4 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
+android.enableR8=true
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..296b146
--- /dev/null
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
diff --git a/android/settings.gradle b/android/settings.gradle
new file mode 100644
index 0000000..44e62bc
--- /dev/null
+++ b/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/android/watermelon_android.iml b/android/watermelon_android.iml
new file mode 100644
index 0000000..1899969
--- /dev/null
+++ b/android/watermelon_android.iml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/.DS_Store b/assets/.DS_Store
new file mode 100644
index 0000000..feabc32
Binary files /dev/null and b/assets/.DS_Store differ
diff --git a/assets/audio/boom.mp3 b/assets/audio/boom.mp3
new file mode 100644
index 0000000..1d92ba7
Binary files /dev/null and b/assets/audio/boom.mp3 differ
diff --git a/assets/audio/boom1.mp3 b/assets/audio/boom1.mp3
new file mode 100644
index 0000000..113cce0
Binary files /dev/null and b/assets/audio/boom1.mp3 differ
diff --git a/assets/audio/boom2.mp3 b/assets/audio/boom2.mp3
new file mode 100644
index 0000000..021b357
Binary files /dev/null and b/assets/audio/boom2.mp3 differ
diff --git a/assets/audio/cheer.mp3 b/assets/audio/cheer.mp3
new file mode 100644
index 0000000..9e98cd9
Binary files /dev/null and b/assets/audio/cheer.mp3 differ
diff --git a/assets/audio/coin.wav b/assets/audio/coin.wav
new file mode 100644
index 0000000..689ad63
Binary files /dev/null and b/assets/audio/coin.wav differ
diff --git a/assets/audio/fall.mp3 b/assets/audio/fall.mp3
new file mode 100644
index 0000000..d3c44bc
Binary files /dev/null and b/assets/audio/fall.mp3 differ
diff --git a/assets/audio/mix.wav b/assets/audio/mix.wav
new file mode 100644
index 0000000..ab30f2f
Binary files /dev/null and b/assets/audio/mix.wav differ
diff --git a/assets/images/.DS_Store b/assets/images/.DS_Store
new file mode 100644
index 0000000..e6f579c
Binary files /dev/null and b/assets/images/.DS_Store differ
diff --git a/assets/images/985/1.png b/assets/images/985/1.png
new file mode 100644
index 0000000..6695969
Binary files /dev/null and b/assets/images/985/1.png differ
diff --git a/assets/images/985/10.png b/assets/images/985/10.png
new file mode 100644
index 0000000..562d0e3
Binary files /dev/null and b/assets/images/985/10.png differ
diff --git a/assets/images/985/11.png b/assets/images/985/11.png
new file mode 100644
index 0000000..ee04d4e
Binary files /dev/null and b/assets/images/985/11.png differ
diff --git a/assets/images/985/2.png b/assets/images/985/2.png
new file mode 100644
index 0000000..59ab6a4
Binary files /dev/null and b/assets/images/985/2.png differ
diff --git a/assets/images/985/3.png b/assets/images/985/3.png
new file mode 100644
index 0000000..6946d3b
Binary files /dev/null and b/assets/images/985/3.png differ
diff --git a/assets/images/985/4.png b/assets/images/985/4.png
new file mode 100644
index 0000000..e032409
Binary files /dev/null and b/assets/images/985/4.png differ
diff --git a/assets/images/985/5.png b/assets/images/985/5.png
new file mode 100644
index 0000000..28ef61d
Binary files /dev/null and b/assets/images/985/5.png differ
diff --git a/assets/images/985/6.png b/assets/images/985/6.png
new file mode 100644
index 0000000..f086ff3
Binary files /dev/null and b/assets/images/985/6.png differ
diff --git a/assets/images/985/7.png b/assets/images/985/7.png
new file mode 100644
index 0000000..e26e792
Binary files /dev/null and b/assets/images/985/7.png differ
diff --git a/assets/images/985/8.png b/assets/images/985/8.png
new file mode 100644
index 0000000..b731ac3
Binary files /dev/null and b/assets/images/985/8.png differ
diff --git a/assets/images/985/9.png b/assets/images/985/9.png
new file mode 100644
index 0000000..315e637
Binary files /dev/null and b/assets/images/985/9.png differ
diff --git a/assets/images/bg.png b/assets/images/bg.png
new file mode 100644
index 0000000..6b9b12f
Binary files /dev/null and b/assets/images/bg.png differ
diff --git a/assets/images/dead_line.png b/assets/images/dead_line.png
new file mode 100644
index 0000000..ebf5ddb
Binary files /dev/null and b/assets/images/dead_line.png differ
diff --git a/assets/images/flame.png b/assets/images/flame.png
new file mode 100644
index 0000000..5addaf6
Binary files /dev/null and b/assets/images/flame.png differ
diff --git a/assets/images/friuts/1.png b/assets/images/friuts/1.png
new file mode 100644
index 0000000..815692f
Binary files /dev/null and b/assets/images/friuts/1.png differ
diff --git a/assets/images/friuts/10.png b/assets/images/friuts/10.png
new file mode 100644
index 0000000..935d711
Binary files /dev/null and b/assets/images/friuts/10.png differ
diff --git a/assets/images/friuts/11.png b/assets/images/friuts/11.png
new file mode 100644
index 0000000..7596c1d
Binary files /dev/null and b/assets/images/friuts/11.png differ
diff --git a/assets/images/friuts/2.png b/assets/images/friuts/2.png
new file mode 100644
index 0000000..2e1d78d
Binary files /dev/null and b/assets/images/friuts/2.png differ
diff --git a/assets/images/friuts/3.png b/assets/images/friuts/3.png
new file mode 100644
index 0000000..42460a6
Binary files /dev/null and b/assets/images/friuts/3.png differ
diff --git a/assets/images/friuts/4.png b/assets/images/friuts/4.png
new file mode 100644
index 0000000..3a4536a
Binary files /dev/null and b/assets/images/friuts/4.png differ
diff --git a/assets/images/friuts/5.png b/assets/images/friuts/5.png
new file mode 100644
index 0000000..286f0de
Binary files /dev/null and b/assets/images/friuts/5.png differ
diff --git a/assets/images/friuts/6.png b/assets/images/friuts/6.png
new file mode 100644
index 0000000..357f310
Binary files /dev/null and b/assets/images/friuts/6.png differ
diff --git a/assets/images/friuts/7.png b/assets/images/friuts/7.png
new file mode 100644
index 0000000..a67f47b
Binary files /dev/null and b/assets/images/friuts/7.png differ
diff --git a/assets/images/friuts/8.png b/assets/images/friuts/8.png
new file mode 100644
index 0000000..5ccc376
Binary files /dev/null and b/assets/images/friuts/8.png differ
diff --git a/assets/images/friuts/9.png b/assets/images/friuts/9.png
new file mode 100644
index 0000000..83c7582
Binary files /dev/null and b/assets/images/friuts/9.png differ
diff --git a/assets/images/qq/1.png b/assets/images/qq/1.png
new file mode 100644
index 0000000..1c12215
Binary files /dev/null and b/assets/images/qq/1.png differ
diff --git a/assets/images/qq/10.png b/assets/images/qq/10.png
new file mode 100644
index 0000000..ed9c3c8
Binary files /dev/null and b/assets/images/qq/10.png differ
diff --git a/assets/images/qq/11.png b/assets/images/qq/11.png
new file mode 100644
index 0000000..d9ea507
Binary files /dev/null and b/assets/images/qq/11.png differ
diff --git a/assets/images/qq/2.png b/assets/images/qq/2.png
new file mode 100644
index 0000000..efaee49
Binary files /dev/null and b/assets/images/qq/2.png differ
diff --git a/assets/images/qq/3.png b/assets/images/qq/3.png
new file mode 100644
index 0000000..4b1e5be
Binary files /dev/null and b/assets/images/qq/3.png differ
diff --git a/assets/images/qq/4.png b/assets/images/qq/4.png
new file mode 100644
index 0000000..7a56fe0
Binary files /dev/null and b/assets/images/qq/4.png differ
diff --git a/assets/images/qq/5.png b/assets/images/qq/5.png
new file mode 100644
index 0000000..30d8074
Binary files /dev/null and b/assets/images/qq/5.png differ
diff --git a/assets/images/qq/6.png b/assets/images/qq/6.png
new file mode 100644
index 0000000..ef5f9dc
Binary files /dev/null and b/assets/images/qq/6.png differ
diff --git a/assets/images/qq/7.png b/assets/images/qq/7.png
new file mode 100644
index 0000000..167c686
Binary files /dev/null and b/assets/images/qq/7.png differ
diff --git a/assets/images/qq/8.png b/assets/images/qq/8.png
new file mode 100644
index 0000000..62b9e76
Binary files /dev/null and b/assets/images/qq/8.png differ
diff --git a/assets/images/qq/9.png b/assets/images/qq/9.png
new file mode 100644
index 0000000..1b3060c
Binary files /dev/null and b/assets/images/qq/9.png differ
diff --git a/assets/images/qr.jpg b/assets/images/qr.jpg
new file mode 100644
index 0000000..87c3170
Binary files /dev/null and b/assets/images/qr.jpg differ
diff --git a/assets/images/setting.png b/assets/images/setting.png
new file mode 100644
index 0000000..ddc4462
Binary files /dev/null and b/assets/images/setting.png differ
diff --git a/assets/images/shandong/1.png b/assets/images/shandong/1.png
new file mode 100644
index 0000000..0ce71da
Binary files /dev/null and b/assets/images/shandong/1.png differ
diff --git a/assets/images/shandong/10.png b/assets/images/shandong/10.png
new file mode 100644
index 0000000..6946d3b
Binary files /dev/null and b/assets/images/shandong/10.png differ
diff --git a/assets/images/shandong/11.png b/assets/images/shandong/11.png
new file mode 100644
index 0000000..f51c28e
Binary files /dev/null and b/assets/images/shandong/11.png differ
diff --git a/assets/images/shandong/2.png b/assets/images/shandong/2.png
new file mode 100644
index 0000000..469bc0e
Binary files /dev/null and b/assets/images/shandong/2.png differ
diff --git a/assets/images/shandong/3.png b/assets/images/shandong/3.png
new file mode 100644
index 0000000..bac0885
Binary files /dev/null and b/assets/images/shandong/3.png differ
diff --git a/assets/images/shandong/4.png b/assets/images/shandong/4.png
new file mode 100644
index 0000000..9bbb334
Binary files /dev/null and b/assets/images/shandong/4.png differ
diff --git a/assets/images/shandong/5.png b/assets/images/shandong/5.png
new file mode 100644
index 0000000..4f37185
Binary files /dev/null and b/assets/images/shandong/5.png differ
diff --git a/assets/images/shandong/6.png b/assets/images/shandong/6.png
new file mode 100644
index 0000000..91b1fb5
Binary files /dev/null and b/assets/images/shandong/6.png differ
diff --git a/assets/images/shandong/7.png b/assets/images/shandong/7.png
new file mode 100644
index 0000000..ff9112f
Binary files /dev/null and b/assets/images/shandong/7.png differ
diff --git a/assets/images/shandong/8.png b/assets/images/shandong/8.png
new file mode 100644
index 0000000..8515027
Binary files /dev/null and b/assets/images/shandong/8.png differ
diff --git a/assets/images/shandong/9.png b/assets/images/shandong/9.png
new file mode 100644
index 0000000..6695969
Binary files /dev/null and b/assets/images/shandong/9.png differ
diff --git a/assets/images/shine.png b/assets/images/shine.png
new file mode 100644
index 0000000..1e63792
Binary files /dev/null and b/assets/images/shine.png differ
diff --git a/flutter-plugins b/flutter-plugins
new file mode 100644
index 0000000..9d0adaf
--- /dev/null
+++ b/flutter-plugins
@@ -0,0 +1,17 @@
+# This is a generated file; do not edit or check into version control.
+audioplayers=/Users/mac/dev/flutter/.pub-cache/hosted/pub.flutter-io.cn/audioplayers-0.15.1/
+flutter_plugin_android_lifecycle=/Users/mac/dev/flutter/.pub-cache/hosted/pub.flutter-io.cn/flutter_plugin_android_lifecycle-1.0.11/
+image_picker=/Users/mac/dev/flutter/.pub-cache/hosted/pub.flutter-io.cn/image_picker-0.6.7+22/
+image_picker_web_redux=/Users/mac/dev/flutter/.pub-cache/hosted/pub.flutter-io.cn/image_picker_web_redux-1.1.3/
+path_provider=/Users/mac/dev/flutter/.pub-cache/hosted/pub.flutter-io.cn/path_provider-1.6.27/
+path_provider_linux=/Users/mac/dev/flutter/.pub-cache/hosted/pub.flutter-io.cn/path_provider_linux-0.0.1+2/
+path_provider_macos=/Users/mac/dev/flutter/.pub-cache/hosted/pub.flutter-io.cn/path_provider_macos-0.0.4+8/
+path_provider_windows=/Users/mac/dev/flutter/.pub-cache/hosted/pub.flutter-io.cn/path_provider_windows-0.0.4+3/
+sensors=/Users/mac/dev/flutter/.pub-cache/hosted/pub.flutter-io.cn/sensors-0.4.2+6/
+share_plus=/Users/mac/dev/flutter/.pub-cache/hosted/pub.flutter-io.cn/share_plus-1.2.0/
+share_plus_web=/Users/mac/dev/flutter/.pub-cache/hosted/pub.flutter-io.cn/share_plus_web-0.1.0/
+url_launcher=/Users/mac/dev/flutter/.pub-cache/hosted/pub.flutter-io.cn/url_launcher-5.7.10/
+url_launcher_linux=/Users/mac/dev/flutter/.pub-cache/hosted/pub.flutter-io.cn/url_launcher_linux-0.0.1+4/
+url_launcher_macos=/Users/mac/dev/flutter/.pub-cache/hosted/pub.flutter-io.cn/url_launcher_macos-0.0.1+9/
+url_launcher_web=/Users/mac/dev/flutter/.pub-cache/hosted/pub.flutter-io.cn/url_launcher_web-0.1.5+3/
+url_launcher_windows=/Users/mac/dev/flutter/.pub-cache/hosted/pub.flutter-io.cn/url_launcher_windows-0.0.1+3/
diff --git a/ios/.DS_Store b/ios/.DS_Store
new file mode 100644
index 0000000..3d110d8
Binary files /dev/null and b/ios/.DS_Store differ
diff --git a/ios/.gitignore b/ios/.gitignore
new file mode 100644
index 0000000..e96ef60
--- /dev/null
+++ b/ios/.gitignore
@@ -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
diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 0000000..9367d48
--- /dev/null
+++ b/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 8.0
+
+
diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig
new file mode 100644
index 0000000..ec97fc6
--- /dev/null
+++ b/ios/Flutter/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "Generated.xcconfig"
diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig
new file mode 100644
index 0000000..c4855bf
--- /dev/null
+++ b/ios/Flutter/Release.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "Generated.xcconfig"
diff --git a/ios/Podfile b/ios/Podfile
new file mode 100644
index 0000000..1e8c3c9
--- /dev/null
+++ b/ios/Podfile
@@ -0,0 +1,41 @@
+# 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 flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+ use_modular_headers!
+
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_ios_build_settings(target)
+ end
+end
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
new file mode 100644
index 0000000..9457a75
--- /dev/null
+++ b/ios/Podfile.lock
@@ -0,0 +1,52 @@
+PODS:
+ - audioplayers (0.0.1):
+ - Flutter
+ - Flutter (1.0.0)
+ - image_picker (0.0.1):
+ - Flutter
+ - path_provider (0.0.1):
+ - Flutter
+ - sensors (0.0.1):
+ - Flutter
+ - share_plus (0.0.1):
+ - Flutter
+ - url_launcher (0.0.1):
+ - Flutter
+
+DEPENDENCIES:
+ - audioplayers (from `.symlinks/plugins/audioplayers/ios`)
+ - Flutter (from `Flutter`)
+ - image_picker (from `.symlinks/plugins/image_picker/ios`)
+ - path_provider (from `.symlinks/plugins/path_provider/ios`)
+ - sensors (from `.symlinks/plugins/sensors/ios`)
+ - share_plus (from `.symlinks/plugins/share_plus/ios`)
+ - url_launcher (from `.symlinks/plugins/url_launcher/ios`)
+
+EXTERNAL SOURCES:
+ audioplayers:
+ :path: ".symlinks/plugins/audioplayers/ios"
+ Flutter:
+ :path: Flutter
+ image_picker:
+ :path: ".symlinks/plugins/image_picker/ios"
+ path_provider:
+ :path: ".symlinks/plugins/path_provider/ios"
+ sensors:
+ :path: ".symlinks/plugins/sensors/ios"
+ share_plus:
+ :path: ".symlinks/plugins/share_plus/ios"
+ url_launcher:
+ :path: ".symlinks/plugins/url_launcher/ios"
+
+SPEC CHECKSUMS:
+ audioplayers: 84f968cea3f2deab00ec4f8ff53358b3c0b3992c
+ Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
+ image_picker: 9c3312491f862b28d21ecd8fdf0ee14e601b3f09
+ path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
+ sensors: 84eb7a30e47a649e4172b71d6e81be614c280336
+ share_plus: 2e6276f752c279a84cd68a9fc27dfbbe2a74b072
+ url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
+
+PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
+
+COCOAPODS: 1.10.0
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..8ea1e18
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,552 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 50;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 39928EF87EEC96441C45FB36 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 97CD8957ACF6D84A33D0686E /* Pods_Runner.framework */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 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 = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 371C7ABE4B087857C42CE523 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
+ 3ABB1A893CE0E8F52499AAB4 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 97CD8957ACF6D84A33D0686E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ E881A444DB36FD6DC5D6022F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 39928EF87EEC96441C45FB36 /* Pods_Runner.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 3A6AC336FE1C0C01491FEBA6 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ E881A444DB36FD6DC5D6022F /* Pods-Runner.debug.xcconfig */,
+ 3ABB1A893CE0E8F52499AAB4 /* Pods-Runner.release.xcconfig */,
+ 371C7ABE4B087857C42CE523 /* Pods-Runner.profile.xcconfig */,
+ );
+ name = Pods;
+ path = Pods;
+ sourceTree = "";
+ };
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ 3A6AC336FE1C0C01491FEBA6 /* Pods */,
+ EACBDF03A9879D151B32ED04 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ EACBDF03A9879D151B32ED04 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 97CD8957ACF6D84A33D0686E /* Pods_Runner.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 09CCBA136446196EE1823583 /* [CP] Check Pods Manifest.lock */,
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ E4271C201D82597302548AE1 /* [CP] Embed Pods Frameworks */,
+ );
+ 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 = "";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ 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 */
+ 09CCBA136446196EE1823583 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 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\" embed_and_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";
+ };
+ E4271C201D82597302548AE1 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* 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 = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ 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 = 9.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)";
+ DEVELOPMENT_TEAM = 57Y9Q45MLS;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = cat.love.mix;
+ 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;
+ 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 = 9.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ 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 = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ 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)";
+ DEVELOPMENT_TEAM = 57Y9Q45MLS;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = cat.love.mix;
+ 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)";
+ DEVELOPMENT_TEAM = 57Y9Q45MLS;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = cat.love.mix;
+ 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 */;
+}
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..a28140c
--- /dev/null
+++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..21a3cc1
--- /dev/null
+++ b/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
new file mode 100644
index 0000000..70693e4
--- /dev/null
+++ b/ios/Runner/AppDelegate.swift
@@ -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)
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..d36b1fa
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -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"
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 0000000..4e8d387
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 0000000..d37a438
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 0000000..a7457b1
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 0000000..3e27ff9
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 0000000..af8c383
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 0000000..26a8866
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 0000000..14864f5
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 0000000..a7457b1
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 0000000..426e232
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 0000000..21e142c
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 0000000..21e142c
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 0000000..bffba12
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 0000000..2e9db66
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 0000000..4469b67
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 0000000..c8825c3
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..0bedcf2
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -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"
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000..89c2725
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -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.
\ No newline at end of file
diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2e259c
--- /dev/null
+++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
new file mode 100644
index 0000000..2e43292
--- /dev/null
+++ b/ios/Runner/Info.plist
@@ -0,0 +1,52 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ watermelon
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+
+ CFBundleDisplayName
+ 合成大瓜
+ NSPhotoLibraryUsageDescription
+ 用于选择本地图片
+
+
+
diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 0000000..308a2a5
--- /dev/null
+++ b/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
diff --git a/lib/.DS_Store b/lib/.DS_Store
new file mode 100644
index 0000000..5083e72
Binary files /dev/null and b/lib/.DS_Store differ
diff --git a/lib/components/background.dart b/lib/components/background.dart
new file mode 100644
index 0000000..480dca7
--- /dev/null
+++ b/lib/components/background.dart
@@ -0,0 +1,17 @@
+import 'package:flame/components.dart';
+import 'package:flame_forge2d/viewport.dart';
+
+import '../game/level/levels.dart';
+import '../tools/image/image_tool.dart';
+
+class Background extends SpriteComponent {
+ Background.create(Viewport viewport) {
+ resize(viewport);
+ sprite = Sprite(ImageTool.image(Levels.background));
+ }
+
+ void resize(Viewport viewport) {
+ width = viewport.size.x;
+ height = viewport.size.y;
+ }
+}
diff --git a/lib/components/ball.dart b/lib/components/ball.dart
new file mode 100644
index 0000000..65b1970
--- /dev/null
+++ b/lib/components/ball.dart
@@ -0,0 +1,112 @@
+import 'package:flame/components.dart';
+import 'package:flame_forge2d/sprite_body_component.dart';
+import 'package:flame_forge2d/viewport.dart';
+import 'package:flutter/foundation.dart';
+import 'package:forge2d/forge2d.dart';
+
+import '../game/level/levels.dart';
+
+class Ball extends SpriteBodyComponent {
+ ///下落位置
+ Vector2 fallPosition;
+
+ ///是否下落
+ bool isFalling;
+
+ ///等级
+ int level;
+
+ ///半径
+ double _radius;
+
+ ///是否落地
+ bool landed = false;
+
+ ///level up
+ bool levelUp = false;
+
+ ///是否remove
+ bool removed = false;
+
+ ///是否开始移动
+ bool moving = true;
+
+ ///是否弹动
+ bool bouncing = false;
+
+ Vector2 get position =>
+ viewport.getWorldToScreen(body?.position ?? Vector2.zero());
+
+ double get radius => _radius * viewport.scale;
+
+ static Ball create(
+ Viewport viewport, {
+ @required Vector2 position,
+ @required int level,
+ bool moving,
+ bool canFall,
+ bool landed,
+ bool bounce,
+ }) {
+ position ??= Vector2.zero();
+ level ??= 1;
+ canFall ??= false;
+ landed ??= false;
+ moving ??= true;
+ bounce ??= false;
+ return Ball(
+ level: level,
+ position: position,
+ canFall: canFall,
+ landed: landed,
+ moving: moving,
+ bounce: bounce,
+ sprite: Levels.sprite(level),
+ radius: Levels.radius(level) * (viewport.size.x / viewport.scale / 100),
+ );
+ }
+
+ Ball({
+ @required Vector2 position,
+ @required Sprite sprite,
+ @required double radius,
+ @required this.level,
+ this.landed,
+ this.moving,
+ bool canFall,
+ bool bounce,
+ }) : super(
+ sprite,
+ bounce ? Vector2.zero() : Vector2(radius * 2, radius * 2),
+ ) {
+ fallPosition = position;
+ isFalling = canFall;
+ bouncing = bounce;
+ _radius = radius;
+ }
+
+ ///物理实体
+ @override
+ Body createBody() {
+ final shape = CircleShape()..radius = size.x / 2;
+ var position = fallPosition.clone();
+ if (!isFalling) {
+ position.x = viewport.center.x;
+ }
+ var worldPosition = viewport.getScreenToWorld(position);
+
+ final fixtureDef = FixtureDef()
+ ..shape = shape
+ ..restitution = 0.1 //弹性
+ ..density = 0.1 //密度
+ ..friction = 0.1; //摩擦力
+
+ final bodyDef = BodyDef()
+ ..userData = this //开启检测碰撞
+ ..angularDamping = 0.1 //角速度阻尼
+ ..position = worldPosition
+ ..type = isFalling ? BodyType.DYNAMIC : BodyType.KINEMATIC;
+
+ return world.createBody(bodyDef)..createFixture(fixtureDef);
+ }
+}
diff --git a/lib/components/boundaries.dart b/lib/components/boundaries.dart
new file mode 100644
index 0000000..aa1ea70
--- /dev/null
+++ b/lib/components/boundaries.dart
@@ -0,0 +1,44 @@
+import 'package:flame_forge2d/body_component.dart';
+import 'package:flame_forge2d/viewport.dart';
+import 'package:forge2d/forge2d.dart';
+
+List createBoundaries(Viewport viewport) {
+ final screenSize = Vector2(viewport.size.x, viewport.size.y) / viewport.scale;
+ final topRight = (screenSize / 2);
+ final bottomLeft = topRight * -1;
+ final topLeft = Vector2(bottomLeft.x, topRight.y);
+ final bottomRight = topLeft * -1;
+
+ return [
+ Wall(topLeft, topRight, 1),
+ Wall(topRight, bottomRight, 2),
+ Wall(bottomRight, bottomLeft, 3),
+ Wall(bottomLeft, topLeft, 4),
+ ];
+}
+
+class Wall extends BodyComponent {
+ final Vector2 start;
+ final Vector2 end;
+ final int side;
+
+ Wall(this.start, this.end, this.side);
+
+ @override
+ Body createBody() {
+ final shape = PolygonShape();
+ shape.setAsEdge(start, end);
+
+ final fixtureDef = FixtureDef()
+ ..shape = shape
+ ..restitution = 0.0
+ ..friction = 0.1;
+
+ final bodyDef = BodyDef()
+ ..userData = this //检测碰撞
+ ..position = Vector2.zero()
+ ..type = BodyType.STATIC;
+
+ return world.createBody(bodyDef)..createFixture(fixtureDef);
+ }
+}
diff --git a/lib/components/dead_line.dart b/lib/components/dead_line.dart
new file mode 100644
index 0000000..02f528b
--- /dev/null
+++ b/lib/components/dead_line.dart
@@ -0,0 +1,40 @@
+import 'package:flame/components.dart';
+import 'package:flame_forge2d/flame_forge2d.dart';
+import 'package:flame_forge2d/viewport.dart';
+
+import '../tools/image/image_tool.dart';
+import '../tools/size_tool.dart';
+
+class DeadLine extends SpriteComponent {
+ ///deadline距屏幕顶部距离,单位:vw
+ static double marginTop = 36;
+
+ ///触发deadline显示,ball距屏幕顶部距离,单位:vw
+ static double showTop = marginTop + 44;
+
+ ///是否显示
+ static bool show = false;
+
+ @override
+ Vector2 position;
+
+ static DeadLine create(Viewport viewport) {
+ final sprite = Sprite(ImageTool.image('dead_line.png'));
+ return DeadLine(
+ sprite: sprite,
+ size: Vector2(
+ viewport.size.x,
+ viewport.vw(1),
+ ),
+ position: Vector2(0, viewport.vw(marginTop)));
+ }
+
+ DeadLine({Sprite sprite, Vector2 size, this.position})
+ : super.fromSprite(size, sprite);
+
+ @override
+ void render(canvas) {
+ if (!show) return;
+ super.render(canvas);
+ }
+}
diff --git a/lib/components/scores.dart b/lib/components/scores.dart
new file mode 100644
index 0000000..94bd361
--- /dev/null
+++ b/lib/components/scores.dart
@@ -0,0 +1,33 @@
+import 'package:flame/components.dart';
+import 'package:flame_forge2d/flame_forge2d.dart';
+import 'package:flame_forge2d/viewport.dart';
+import 'package:flutter/material.dart' hide Viewport;
+
+import '../tools/size_tool.dart';
+
+class Scores extends TextComponent {
+ static double margin = 5;
+
+ @override
+ Vector2 position;
+
+ static Scores create(
+ Viewport viewport, {
+ String text,
+ Color color,
+ double size,
+ }) {
+ color ??= Colors.orange;
+ size ??= viewport.vw(10);
+ return Scores(
+ text: text,
+ config: TextConfig(color: color, fontSize: size),
+ position: Vector2(viewport.vw(margin), viewport.vw(margin)));
+ }
+
+ Scores({
+ String text,
+ TextConfig config,
+ this.position,
+ }) : super(text, config: config);
+}
diff --git a/lib/components/setting_button.dart b/lib/components/setting_button.dart
new file mode 100644
index 0000000..853831c
--- /dev/null
+++ b/lib/components/setting_button.dart
@@ -0,0 +1,35 @@
+import 'package:flame/components.dart';
+import 'package:flame_forge2d/flame_forge2d.dart';
+import 'package:flame_forge2d/viewport.dart';
+import 'package:flutter/material.dart' hide Viewport;
+
+import '../game/game_life.dart';
+import '../tools/image/image_tool.dart';
+import '../tools/size_tool.dart';
+
+class SettingButton extends SpriteComponent with Tapable, HasGameRef {
+ static double margin = 5;
+
+ @override
+ Vector2 position;
+
+ @override
+ bool onTapDown(TapDownDetails details) {
+ GameLife(gameRef).setting();
+ return true;
+ }
+
+ static SettingButton create(Viewport viewport) {
+ final sprite = Sprite(ImageTool.image('setting.png'));
+ return SettingButton(
+ sprite: sprite,
+ size: Vector2(
+ viewport.vw(10),
+ viewport.vw(10),
+ ),
+ position: Vector2(viewport.vw(100 - margin - 10), viewport.vw(margin)));
+ }
+
+ SettingButton({Sprite sprite, Vector2 size, this.position})
+ : super.fromSprite(size, sprite);
+}
diff --git a/lib/contacts/ball_ball_contact.dart b/lib/contacts/ball_ball_contact.dart
new file mode 100644
index 0000000..cedfe16
--- /dev/null
+++ b/lib/contacts/ball_ball_contact.dart
@@ -0,0 +1,26 @@
+import 'package:flame_forge2d/contact_callbacks.dart';
+import 'package:flame_forge2d/flame_forge2d.dart';
+
+import '../components/ball.dart';
+import '../game/game_state.dart';
+
+class BallBallContactCallback extends ContactCallback {
+ @override
+ void begin(Ball ball1, Ball ball2, Contact contact) {
+ if (GameState.gameStatus != GameStatus.start) return;
+ ball1.landed = true;
+ ball2.landed = true;
+ if (ball1.level == ball2.level) {
+ if (ball1.position.y < ball2.position.y) {
+ ball1.removed = true;
+ ball2.levelUp = true;
+ } else {
+ ball2.removed = true;
+ ball1.levelUp = true;
+ }
+ }
+ }
+
+ @override
+ void end(Ball ball1, Ball ball2, Contact contact) {}
+}
diff --git a/lib/contacts/ball_wall_contact.dart b/lib/contacts/ball_wall_contact.dart
new file mode 100644
index 0000000..a1f148a
--- /dev/null
+++ b/lib/contacts/ball_wall_contact.dart
@@ -0,0 +1,22 @@
+import 'package:flame_forge2d/contact_callbacks.dart';
+import 'package:flame_forge2d/flame_forge2d.dart';
+
+import '../components/ball.dart';
+import '../components/boundaries.dart';
+import '../game/game_state.dart';
+import '../tools/audio_tool.dart';
+
+class BallWallContactCallback extends ContactCallback {
+ @override
+ void begin(Ball ball, Wall wall, Contact contact) {
+ if (GameState.gameStatus != GameStatus.start) return;
+ if (wall.side != 3) return; //非底边
+ if (!ball.landed) {
+ AudioTool.fall();
+ }
+ ball.landed = true;
+ }
+
+ @override
+ void end(Ball ball, Wall wall, Contact contact) {}
+}
diff --git a/lib/controllers/generate_ball.dart b/lib/controllers/generate_ball.dart
new file mode 100644
index 0000000..5473130
--- /dev/null
+++ b/lib/controllers/generate_ball.dart
@@ -0,0 +1,40 @@
+import 'package:flame/components.dart';
+
+import '../components/ball.dart';
+import '../game/game_state.dart';
+import '../game/level/levels.dart';
+import '../game/my_game.dart';
+import '../tools/size_tool.dart';
+
+class GenerateBall {
+ MyGame gameRef;
+ GenerateBall(this.gameRef);
+
+ bool get canGenerateBall => gameRef.components
+ .where((e) => e is Ball && e.moving && !e.landed)
+ .isEmpty;
+
+ ///生成新球
+ Future generateBall([double x = 0]) async {
+ if (gameRef.hide) return;
+ if (!canGenerateBall) return;
+ if (GameState.gameStatus != GameStatus.start) return;
+ if (GameState.lastBall != null) {
+ GameState.lastBall.fallPosition.x = x;
+ GameState.lastBall.moving = true;
+ }
+ final level = await Levels.generateLevel();
+ final position = Vector2(x, gameRef.viewport.vw(Levels.radius(level) + 5));
+ final ball = Ball.create(
+ gameRef.viewport,
+ position: position,
+ level: level,
+ moving: false,
+ bounce: true,
+ );
+ Future.delayed(Duration(milliseconds: 600), () {
+ gameRef.add(ball);
+ });
+ GameState.lastBall = ball;
+ }
+}
diff --git a/lib/controllers/update_balls_bounce.dart b/lib/controllers/update_balls_bounce.dart
new file mode 100644
index 0000000..3434da2
--- /dev/null
+++ b/lib/controllers/update_balls_bounce.dart
@@ -0,0 +1,32 @@
+import 'package:flame/components.dart';
+
+import '../components/ball.dart';
+import '../game/my_game.dart';
+
+class UpdateBallsBounce extends Component with HasGameRef {
+ @override
+ void update(double dt) {
+ if (gameRef.hide) return;
+ // if (GameState.gameStatus != GameStatus.start) return;
+ gameRef.components.where((e) => e is Ball && e.bouncing).forEach((ball) {
+ Ball b = ball;
+ final realSize = b.radius * 2 / gameRef.viewport.scale;
+ if (b.size.x < realSize) {
+ var size = b.size.x + dt * 10 * realSize;
+ size = size.clamp(b.size.x, realSize);
+ b.size = Vector2(size, size);
+ } else {
+ b.bouncing = false;
+ if (!b.isFalling) return;
+ gameRef.remove(ball);
+ gameRef.add(Ball.create(
+ gameRef.viewport,
+ position: b.position,
+ level: b.level,
+ canFall: true,
+ landed: true,
+ ));
+ }
+ });
+ }
+}
diff --git a/lib/controllers/update_balls_falling.dart b/lib/controllers/update_balls_falling.dart
new file mode 100644
index 0000000..48c7ce6
--- /dev/null
+++ b/lib/controllers/update_balls_falling.dart
@@ -0,0 +1,37 @@
+import 'package:flame/components.dart';
+
+import '../components/ball.dart';
+import '../game/game_state.dart';
+import '../game/my_game.dart';
+import '../tools/size_tool.dart';
+
+class UpdateBallsFalling extends Component with HasGameRef {
+ @override
+ void update(double t) {
+ if (gameRef.hide) return;
+ if (GameState.gameStatus != GameStatus.start) return;
+ gameRef.components
+ .where((e) => e is Ball && e.isFalling == false)
+ .forEach((ball) {
+ Ball b = ball;
+ if (!b.moving) return;
+ final p = b.position;
+ final fp = b.fallPosition;
+ final width = gameRef.viewport.vw(100);
+ final left = fp.x < gameRef.viewport.center.x;
+ final center =
+ (fp.x - gameRef.viewport.center.x).abs() < gameRef.viewport.vw(5);
+ if (!center && b.body.linearVelocity.x == 0) {
+ b.body.linearVelocity =
+ Vector2((left ? -1 : 1) * gameRef.viewport.size.velocitySize, 0);
+ }
+ if (center ||
+ (left && (p.x < b.radius || p.x < fp.x)) ||
+ (!left && (width - p.x < b.radius || p.x > fp.x))) {
+ gameRef.remove(ball);
+ gameRef.add(Ball.create(gameRef.viewport,
+ position: p, level: b.level, canFall: true));
+ }
+ });
+ }
+}
diff --git a/lib/controllers/update_dead_line.dart b/lib/controllers/update_dead_line.dart
new file mode 100644
index 0000000..91bd01a
--- /dev/null
+++ b/lib/controllers/update_dead_line.dart
@@ -0,0 +1,31 @@
+import 'package:flame/components.dart';
+
+import '../components/ball.dart';
+import '../components/dead_line.dart';
+import '../game/game_life.dart';
+import '../game/game_state.dart';
+import '../game/my_game.dart';
+import '../tools/size_tool.dart';
+
+class UpdateDeadLine extends Component with HasGameRef {
+ @override
+ void update(double t) {
+ if (gameRef.hide) return;
+ if (GameState.gameStatus != GameStatus.start) return;
+ final almostDeads = gameRef.components.where((e) =>
+ e is Ball &&
+ e.landed &&
+ e.position.y < gameRef.viewport.vw(DeadLine.showTop));
+ if (almostDeads.isNotEmpty) {
+ DeadLine.show = true;
+ } else {
+ DeadLine.show = false;
+ }
+ if (gameRef.components.any((e) =>
+ e is Ball &&
+ e.landed &&
+ e.position.y < gameRef.viewport.vw(DeadLine.marginTop))) {
+ GameLife(gameRef).dead();
+ }
+ }
+}
diff --git a/lib/controllers/update_gravity.dart b/lib/controllers/update_gravity.dart
new file mode 100644
index 0000000..a5a87fa
--- /dev/null
+++ b/lib/controllers/update_gravity.dart
@@ -0,0 +1,36 @@
+import 'package:flame/components.dart';
+
+import '../game/game_state.dart';
+import '../game/my_game.dart';
+import '../tools/sensor_tool.dart';
+import '../tools/size_tool.dart';
+
+class UpdateGravity extends Component with HasGameRef {
+ void changeGravity(Vector2 gravity) => gameRef.world.setGravity(gravity);
+
+ double get p {
+ final x = SensorTool.xyz.x;
+ if (x.abs() > 1) {
+ return -2 * (x / 10);
+ }
+ return 0; //middle
+ }
+
+ @override
+ void update(double t) {
+ if (gameRef.hide) return;
+ // if (GameState.gameStatus != GameStatus.start) return;
+ final size = gameRef.size.gravitySize;
+ final gravity = Vector2(0, -1 * size);
+ if (GameState.gameSetting.gravity) {
+ final size = gameRef.size.gravitySize;
+ final offset = Vector2(size, 0) * p;
+ final newGravity = Vector2(0, -1 * size) + offset;
+ changeGravity(newGravity);
+ } else {
+ if (gameRef.world.getGravity() != gravity) {
+ changeGravity(gravity);
+ }
+ }
+ }
+}
diff --git a/lib/controllers/update_level_up.dart b/lib/controllers/update_level_up.dart
new file mode 100644
index 0000000..d0cc496
--- /dev/null
+++ b/lib/controllers/update_level_up.dart
@@ -0,0 +1,40 @@
+import 'package:flame/components.dart';
+
+import '../components/ball.dart';
+import '../game/game_life.dart';
+import '../game/game_state.dart';
+import '../game/level/levels.dart';
+import '../game/my_game.dart';
+import '../particles/bloom_particle.dart';
+import '../tools/audio_tool.dart';
+
+class UpdateLevelUp extends Component with HasGameRef {
+ @override
+ void update(double t) {
+ if (gameRef.hide) return;
+ if (GameState.gameStatus != GameStatus.start) return;
+ gameRef.components
+ .where((e) => e is Ball && e.removed)
+ .forEach(gameRef.remove);
+ gameRef.components.where((e) => e is Ball && e.levelUp).forEach((ball) {
+ Ball b = ball;
+ final isLastLevel = Levels.isLastLevel(b.level + 1);
+ gameRef.remove(ball);
+ AudioTool.mix();
+ GameState.updateScore(GameState.score + b.level);
+ final radius = Levels.radius(b.level + 1);
+ gameRef.add(Ball.create(
+ gameRef.viewport,
+ position: b.position..y += (radius - b.radius),
+ level: b.level + 1,
+ canFall: true,
+ landed: true,
+ bounce: true,
+ ));
+ BloomPartcle(gameRef).show(b.position.toOffset(), radius);
+ if (isLastLevel) {
+ GameLife(gameRef).win();
+ }
+ });
+ }
+}
diff --git a/lib/game/game_init.dart b/lib/game/game_init.dart
new file mode 100644
index 0000000..5d87cbb
--- /dev/null
+++ b/lib/game/game_init.dart
@@ -0,0 +1,26 @@
+import 'package:flame/components.dart';
+import 'package:flame_forge2d/viewport.dart';
+
+import '../components/boundaries.dart';
+import '../contacts/ball_ball_contact.dart';
+import '../contacts/ball_wall_contact.dart';
+import '../tools/size_tool.dart';
+import 'my_game.dart';
+
+class GameInit {
+ MyGame gameRef;
+ GameInit(this.gameRef);
+
+ ///初始化
+ void init(Vector2 size) {
+ final scale = 10.0;
+ final gravity = Vector2(0, -1) * size.gravitySize;
+ gameRef.world.setGravity(gravity);
+ gameRef.viewport = Viewport(size, scale);
+ final boundaries = createBoundaries(gameRef.viewport);
+ boundaries.forEach(gameRef.add);
+ //初始化碰撞检测器
+ gameRef.addContactCallback(BallWallContactCallback());
+ gameRef.addContactCallback(BallBallContactCallback());
+ }
+}
diff --git a/lib/game/game_life.dart b/lib/game/game_life.dart
new file mode 100644
index 0000000..cbf442d
--- /dev/null
+++ b/lib/game/game_life.dart
@@ -0,0 +1,70 @@
+import '../pages/game_over_page.dart';
+import '../pages/game_page.dart';
+import '../pages/setting/game_setting_page.dart';
+import '../tools/audio_tool.dart';
+import '../tools/navigator_tool.dart';
+import 'game_onload.dart';
+import 'game_state.dart';
+import 'my_game.dart';
+
+class GameLife {
+ MyGame gameRef;
+ GameLife(this.gameRef);
+
+ static void start() async {
+ await GameOnload.init();
+ await NavigatorTool.push(GamePage());
+ }
+
+ ///暂停
+ void pause() {
+ gameRef.pauseEngine();
+ GameState.gameStatus = GameStatus.pause;
+ }
+
+ ///继续
+ void resume() {
+ gameRef.resumeEngine();
+ GameState.gameStatus = GameStatus.start;
+ }
+
+ ///win
+ void win() {
+ AudioTool.win();
+ GameState.gameStatus = GameStatus.win;
+ NavigatorTool.push(GameOverPage(
+ gameRef,
+ isWin: true,
+ ));
+ }
+
+ ///game over
+ void dead() {
+ AudioTool.dead();
+ GameState.gameStatus = GameStatus.over;
+ NavigatorTool.push(GameOverPage(
+ gameRef,
+ isWin: false,
+ ));
+ }
+
+ ///重新开始
+ void restart() {
+ pause();
+ NavigatorTool.replace(GamePage());
+ }
+
+ ///回主页
+ void back2Home() {
+ NavigatorTool.pop();
+ }
+
+ ///打开设置页面
+ void setting() async {
+ pause();
+ await NavigatorTool.push(GameSettingPage(
+ fromHome: false,
+ ));
+ resume();
+ }
+}
diff --git a/lib/game/game_onload.dart b/lib/game/game_onload.dart
new file mode 100644
index 0000000..eb4c270
--- /dev/null
+++ b/lib/game/game_onload.dart
@@ -0,0 +1,52 @@
+import '../components/background.dart';
+import '../components/dead_line.dart';
+import '../components/scores.dart';
+import '../components/setting_button.dart';
+import '../controllers/generate_ball.dart';
+import '../controllers/update_balls_bounce.dart';
+import '../controllers/update_balls_falling.dart';
+import '../controllers/update_dead_line.dart';
+import '../controllers/update_gravity.dart';
+import '../controllers/update_level_up.dart';
+import '../tools/audio_tool.dart';
+import '../tools/image/image_tool.dart';
+import 'game_state.dart';
+import 'level/levels.dart';
+import 'my_game.dart';
+
+class GameOnload {
+ MyGame gameRef;
+ GameOnload(this.gameRef);
+
+ static bool inited = false;
+
+ static Future init() async {
+ await GameState.init();
+ await Levels.init();
+ if (inited) return;
+ await ImageTool.loadAll();
+ await AudioTool.loadAll();
+ inited = true;
+ }
+
+ ///初始化
+ Future onLoad() async {
+ await init();
+ final background = Background.create(gameRef.viewport);
+ await gameRef.add(background);
+ final deadLine = DeadLine.create(gameRef.viewport);
+ await gameRef.add(deadLine);
+ GameState.scoreComponent =
+ Scores.create(gameRef.viewport, text: GameState.score.toString());
+ await gameRef.add(GameState.scoreComponent);
+ final settingButton = SettingButton.create(gameRef.viewport);
+ await gameRef.add(settingButton);
+ await GenerateBall(gameRef).generateBall();
+ //初始化 game controllers
+ await gameRef.add(UpdateBallsFalling());
+ await gameRef.add(UpdateBallsBounce());
+ await gameRef.add(UpdateLevelUp());
+ await gameRef.add(UpdateDeadLine());
+ await gameRef.add(UpdateGravity());
+ }
+}
diff --git a/lib/game/game_setting.dart b/lib/game/game_setting.dart
new file mode 100644
index 0000000..36cb89b
--- /dev/null
+++ b/lib/game/game_setting.dart
@@ -0,0 +1,60 @@
+import 'dart:convert';
+
+import '../game/game_state.dart';
+
+class GameSetting {
+ ///合成大/小瓜
+ bool levelUp;
+
+ ///随机/只生成小、大瓜模式
+ bool random;
+
+ ///音效
+ bool music;
+
+ ///动效
+ bool bloom;
+
+ ///重力感应
+ bool gravity;
+
+ GameSetting({
+ this.levelUp,
+ this.random,
+ this.music,
+ this.bloom,
+ this.gravity,
+ }) {
+ levelUp ??= true;
+ random ??= true;
+ music ??= true;
+ bloom ??= true;
+ gravity ??= false;
+ }
+
+ GameSetting.fromJson(Map json) {
+ levelUp = json['levelUp'];
+ random = json['random'];
+ music = json['music'];
+ bloom = json['bloom'];
+ gravity = json['gravity'];
+ }
+
+ Map toJson() {
+ final data = {};
+ data['levelUp'] = levelUp;
+ data['random'] = random;
+ data['music'] = music;
+ data['bloom'] = bloom;
+ data['gravity'] = gravity;
+ return data;
+ }
+
+ String toJsonStr() => jsonEncode(toJson());
+
+ static GameSetting fromJsonStr(String result) {
+ return GameSetting.fromJson(jsonDecode(result));
+ }
+
+ Future update() async => GameState.updateSetting(this);
+}
diff --git a/lib/game/game_state.dart b/lib/game/game_state.dart
new file mode 100644
index 0000000..c1de012
--- /dev/null
+++ b/lib/game/game_state.dart
@@ -0,0 +1,77 @@
+import '../components/ball.dart';
+import '../components/scores.dart';
+import '../game/game_setting.dart';
+import '../tools/hive_tool.dart';
+
+class GameState {
+ ///成绩
+ static int score = 0;
+
+ ///最高纪录
+ static int record = 0;
+
+ ///是否是新纪录
+ static bool isNewRecord = false;
+
+ ///游戏状态
+ static GameStatus gameStatus = GameStatus.start;
+
+ ///游戏设置
+ static GameSetting gameSetting = GameSetting();
+
+ ///上一个小球
+ static Ball lastBall;
+
+ ///分数部件
+ static Scores scoreComponent;
+
+ static Future updateScore(int newScore) async {
+ score = newScore;
+ scoreComponent?.text = GameState.score.toString();
+ if (newScore > record) {
+ record = newScore;
+ isNewRecord = true;
+ await HiveTool().set('record', score);
+ }
+ }
+
+ static Future updateSetting(GameSetting setting) async {
+ gameSetting = setting;
+ await HiveTool().set('gameSetting', gameSetting.toJsonStr());
+ }
+
+ static bool inited = false;
+
+ ///重置游戏状态
+ static Future init() async {
+ score = 0;
+ isNewRecord = false;
+ gameStatus = GameStatus.start;
+ lastBall = null;
+ await initSetting();
+ }
+
+ static Future initSetting() async {
+ if (inited) return;
+ final n = await HiveTool().get('record');
+ if (n != null) record = n;
+ final s = await HiveTool().get('gameSetting');
+ if (s != null) gameSetting = GameSetting.fromJsonStr(s);
+ inited = true;
+ }
+}
+
+///游戏状态
+enum GameStatus {
+ ///挂了
+ over,
+
+ ///赢了
+ win,
+
+ ///暂停
+ pause,
+
+ ///开始
+ start,
+}
diff --git a/lib/game/level/level.dart b/lib/game/level/level.dart
new file mode 100644
index 0000000..276f59b
--- /dev/null
+++ b/lib/game/level/level.dart
@@ -0,0 +1,36 @@
+import 'dart:convert';
+
+class Level {
+ String image;
+ double radius;
+
+ Level(this.radius, this.image);
+
+ Level.fromJson(Map json) {
+ image = json['image'];
+ radius = json['radius'];
+ }
+
+ Map toJson() {
+ final data = {};
+ data['image'] = image;
+ data['radius'] = radius;
+ return data;
+ }
+
+ static List fromJsonList(List list) {
+ final results = [];
+ for (var i = 0; i < list.length; i++) {
+ results.add(Level.fromJson(list[i]));
+ }
+ return results;
+ }
+
+ static String toJsons(List levels) =>
+ levels.map((e) => jsonEncode(e.toJson())).toList().join('|');
+
+ static List fromJsons(String result) {
+ final levels = result.split('|').map((e) => jsonDecode(e)).toList();
+ return fromJsonList(levels);
+ }
+}
diff --git a/lib/game/level/levels.dart b/lib/game/level/levels.dart
new file mode 100644
index 0000000..1f2e4c6
--- /dev/null
+++ b/lib/game/level/levels.dart
@@ -0,0 +1,123 @@
+import 'dart:math';
+import 'dart:ui';
+
+import 'package:flame/components.dart';
+
+import '../../tools/hive_tool.dart';
+import '../../tools/image/image_tool.dart';
+import '../game_state.dart';
+import 'level.dart';
+import 'levels_inner.dart';
+
+class Levels {
+ static bool isLastLevel(int level) => level > topLevel - 1;
+
+ static int get topLevel => kLevels.length;
+
+ static Future generateLevel() async {
+ if (!inited) await setDefaultLevels();
+ if (!GameState.gameSetting.random) return 1;
+ while (true) {
+ if (topLevel < 1) {
+ kLevels.clear();
+ kLevels.addAll(LevelsInner.levels['friut']);
+ }
+ final level = topLevel < 2 ? 1 : (Random().nextInt(topLevel - 1) + 1);
+ if (level < topLevel / 2) return level;
+ }
+ }
+
+ ///小球半径 单位:vw
+ static double radius(int level) {
+ return !GameState.gameSetting.levelUp
+ ? (topLevel * 2 + 2) - level * 2.0
+ : 2.0 + level * 2.0;
+ }
+
+ ///小球贴图
+ static Sprite sprite(int level) {
+ if (!GameState.gameSetting.levelUp) {
+ level = topLevel + 1 - level;
+ }
+ return Sprite(ImageTool.image(kLevels[level - 1].image));
+ }
+
+ static Image image(int level) {
+ if (!GameState.gameSetting.levelUp) {
+ level = topLevel + 1 - level;
+ }
+ return ImageTool.image(kLevels[level - 1].image);
+ }
+
+ static String imagePath(int level) {
+ if (!GameState.gameSetting.levelUp) {
+ level = topLevel + 1 - level;
+ }
+ return kLevels[level - 1].image;
+ }
+
+ ///等级表(level,radius)
+ static final kLevels = [];
+
+ static String background = 'bg.png';
+
+ static Future setBackground(String path) async {
+ background = path;
+ await HiveTool().set('background', background);
+ }
+
+ static bool get inited => kLevels.isNotEmpty;
+
+ static Future init() async {
+ if (inited) return;
+ final bg = await HiveTool().get('background');
+ if (bg != null) background = bg;
+ final result = await HiveTool().get('kLevels');
+ if (result != null) {
+ final levels = Level.fromJsons(result);
+ if (levels != null) {
+ await sets(levels);
+ return;
+ }
+ }
+ await setDefaultLevels();
+ }
+
+ static Future setDefaultLevels() async {
+ return await sets(LevelsInner.levels['friut']);
+ }
+
+ ///todo level增删查改
+ static Future updates() async {
+ final result = Level.toJsons(kLevels);
+ await HiveTool().set('kLevels', result);
+ }
+
+ static Future sets(List levels) async {
+ if (levels == null) return;
+ kLevels.clear();
+ kLevels.addAll(levels);
+ await updates();
+ }
+
+ static Future add(Level level, {int index}) async {
+ index ??= kLevels.length;
+ if (index > kLevels.length) return;
+ kLevels.insert(index, level);
+ await updates();
+ }
+
+ static Future del({int index}) async {
+ index ??= kLevels.length - 1;
+ if (index > kLevels.length - 1) return;
+ kLevels.removeAt(index);
+ await updates();
+ }
+
+ static Future update(Level level, {int index}) async {
+ index ??= kLevels.length - 1;
+ if (index > kLevels.length - 1) return;
+ kLevels[index] = level;
+ await updates();
+ }
+}
diff --git a/lib/game/level/levels_inner.dart b/lib/game/level/levels_inner.dart
new file mode 100644
index 0000000..1b68f35
--- /dev/null
+++ b/lib/game/level/levels_inner.dart
@@ -0,0 +1,66 @@
+import 'level.dart';
+
+class LevelsInner {
+ static final levels = {
+ 'friut': _friutsLevels,
+ 'emoji': _emojiLevels,
+ '985': _985Levels,
+ 'shandong': _shandongLevels,
+ };
+
+ static final _friutsLevels = [
+ Level(2.0, 'friuts/1.png'),
+ Level(4.0, 'friuts/2.png'),
+ Level(6.0, 'friuts/3.png'),
+ Level(8.0, 'friuts/4.png'),
+ Level(10.0, 'friuts/5.png'),
+ Level(12.0, 'friuts/6.png'),
+ Level(14.0, 'friuts/7.png'),
+ Level(16.0, 'friuts/8.png'),
+ Level(18.0, 'friuts/9.png'),
+ Level(20.0, 'friuts/10.png'),
+ Level(22.0, 'friuts/11.png'),
+ ];
+
+ static final _emojiLevels = [
+ Level(2.0, 'qq/1.png'),
+ Level(4.0, 'qq/2.png'),
+ Level(6.0, 'qq/3.png'),
+ Level(8.0, 'qq/4.png'),
+ Level(10.0, 'qq/5.png'),
+ Level(12.0, 'qq/6.png'),
+ Level(14.0, 'qq/7.png'),
+ Level(16.0, 'qq/8.png'),
+ Level(18.0, 'qq/9.png'),
+ Level(20.0, 'qq/10.png'),
+ Level(22.0, 'qq/11.png'),
+ ];
+
+ static final _985Levels = [
+ Level(2.0, '985/1.png'),
+ Level(4.0, '985/2.png'),
+ Level(6.0, '985/3.png'),
+ Level(8.0, '985/4.png'),
+ Level(10.0, '985/5.png'),
+ Level(12.0, '985/6.png'),
+ Level(14.0, '985/7.png'),
+ Level(16.0, '985/8.png'),
+ Level(18.0, '985/9.png'),
+ Level(20.0, '985/10.png'),
+ Level(22.0, '985/11.png'),
+ ];
+
+ static final _shandongLevels = [
+ Level(2.0, 'shandong/1.png'),
+ Level(4.0, 'shandong/2.png'),
+ Level(6.0, 'shandong/3.png'),
+ Level(8.0, 'shandong/4.png'),
+ Level(10.0, 'shandong/5.png'),
+ Level(12.0, 'shandong/6.png'),
+ Level(14.0, 'shandong/7.png'),
+ Level(16.0, 'shandong/8.png'),
+ Level(18.0, 'shandong/9.png'),
+ Level(20.0, 'shandong/10.png'),
+ Level(22.0, 'shandong/11.png'),
+ ];
+}
diff --git a/lib/game/my_game.dart b/lib/game/my_game.dart
new file mode 100644
index 0000000..15eec58
--- /dev/null
+++ b/lib/game/my_game.dart
@@ -0,0 +1,49 @@
+import 'package:flame/components.dart';
+import 'package:flame_forge2d/forge2d_game.dart';
+import 'package:flutter/material.dart';
+import 'package:forge2d/forge2d.dart';
+
+import '../controllers/generate_ball.dart';
+import '../tools/size_tool.dart';
+import 'game_init.dart';
+import 'game_onload.dart';
+
+class MyGame extends Forge2DGame with HasTapableComponents {
+ MyGame(
+ double width,
+ double height, {
+ this.hide,
+ }) {
+ hide ??= false;
+ final viewportSize = Vector2(width, height);
+ GameInit(this).init(viewportSize);
+ }
+
+ bool hide;
+
+ @override
+ Future onLoad() async {
+ await GameOnload(this).onLoad();
+ }
+
+ @override
+ void onTapDown(_, TapDownDetails details) {
+ if (hide) return;
+ super.onTapDown(_, details);
+ if (details.localPosition.dy > viewport.vw(30)) {
+ GenerateBall(this).generateBall(details.localPosition.dx);
+ }
+ }
+
+ @override
+ void update(double dt) {
+ if (hide) return;
+ super.update(dt);
+ }
+
+ @override
+ void render(Canvas canvas) {
+ if (hide) return;
+ super.render(canvas);
+ }
+}
diff --git a/lib/generated_plugin_registrant.dart b/lib/generated_plugin_registrant.dart
new file mode 100644
index 0000000..13c6a39
--- /dev/null
+++ b/lib/generated_plugin_registrant.dart
@@ -0,0 +1,21 @@
+//
+// Generated file. Do not edit.
+//
+
+// ignore_for_file: lines_longer_than_80_chars
+
+import 'package:audioplayers/audioplayers_web.dart';
+import 'package:image_picker_web_redux/image_picker_web_redux.dart';
+import 'package:share_plus_web/share_plus_web.dart';
+import 'package:url_launcher_web/url_launcher_web.dart';
+
+import 'package:flutter_web_plugins/flutter_web_plugins.dart';
+
+// ignore: public_member_api_docs
+void registerPlugins(Registrar registrar) {
+ AudioplayersPlugin.registerWith(registrar);
+ ImagePickerWeb.registerWith(registrar);
+ SharePlusPlugin.registerWith(registrar);
+ UrlLauncherPlugin.registerWith(registrar);
+ registrar.registerMessageHandler();
+}
diff --git a/lib/main.dart b/lib/main.dart
new file mode 100644
index 0000000..d3fdb6d
--- /dev/null
+++ b/lib/main.dart
@@ -0,0 +1,21 @@
+import 'package:flutter/material.dart';
+
+import 'pages/home_page.dart';
+import 'tools/navigator_tool.dart';
+import 'tools/system_tool.dart';
+
+void main() async {
+ runApp(MyApp());
+ SystemTool.keepPortrait();
+}
+
+class MyApp extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: '合成大瓜',
+ navigatorObservers: [NavigatorTool()],
+ home: HomePage(),
+ );
+ }
+}
diff --git a/lib/pages/about_page.dart b/lib/pages/about_page.dart
new file mode 100644
index 0000000..6d73039
--- /dev/null
+++ b/lib/pages/about_page.dart
@@ -0,0 +1,136 @@
+import 'package:flutter/material.dart';
+
+import '../tools/navigator_tool.dart';
+import '../tools/screen/screen_config.dart';
+import '../tools/screen/screen_extension.dart';
+import '../tools/url_tool.dart';
+import '../widgets/base_widget.dart';
+import '../widgets/pulse.dart';
+
+class AboutPage extends StatelessWidget {
+ void _back() {
+ NavigatorTool.pop();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return ScreenConfig(
+ builder: () => Scaffold(
+ backgroundColor: Colors.grey[100],
+ body: SafeArea(
+ child: Center(
+ child: Column(
+ children: [
+ _topAction(),
+ lExpanded(),
+ lExpanded(flex: 10),
+ lText(
+ 'A',
+ bold: true,
+ size: 18,
+ color: Colors.grey[600],
+ ),
+ lExpanded(),
+ lText(
+ 'Work',
+ bold: true,
+ size: 36,
+ color: Colors.black,
+ ),
+ lExpanded(),
+ lText(
+ 'by',
+ bold: true,
+ size: 18,
+ color: Colors.grey[600],
+ ),
+ lExpanded(),
+ Image.asset(
+ 'assets/images/qr.jpg',
+ width: 30.vw,
+ ),
+ lExpanded(flex: 10, child: _homeSite()),
+ _madeWithLove(),
+ lExpanded(),
+ _bottomAction(),
+ lExpanded(),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget _homeSite() {
+ final url = 'https://xbox.work';
+ return GestureDetector(
+ onTap: () {
+ UrlTool.open(url);
+ },
+ child: lText(
+ url,
+ color: Colors.blue,
+ ),
+ );
+ }
+
+ Widget _topAction() => Container(
+ padding: EdgeInsets.all(5.vw),
+ child: Row(
+ children: [
+ lIconButton(
+ Icons.chevron_left,
+ size: 10.vw,
+ color: Colors.black,
+ onTap: _back,
+ ),
+ lExpanded(),
+ ],
+ ),
+ );
+
+ Widget _madeWithLove() => Row(
+ children: [
+ lExpanded(),
+ lText(
+ 'Made with ',
+ bold: true,
+ size: 18,
+ color: Colors.grey[600],
+ ),
+ Pulse(
+ child: Icon(
+ Icons.favorite,
+ size: 5.vw,
+ color: Colors.red,
+ ),
+ ),
+ lExpanded(),
+ ],
+ );
+
+ Widget _bottomAction() => Row(
+ children: [
+ lExpanded(),
+ lText(
+ 'Powered by',
+ bold: true,
+ size: 18,
+ color: Colors.grey[600],
+ ),
+ lWidth(5.vw),
+ FlutterLogo(size: 8.vw),
+ Icon(
+ Icons.close,
+ size: 5.vw,
+ color: Colors.black54,
+ ),
+ Image.asset(
+ 'assets/images/flame.png',
+ width: 8.vw,
+ ),
+ lExpanded(),
+ ],
+ );
+}
diff --git a/lib/pages/game_over_page.dart b/lib/pages/game_over_page.dart
new file mode 100644
index 0000000..e044de1
--- /dev/null
+++ b/lib/pages/game_over_page.dart
@@ -0,0 +1,188 @@
+import 'package:flutter/material.dart';
+
+import '../game/game_life.dart';
+import '../game/game_state.dart';
+import '../game/level/levels.dart';
+import '../game/my_game.dart';
+import '../tools/image/image_tool.dart';
+import '../tools/navigator_tool.dart';
+import '../tools/screen/screen_config.dart';
+import '../tools/screen/screen_extension.dart';
+import '../tools/share_tool.dart';
+import '../widgets/base_widget.dart';
+import '../widgets/spinner.dart';
+import 'setting/game_setting_page.dart';
+
+// ignore: must_be_immutable
+class GameOverPage extends StatelessWidget {
+ MyGame gameRef;
+ bool isWin;
+
+ GameOverPage(
+ this.gameRef, {
+ this.isWin,
+ }) {
+ isWin ??= true;
+ }
+
+ void _share() async {
+ await ShareTool.share(
+ '我合成大西瓜的最高分是${GameState.record}!关注微信号:乂乂又又,回复:大西瓜,就有链接了^^');
+ }
+
+ void _retry() {
+ NavigatorTool.pop();
+ GameLife(gameRef).restart();
+ }
+
+ void _home() {
+ NavigatorTool.pop();
+ GameLife(gameRef).back2Home();
+ }
+
+ void _setting() => NavigatorTool.push(
+ GameSettingPage(
+ fromHome: false,
+ ),
+ );
+
+ @override
+ Widget build(BuildContext context) {
+ return ScreenConfig(
+ builder: () => WillPopScope(
+ onWillPop: () async {
+ _retry();
+ return false;
+ },
+ child: Scaffold(
+ backgroundColor: Colors.black45,
+ body: SafeArea(
+ child: Center(
+ child: Column(
+ children: [
+ lExpanded(child: _topAction()),
+ ...GameState.isNewRecord ? _newRecord() : _normalRecord(),
+ if (isWin)
+ Stack(
+ alignment: Alignment.center,
+ children: [
+ _shine(),
+ _topBall(),
+ ],
+ ),
+ lHeight(10.vw),
+ lExpanded(
+ child: _bottomAction(),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget _topAction() => Row(
+ children: [
+ lWidth(10.vw),
+ lIconButton(
+ Icons.home,
+ size: 10.vw,
+ color: Colors.white,
+ onTap: _home,
+ ),
+ lExpanded(),
+ lIconButton(
+ Icons.settings,
+ size: 10.vw,
+ color: Colors.white,
+ onTap: _setting,
+ ),
+ lWidth(10.vw),
+ ],
+ );
+
+ Widget _bottomAction() => Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ lButton(
+ '分享一下',
+ onTap: _share,
+ height: 5.vw * 2.2,
+ colorBg: Colors.black,
+ colorText: Colors.white,
+ ),
+ lButton(
+ '再来一次',
+ onTap: _retry,
+ height: 5.vw * 2.2,
+ ),
+ ],
+ );
+
+ List _normalRecord() => [
+ Text(
+ '最高分:${GameState.record}',
+ style: TextStyle(
+ fontSize: 24,
+ color: Colors.white,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ lHeight(10.vw),
+ Text(
+ '得分:${GameState.score}',
+ style: TextStyle(
+ fontSize: 36,
+ color: Colors.orange,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ lHeight(10.vw),
+ ];
+
+ List _newRecord() => [
+ Text(
+ '新纪录',
+ style: TextStyle(
+ fontSize: 36,
+ color: Colors.white,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ lHeight(5.vw),
+ Text(
+ '${GameState.score}',
+ style: TextStyle(
+ fontSize: 36,
+ color: Colors.orange,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ lHeight(10.vw),
+ ];
+
+ Widget _shine() {
+ return Spinner(
+ child: Container(
+ width: 90.vw,
+ height: 90.vw,
+ child: RawImage(
+ image: ImageTool.image('shine.png'),
+ fit: BoxFit.cover,
+ ),
+ ));
+ }
+
+ Widget _topBall() {
+ return Container(
+ width: Levels.radius(Levels.topLevel).vw * 2,
+ height: Levels.radius(Levels.topLevel).vw * 2,
+ child: RawImage(
+ image: Levels.image(Levels.topLevel),
+ fit: BoxFit.cover,
+ ),
+ );
+ }
+}
diff --git a/lib/pages/game_page.dart b/lib/pages/game_page.dart
new file mode 100644
index 0000000..71bae9a
--- /dev/null
+++ b/lib/pages/game_page.dart
@@ -0,0 +1,57 @@
+import 'package:flame/game.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+
+import '../game/my_game.dart';
+import '../tools/navigator_tool.dart';
+import '../tools/sensor_tool.dart';
+import '../tools/system_tool.dart';
+
+class GamePage extends StatefulWidget {
+ final bool hide;
+ GamePage({this.hide});
+ @override
+ _GamePageState createState() => _GamePageState();
+}
+
+class _GamePageState extends State {
+ @override
+ void initState() {
+ super.initState();
+ SensorTool.start();
+ }
+
+ @override
+ void dispose() {
+ SensorTool.stop();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return GestureDetector(
+ onHorizontalDragEnd: (_) {
+ if (kIsWeb) {
+ //侧滑返回手势
+ NavigatorTool.pop();
+ }
+ },
+ child: Scaffold(
+ body: SafeArea(
+ child: LayoutBuilder(
+ builder: (_, constraints) {
+ SystemTool.changeStatusBarColor();
+ return GameWidget(
+ game: MyGame(
+ constraints.maxWidth,
+ constraints.maxHeight,
+ hide: widget.hide,
+ ),
+ );
+ },
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart
new file mode 100644
index 0000000..4149b4e
--- /dev/null
+++ b/lib/pages/home_page.dart
@@ -0,0 +1,95 @@
+import 'package:flutter/material.dart';
+
+import '../game/game_life.dart';
+import '../game/game_state.dart';
+import '../tools/navigator_tool.dart';
+import '../tools/screen/screen_config.dart';
+import '../tools/screen/screen_extension.dart';
+import '../widgets/base_widget.dart';
+import 'about_page.dart';
+import 'game_page.dart';
+import 'setting/game_setting_page.dart';
+
+class HomePage extends StatelessWidget {
+ void _start() {
+ GameLife.start();
+ }
+
+ void _about() {
+ NavigatorTool.push(AboutPage());
+ }
+
+ Future _setting() async {
+ await GameState.initSetting();
+ await NavigatorTool.push(
+ GameSettingPage(
+ fromHome: true,
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return ScreenConfig(
+ builder: () => Scaffold(
+ backgroundColor: Colors.grey[100],
+ body: SafeArea(
+ child: Center(
+ child: Column(
+ children: [
+ _topAction(),
+ lExpanded(
+ child: _bottomAction(),
+ ),
+ _hideGame(),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ ///预加载
+ Widget _hideGame() => Offstage(
+ child: Container(
+ width: 1,
+ height: 1,
+ child: GamePage(hide: true),
+ ),
+ );
+
+ Widget _topAction() => Container(
+ padding: EdgeInsets.all(5.vw),
+ child: Row(
+ children: [
+ lIconButton(
+ Icons.info,
+ size: 10.vw,
+ color: Colors.black,
+ onTap: _about,
+ ),
+ lExpanded(),
+ lIconButton(
+ Icons.settings,
+ size: 10.vw,
+ color: Colors.black,
+ onTap: _setting,
+ ),
+ ],
+ ),
+ );
+
+ Widget _bottomAction() => Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ lButton(
+ '开始游戏',
+ onTap: _start,
+ height: 5.vw * 2.2,
+ colorBg: Colors.black,
+ colorText: Colors.white,
+ ),
+ ],
+ );
+}
diff --git a/lib/pages/setting/background_page.dart b/lib/pages/setting/background_page.dart
new file mode 100644
index 0000000..c525f6e
--- /dev/null
+++ b/lib/pages/setting/background_page.dart
@@ -0,0 +1,102 @@
+import 'package:flutter/material.dart';
+
+import '../../game/level/levels.dart';
+import '../../tools/image/image_pick_tool.dart';
+import '../../tools/image/image_tool.dart';
+import '../../tools/navigator_tool.dart';
+import '../../tools/screen/screen_config.dart';
+import '../../tools/screen/screen_extension.dart';
+import '../../widgets/base_widget.dart';
+
+class BackgroundPage extends StatefulWidget {
+ @override
+ _BackgroundPageState createState() => _BackgroundPageState();
+}
+
+class _BackgroundPageState extends State {
+ void _back() {
+ NavigatorTool.pop();
+ }
+
+ void _pickImage() async {
+ final path = await ImagePickTool.pickImageAndSave();
+ if (path != null) {
+ await Levels.setBackground(path);
+ setState(() {});
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return ScreenConfig(
+ builder: () => Scaffold(
+ backgroundColor: Colors.grey[100],
+ body: SafeArea(
+ child: Center(
+ child: Column(
+ children: [
+ _topAction(),
+ lExpanded(
+ child: _background(),
+ ),
+ _bottomAction(),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget _background() {
+ return Container(
+ width: 100.vw,
+ height: 100.vh,
+ child: RawImage(
+ image: ImageTool.image(Levels.background),
+ fit: BoxFit.cover,
+ ),
+ );
+ }
+
+ Widget _topAction() => Container(
+ padding: EdgeInsets.all(5.vw),
+ child: Row(
+ children: [
+ lIconButton(
+ Icons.chevron_left,
+ size: 10.vw,
+ color: Colors.black,
+ onTap: _back,
+ ),
+ lExpanded(
+ child: lText(
+ '更换背景',
+ bold: true,
+ size: 18,
+ )),
+ lIconButton(
+ Icons.chevron_left,
+ size: 10.vw,
+ color: Colors.transparent,
+ ),
+ ],
+ ),
+ );
+
+ Widget _bottomAction() => Container(
+ padding: EdgeInsets.all(10.vw),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ lButton(
+ '更换图片',
+ onTap: _pickImage,
+ height: 5.vw * 2.2,
+ colorBg: Colors.black,
+ colorText: Colors.white,
+ ),
+ ],
+ ),
+ );
+}
diff --git a/lib/pages/setting/game_setting_page.dart b/lib/pages/setting/game_setting_page.dart
new file mode 100644
index 0000000..1166c26
--- /dev/null
+++ b/lib/pages/setting/game_setting_page.dart
@@ -0,0 +1,156 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+
+import '../../game/game_setting.dart';
+import '../../game/game_state.dart';
+import '../../game/level/levels.dart';
+import '../../tools/dialog_tool.dart';
+import '../../tools/navigator_tool.dart';
+import '../../tools/screen/screen_config.dart';
+import '../../tools/screen/screen_extension.dart';
+import '../../widgets/base_widget.dart';
+import 'more_setting_page.dart';
+
+class GameSettingPage extends StatefulWidget {
+ final bool fromHome;
+ GameSettingPage({@required this.fromHome});
+ @override
+ _GameSettingPageState createState() => _GameSettingPageState();
+}
+
+class _GameSettingPageState extends State {
+ void _back() {
+ NavigatorTool.pop();
+ }
+
+ void _goHome() {
+ NavigatorTool.pop(); //pop setting page
+ if (GameState.gameStatus != GameStatus.pause) {
+ NavigatorTool.pop(); //pop gameover page
+ }
+ NavigatorTool.pop(); //pop game page
+ }
+
+ Future _more() async {
+ if (!widget.fromHome) {
+ await DialogTool.show(
+ context,
+ title: '提示',
+ content: '只能从首页进入高级设置',
+ actionText: '首页',
+ action: _goHome,
+ );
+ return;
+ }
+ await Levels.init();
+ await NavigatorTool.push(MoreSettingPage());
+ }
+
+ GameSetting get setting => GameState.gameSetting;
+
+ String get _levelUp => setting.levelUp ? '合成大瓜' : '合成小瓜';
+
+ String get _random => setting.random
+ ? '随机模式'
+ : setting.levelUp
+ ? '小瓜模式'
+ : '大瓜模式';
+
+ @override
+ Widget build(BuildContext context) {
+ return ScreenConfig(
+ builder: () => Scaffold(
+ backgroundColor: Colors.grey[100],
+ body: SafeArea(
+ child: Center(
+ child: Column(
+ children: [
+ _topAction(),
+ lExpanded(),
+ _switchItem(_levelUp, value: setting.levelUp,
+ onChanged: (v) async {
+ if (!widget.fromHome) {
+ await DialogTool.show(
+ context,
+ title: '提示',
+ content: '只能从首页进入设置该选项',
+ actionText: '首页',
+ action: _goHome,
+ );
+ return;
+ }
+ setting.levelUp = v;
+ await setting.update();
+ }),
+ _switchItem(_random, value: setting.random,
+ onChanged: (v) async {
+ setting.random = v;
+ await setting.update();
+ }),
+ if (!kIsWeb)
+ _switchItem('重力感应', value: setting.gravity,
+ onChanged: (v) async {
+ setting.gravity = v;
+ await setting.update();
+ }),
+ _switchItem('音效', value: setting.music, onChanged: (v) async {
+ setting.music = v;
+ await setting.update();
+ }),
+ _switchItem('动效', value: setting.bloom, onChanged: (v) async {
+ setting.bloom = v;
+ await setting.update();
+ }),
+ lExpanded(),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget _topAction() => Container(
+ padding: EdgeInsets.all(5.vw),
+ child: Row(
+ children: [
+ lIconButton(
+ Icons.chevron_left,
+ size: 10.vw,
+ color: Colors.black,
+ onTap: _back,
+ ),
+ lExpanded(),
+ lButton(
+ '高级设置',
+ onTap: _more,
+ height: 5.vw * 2.2,
+ colorBg: Colors.black,
+ colorText: Colors.white,
+ ),
+ ],
+ ),
+ );
+
+ Widget _switchItem(String lable, {bool value, Function(bool) onChanged}) =>
+ lExpanded(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ lButton(
+ lable,
+ height: 5.vw * 2.2,
+ colorBg: Colors.black,
+ colorText: Colors.white,
+ ),
+ CupertinoSwitch(
+ value: value,
+ onChanged: (v) {
+ onChanged(v);
+ setState(() {});
+ }),
+ ],
+ ),
+ );
+}
diff --git a/lib/pages/setting/image_crop_page.dart b/lib/pages/setting/image_crop_page.dart
new file mode 100644
index 0000000..7f6ed5f
--- /dev/null
+++ b/lib/pages/setting/image_crop_page.dart
@@ -0,0 +1,130 @@
+import 'package:crop/crop.dart';
+import 'package:flutter/material.dart';
+
+import '../../tools/image/image_pick_tool.dart';
+import '../../tools/image/image_tool.dart';
+import '../../tools/navigator_tool.dart';
+import '../../tools/screen/screen_config.dart';
+import '../../tools/screen/screen_extension.dart';
+import '../../widgets/base_widget.dart';
+
+class ImageCropPage extends StatefulWidget {
+ @override
+ _ImageCropPageState createState() => _ImageCropPageState();
+}
+
+class _ImageCropPageState extends State {
+ void _back() {
+ NavigatorTool.pop();
+ }
+
+ final cropController = CropController(aspectRatio: 1);
+ final cropShape = BoxShape.circle;
+ final cropFit = BoxFit.contain;
+
+ Widget cropImage;
+
+ Future _crop() async {
+ final pixelRatio = MediaQuery.of(context).devicePixelRatio;
+ final cropped = await cropController.crop(pixelRatio: pixelRatio);
+ final path = await ImageTool.saveImage(cropped);
+ NavigatorTool.pop(path);
+ CircleAvatar();
+ }
+
+ void _pickImage() async {
+ final bytes = await ImagePickTool.pickImageAsBytes();
+ if (bytes != null) {
+ cropImage = Image.memory(
+ bytes,
+ fit: cropFit,
+ );
+ setState(() {});
+ }
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ cropImage = Image.asset(
+ 'assets/images/friuts/10.png',
+ fit: cropFit,
+ );
+ _pickImage();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return ScreenConfig(
+ builder: () => Scaffold(
+ backgroundColor: Colors.grey[100],
+ body: SafeArea(
+ child: Center(
+ child: Column(
+ children: [
+ _topAction(),
+ lExpanded(
+ child: _cropArea(),
+ ),
+ _bottomAction(),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget _cropArea() {
+ return AspectRatio(
+ aspectRatio: 1,
+ child: Crop(
+ controller: cropController,
+ shape: cropShape,
+ child: cropImage,
+ fit: cropFit,
+ ),
+ );
+ }
+
+ Widget _topAction() => Container(
+ padding: EdgeInsets.all(5.vw),
+ child: Row(
+ children: [
+ lIconButton(
+ Icons.chevron_left,
+ size: 10.vw,
+ color: Colors.black,
+ onTap: _back,
+ ),
+ lExpanded(
+ child: lText(
+ '裁剪图片',
+ bold: true,
+ size: 18,
+ )),
+ lIconButton(
+ Icons.chevron_left,
+ size: 10.vw,
+ color: Colors.transparent,
+ ),
+ ],
+ ),
+ );
+
+ Widget _bottomAction() => Container(
+ padding: EdgeInsets.all(10.vw),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ lButton(
+ '确定',
+ onTap: _crop,
+ height: 5.vw * 2.2,
+ colorBg: Colors.black,
+ colorText: Colors.white,
+ ),
+ ],
+ ),
+ );
+}
diff --git a/lib/pages/setting/inner_level_page.dart b/lib/pages/setting/inner_level_page.dart
new file mode 100644
index 0000000..f84cea3
--- /dev/null
+++ b/lib/pages/setting/inner_level_page.dart
@@ -0,0 +1,220 @@
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+import '../../game/level/level.dart';
+import '../../game/level/levels.dart';
+import '../../game/level/levels_inner.dart';
+import '../../tools/image/image_tool.dart';
+import '../../tools/navigator_tool.dart';
+import '../../tools/screen/screen_config.dart';
+import '../../tools/screen/screen_extension.dart';
+import '../../widgets/base_widget.dart';
+
+class InnerLevelPage extends StatefulWidget {
+ @override
+ _InnerLevelPageState createState() => _InnerLevelPageState();
+}
+
+class _InnerLevelPageState extends State {
+ void _back() {
+ NavigatorTool.pop();
+ }
+
+ bool loaded = false;
+
+ List innerLevels = LevelsInner.levels.values.toList();
+
+ @override
+ void initState() {
+ super.initState();
+ loadInnerLevels();
+ }
+
+ Future loadInnerLevels() async {
+ await ImageTool.loadInnerLevels();
+ setState(() {
+ loaded = true;
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return ScreenConfig(
+ builder: () => Scaffold(
+ backgroundColor: Colors.grey[100],
+ body: SafeArea(
+ child: Center(
+ child: Column(
+ children: [
+ _topAction(),
+ lExpanded(child: _innerLevelsList()),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ int checkedIndex;
+
+ Widget _innerLevelsList() {
+ return !loaded
+ ? CircularProgressIndicator()
+ : ListView.builder(
+ physics: BouncingScrollPhysics(),
+ itemCount: innerLevels.length,
+ itemBuilder: (_, index) {
+ return Container(
+ padding: EdgeInsets.all(2.vw),
+ margin: EdgeInsets.all(5.vw),
+ decoration: BoxDecoration(
+ color: checkedIndex == index
+ ? Colors.greenAccent
+ : Colors.grey[200],
+ borderRadius: BorderRadiusDirectional.circular(5.vw),
+ ),
+ child: LevelImageList(
+ items: innerLevels[index],
+ onTap: () async {
+ checkedIndex = index;
+ await Levels.sets(innerLevels[index]);
+ setState(() {});
+ },
+ ),
+ );
+ },
+ );
+ }
+
+ Widget _topAction() => Container(
+ padding: EdgeInsets.all(5.vw),
+ child: Row(
+ children: [
+ lIconButton(
+ Icons.chevron_left,
+ size: 10.vw,
+ color: Colors.black,
+ onTap: _back,
+ ),
+ lExpanded(
+ child: lText(
+ '更换主题',
+ bold: true,
+ size: 18,
+ )),
+ lIconButton(
+ Icons.chevron_left,
+ size: 10.vw,
+ color: Colors.transparent,
+ ),
+ ],
+ ),
+ );
+}
+
+class LevelImageList extends StatefulWidget {
+ final Function onTap;
+ final double width, height;
+ final List items;
+ LevelImageList({
+ this.items,
+ this.width,
+ this.height,
+ this.onTap,
+ });
+ @override
+ _LevelImageListState createState() => _LevelImageListState();
+}
+
+class _LevelImageListState extends State {
+ PageController controller;
+
+ void onTapImage(Level level) async {
+ if (widget.onTap != null) await widget.onTap();
+ }
+
+ int currentPage = 0;
+
+ void _pageListener() {
+ var next = controller.page.round();
+ if (currentPage != next) {
+ setState(() {
+ currentPage = next;
+ });
+ }
+ }
+
+ double get _screenWidth => widget.width ?? 100.vw;
+ double get _height => widget.height ?? 20.vw;
+
+ @override
+ void initState() {
+ super.initState();
+ controller = PageController(viewportFraction: _height / _screenWidth);
+ controller.addListener(_pageListener);
+ Future.delayed(Duration.zero, () async {
+ if (widget.items.length > 1) {
+ await controller.animateToPage(
+ 2, //居中
+ duration: Duration(milliseconds: 100),
+ curve: Curves.easeOutQuint,
+ );
+ }
+ });
+ }
+
+ @override
+ void dispose() {
+ controller?.dispose();
+ super.dispose();
+ }
+
+ Widget levelImageView(Level level, bool active) {
+ var top = active ? 0 : _height * 0.2;
+ return GestureDetector(
+ onTap: () async {
+ await onTapImage(level);
+ },
+ child: Container(
+ width: _height,
+ height: _height,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ AnimatedContainer(
+ width: _height,
+ height: _height - top,
+ duration: Duration(milliseconds: 500),
+ curve: Curves.easeOutQuint,
+ child: RawImage(
+ image: ImageTool.image(level.image),
+ fit: BoxFit.contain,
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ width: _screenWidth,
+ height: _height,
+ alignment: Alignment.center,
+ child: PageView.builder(
+ physics: BouncingScrollPhysics(),
+ controller: controller,
+ itemCount: widget.items.length,
+ itemBuilder: (context, int currentIndex) {
+ var active = currentIndex == currentPage;
+ return levelImageView(widget.items[currentIndex], active);
+ },
+ ),
+ );
+ }
+}
diff --git a/lib/pages/setting/level_setting_page.dart b/lib/pages/setting/level_setting_page.dart
new file mode 100644
index 0000000..400d0df
--- /dev/null
+++ b/lib/pages/setting/level_setting_page.dart
@@ -0,0 +1,283 @@
+import 'package:flutter/material.dart';
+
+import '../../game/level/level.dart';
+import '../../game/level/levels.dart';
+import '../../tools/image/image_crop_tool.dart';
+import '../../tools/image/image_tool.dart';
+import '../../tools/navigator_tool.dart';
+import '../../tools/screen/screen_config.dart';
+import '../../tools/screen/screen_extension.dart';
+import '../../widgets/base_widget.dart';
+
+class LevelSettingPage extends StatefulWidget {
+ @override
+ _LevelSettingPageState createState() => _LevelSettingPageState();
+}
+
+class _LevelSettingPageState extends State {
+ void _back() {
+ NavigatorTool.pop();
+ }
+
+ Future _pickImage() async {
+ final path = await ImageCropTool.crop();
+ if (path != null) {
+ await Levels.update(
+ levels[currentPage]..image = path,
+ index: currentPage,
+ );
+ setState(() {});
+ }
+ }
+
+ Future onTapImage(Level level) async => _pickImage();
+
+ Future next() async {
+ if (isLast) return;
+ await controller.nextPage(
+ duration: Duration(milliseconds: 300),
+ curve: Curves.easeOutQuint,
+ );
+ }
+
+ Future pre() async {
+ if (isFirst) return;
+ await controller.previousPage(
+ duration: Duration(milliseconds: 300),
+ curve: Curves.easeOutQuint,
+ );
+ }
+
+ bool get isLast => currentPage == levels.length - 1;
+ bool get isFirst => currentPage < 1;
+ int get currentPage => controller.page.toInt();
+
+ bool loaded = false;
+
+ List get levels => Levels.kLevels;
+
+ PageController controller = PageController(viewportFraction: 1);
+
+ @override
+ void initState() {
+ super.initState();
+ loadInnerLevels();
+ }
+
+ @override
+ void dispose() {
+ controller?.dispose();
+ super.dispose();
+ }
+
+ Future loadInnerLevels() async {
+ await ImageTool.loadAll();
+ setState(() {
+ loaded = true;
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return ScreenConfig(
+ builder: () => Scaffold(
+ backgroundColor: Colors.grey[100],
+ body: SafeArea(
+ child: Center(
+ child: Column(
+ children: [
+ _topAction(),
+ lExpanded(
+ child: _body(),
+ ),
+ _bottomAction(),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget _body() {
+ return !loaded
+ ? CircularProgressIndicator()
+ : Container(
+ child: Stack(
+ alignment: Alignment.center,
+ children: [
+ _Levels(),
+ Container(
+ padding: EdgeInsets.all(5.vw),
+ child: Row(
+ children: [
+ lIconButton(
+ Icons.chevron_left,
+ color: Colors.black,
+ onTap: pre,
+ ),
+ lExpanded(),
+ lIconButton(
+ Icons.chevron_right,
+ color: Colors.black,
+ onTap: next,
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ Widget _Levels() {
+ return LevelEditImageList(
+ width: 100.vw,
+ height: 100.vw,
+ controller: controller,
+ onTap: onTapImage,
+ );
+ }
+
+ Widget _topAction() => Container(
+ padding: EdgeInsets.all(5.vw),
+ child: Row(
+ children: [
+ lIconButton(
+ Icons.chevron_left,
+ size: 10.vw,
+ color: Colors.black,
+ onTap: _back,
+ ),
+ lExpanded(
+ child: lText(
+ '自定义图片',
+ bold: true,
+ size: 18,
+ )),
+ lIconButton(
+ Icons.chevron_left,
+ size: 10.vw,
+ color: Colors.transparent,
+ ),
+ ],
+ ),
+ );
+
+ Widget _bottomAction() => Container(
+ padding: EdgeInsets.all(10.vw),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ lButton(
+ '更换图片',
+ onTap: _pickImage,
+ height: 5.vw * 2.2,
+ colorBg: Colors.black,
+ colorText: Colors.white,
+ ),
+ ],
+ ),
+ );
+}
+
+class LevelEditImageList extends StatefulWidget {
+ final Function(Level level) onTap;
+ final double width, height;
+ final PageController controller;
+ LevelEditImageList({
+ this.width,
+ this.height,
+ this.onTap,
+ this.controller,
+ });
+ @override
+ _LevelEditImageListState createState() => _LevelEditImageListState();
+}
+
+class _LevelEditImageListState extends State {
+ PageController controller;
+
+ void onTapImage(Level level) async {
+ if (widget.onTap != null) await widget.onTap(level);
+ }
+
+ int currentPage = 0;
+ List get levels => Levels.kLevels;
+
+ void _pageListener() {
+ var next = controller.page.round();
+ if (currentPage != next) {
+ setState(() {
+ currentPage = next;
+ });
+ }
+ }
+
+ double get _screenWidth => widget.width ?? 100.vw;
+ double get _height => widget.height ?? 20.vw;
+
+ @override
+ void dispose() {
+ if (widget.controller == null) {
+ controller?.dispose();
+ }
+ super.dispose();
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ controller = widget.controller ??
+ PageController(viewportFraction: _height / _screenWidth);
+ controller.addListener(_pageListener);
+ }
+
+ Widget levelImageView(Level level, bool active) {
+ final size = Levels.radius(currentPage).vw * 2;
+ var top = active ? 0 : size * 0.3;
+ return GestureDetector(
+ onTap: () async {
+ await onTapImage(level);
+ },
+ child: Container(
+ width: _height,
+ height: _height,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ AnimatedContainer(
+ width: size,
+ height: size - top,
+ duration: Duration(milliseconds: 500),
+ curve: Curves.easeOutQuint,
+ child: RawImage(
+ image: ImageTool.image(level.image),
+ fit: BoxFit.contain,
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ width: _screenWidth,
+ height: _height,
+ alignment: Alignment.center,
+ child: PageView.builder(
+ physics: BouncingScrollPhysics(),
+ controller: controller,
+ itemCount: levels.length,
+ itemBuilder: (context, int currentIndex) {
+ var active = currentIndex == currentPage;
+ return levelImageView(levels[currentIndex], active);
+ },
+ ),
+ );
+ }
+}
diff --git a/lib/pages/setting/more_setting_page.dart b/lib/pages/setting/more_setting_page.dart
new file mode 100644
index 0000000..5488eb7
--- /dev/null
+++ b/lib/pages/setting/more_setting_page.dart
@@ -0,0 +1,78 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+import '../../game/game_setting.dart';
+import '../../game/game_state.dart';
+import '../../tools/navigator_tool.dart';
+import '../../tools/screen/screen_config.dart';
+import '../../tools/screen/screen_extension.dart';
+import '../../widgets/base_widget.dart';
+import 'background_page.dart';
+import 'inner_level_page.dart';
+import 'level_setting_page.dart';
+
+class MoreSettingPage extends StatelessWidget {
+ void _back() {
+ NavigatorTool.pop();
+ }
+
+
+ GameSetting get setting => GameState.gameSetting;
+
+ @override
+ Widget build(BuildContext context) {
+ return ScreenConfig(
+ builder: () => Scaffold(
+ backgroundColor: Colors.grey[100],
+ body: SafeArea(
+ child: Center(
+ child: Column(
+ children: [
+ _topAction(),
+ lExpanded(),
+ _button('更换主题', InnerLevelPage()),
+ lHeight(10.vw),
+ _button('更换背景', BackgroundPage()),
+ lHeight(10.vw),
+ _button('更换图片', LevelSettingPage()),
+ lExpanded(),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget _topAction() => Container(
+ padding: EdgeInsets.all(5.vw),
+ child: Row(
+ children: [
+ lIconButton(
+ Icons.chevron_left,
+ size: 10.vw,
+ color: Colors.black,
+ onTap: _back,
+ ),
+ lExpanded(
+ child: lText(
+ '高级设置',
+ bold: true,
+ size: 18,
+ )),
+ lIconButton(
+ Icons.chevron_left,
+ size: 10.vw,
+ color: Colors.transparent,
+ ),
+ ],
+ ),
+ );
+
+ Widget _button(String lable, Widget page) => lButton(lable,
+ height: 5.vw * 2.2,
+ colorBg: Colors.black,
+ colorText: Colors.white, onTap: () {
+ NavigatorTool.push(page);
+ });
+}
diff --git a/lib/particles/bloom_particle.dart b/lib/particles/bloom_particle.dart
new file mode 100644
index 0000000..0291949
--- /dev/null
+++ b/lib/particles/bloom_particle.dart
@@ -0,0 +1,84 @@
+import 'dart:math';
+import 'dart:ui';
+
+import 'package:flame/components.dart';
+import 'package:flame/composition.dart';
+import 'package:flame/particles.dart';
+import 'package:flutter/material.dart';
+
+import '../game/game_state.dart';
+import '../game/my_game.dart';
+import '../tools/size_tool.dart';
+
+class BloomPartcle {
+ MyGame gameRef;
+ BloomPartcle(this.gameRef);
+
+ static final List bloomColors = [
+ Colors.amberAccent,
+ Colors.pink,
+ Colors.blueAccent,
+ Colors.greenAccent,
+ Colors.purpleAccent,
+ Colors.deepOrange,
+ ];
+
+ static final Random rnd = Random();
+ double randomSpeed(double radius) =>
+ (2 + rnd.nextDouble() * 1) * gameRef.viewport.vw(radius);
+ double randomRadius(double radius) =>
+ (2 + rnd.nextDouble() * 1) * gameRef.viewport.vw(radius);
+ double randomAngle(double angle) => angle + rnd.nextDouble() * pi / 6;
+ double randomTime(double radius) => 0.2 + rnd.nextDouble() * 0.5;
+ int randomCount(double radius) => 6 + rnd.nextInt(4) + (radius ~/ 2);
+ Color randomColor(double radius) =>
+ bloomColors[(radius ~/ 2) % bloomColors.length];
+
+ void show(Offset position, double radius) {
+ if (!GameState.gameSetting.bloom) return;
+ final n = randomCount(radius);
+ final color = randomColor(radius);
+ gameRef.add(
+ ParticleComponent(
+ particle: Particle.generate(
+ count: n,
+ lifespan: randomTime(radius),
+ generator: (i) {
+ final angle = randomAngle((2 * pi / n) * i);
+ return generate(
+ position: position,
+ angle: Offset(sin(angle), cos(angle)),
+ radius: randomRadius(radius),
+ speed: randomSpeed(radius),
+ color: color,
+ );
+ },
+ ),
+ ),
+ );
+ }
+
+ AcceleratedParticle generate({
+ Offset position,
+ Offset angle,
+ double speed,
+ double radius,
+ Color color,
+ }) {
+ return AcceleratedParticle(
+ position: position,
+ speed: angle * speed,
+ acceleration: angle * radius,
+ child: ComputedParticle(
+ renderer: (canvas, particle) => canvas.drawCircle(
+ Offset.zero,
+ particle.progress * 5,
+ Paint()
+ ..color = Color.lerp(
+ color,
+ Colors.white,
+ particle.progress * 0.1,
+ ),
+ )));
+ }
+}
diff --git a/lib/tools/audio_tool.dart b/lib/tools/audio_tool.dart
new file mode 100644
index 0000000..b704208
--- /dev/null
+++ b/lib/tools/audio_tool.dart
@@ -0,0 +1,54 @@
+import 'package:flame_audio/flame_audio.dart';
+import 'package:flutter/foundation.dart';
+
+import '../game/game_state.dart';
+
+class AudioTool {
+ // static Future save(Image img) async {
+ // final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
+ // final bytes = await img.toBytes();
+ // final path = '/audios/$timestamp.a';
+ // await FileTool.write(path, bytes);
+ // return path;
+ // }
+
+ static void loadAll() async {
+ if (kIsWeb) return; //audioplayers不支持web端音频缓存
+ await FlameAudio.audioCache.loadAll([
+ 'boom.mp3',
+ 'boom1.mp3',
+ 'boom2.mp3',
+ 'cheer.mp3',
+ 'coin.wav',
+ 'fall.mp3',
+ 'mix.wav',
+ ]);
+ }
+
+ static int boomCount = 0;
+
+ static void play(String path) async {
+ if (!GameState.gameSetting.music) return;
+ await FlameAudio.play(path);
+ }
+
+ static void fall() => play('fall.mp3');
+ static void win() => play('cheer.mp3');
+ static void dead() => play('boom1.mp3');
+
+ static void mix() async {
+ boomCount++;
+ Future.delayed(Duration(milliseconds: 100), () {
+ if (boomCount == 1) {
+ play('boom2.mp3');
+ } else if (boomCount == 2) {
+ play('mix.wav');
+ } else if (boomCount == 3) {
+ play('coin.wav');
+ } else if (boomCount > 3) {
+ play('boom.mp3');
+ }
+ boomCount = 0;
+ });
+ }
+}
diff --git a/lib/tools/cache/audio_caches.dart b/lib/tools/cache/audio_caches.dart
new file mode 100644
index 0000000..e69de29
diff --git a/lib/tools/cache/image_caches.dart b/lib/tools/cache/image_caches.dart
new file mode 100644
index 0000000..14f7e75
--- /dev/null
+++ b/lib/tools/cache/image_caches.dart
@@ -0,0 +1,82 @@
+import 'dart:async';
+import 'dart:typed_data';
+import 'dart:ui';
+
+import 'package:flutter/services.dart';
+
+import '../file_tool.dart';
+
+class ImageCaches {
+ final Map _loadedFiles = {};
+
+ void clear() {
+ _loadedFiles.clear();
+ }
+
+ Image fromCache(String fileName) {
+ final image = _loadedFiles[fileName];
+ //todo default img
+ return image?.loadedImage ?? _loadedFiles.values.first.loadedImage;
+ }
+
+ Future loadBytes(String path, Uint8List bytes) async {
+ _loadedFiles[path] = _ImageLoader(_loadBytes(bytes));
+ return await _loadedFiles[path].retrieve();
+ }
+
+ Future loadImage(String path, Image img) async {
+ _loadedFiles[path] = _ImageLoader(Future.value(img));
+ return await _loadedFiles[path].retrieve();
+ }
+
+ Future> loadAll(List fileNames) async {
+ return Future.wait(fileNames.map(load));
+ }
+
+ Future load(String fileName) async {
+ return fileName.startsWith('/') ? fromFile(fileName) : fromBundle(fileName);
+ }
+
+ Future fromBundle(String fileName) async {
+ if (!_loadedFiles.containsKey(fileName)) {
+ _loadedFiles[fileName] = _ImageLoader(_fetchToMemory(fileName));
+ }
+ return await _loadedFiles[fileName].retrieve();
+ }
+
+ Future fromFile(String path) async {
+ if (!_loadedFiles.containsKey(path)) {
+ _loadedFiles[path] = _ImageLoader(_fetchFromFile(path));
+ }
+ return await _loadedFiles[path].retrieve();
+ }
+
+ Future _fetchFromFile(String path) async {
+ final bytes = await FileTool.read(path);
+ return _loadBytes(bytes);
+ }
+
+ Future _fetchToMemory(String name) async {
+ final data = await rootBundle.load('assets/images/' + name);
+ final bytes = Uint8List.view(data.buffer);
+ return _loadBytes(bytes);
+ }
+
+ Future _loadBytes(Uint8List bytes) {
+ final completer = Completer();
+ decodeImageFromList(bytes, (image) => completer.complete(image));
+ return completer.future;
+ }
+}
+
+class _ImageLoader {
+ _ImageLoader(this.future);
+
+ Image loadedImage;
+ Future future;
+
+ Future retrieve() async {
+ loadedImage ??= await future;
+ return loadedImage;
+ }
+}
diff --git a/lib/tools/dialog_tool.dart b/lib/tools/dialog_tool.dart
new file mode 100644
index 0000000..80e19cc
--- /dev/null
+++ b/lib/tools/dialog_tool.dart
@@ -0,0 +1,61 @@
+import 'package:flutter/cupertino.dart';
+
+import '../tools/navigator_tool.dart';
+
+class DialogTool {
+ static Future show(
+ BuildContext context, {
+ String title,
+ String content,
+ Function action,
+ String actionText,
+ }) async {
+ await showCupertinoDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return CupertinoAlertDialog(
+ title: Text('$title'),
+ content: Text('$content'),
+ actions: [
+ if (actionText != null)
+ CupertinoDialogAction(
+ onPressed: () {
+ Navigator.of(context).pop();
+ if (action != null) action();
+ },
+ child: Text('$actionText'),
+ ),
+ CupertinoDialogAction(
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ child: Text('确定'),
+ ),
+ ],
+ );
+ });
+ }
+
+ static bool showing = false;
+
+ static void loading(String title) {
+ showing = true;
+ showCupertinoDialog(
+ context: NavigatorTool.gContext,
+ builder: (BuildContext context) {
+ return CupertinoAlertDialog(
+ title: Text('$title'),
+ content: Container(
+ padding: EdgeInsets.all(10),
+ child: Text('这需要一点时间,请稍候...'),
+ ),
+ );
+ });
+ }
+
+ static Future close() async {
+ if (!showing) return;
+ await Navigator.of(NavigatorTool.gContext).pop();
+ showing = false;
+ }
+}
diff --git a/lib/tools/file/file_io.dart b/lib/tools/file/file_io.dart
new file mode 100644
index 0000000..7f623cd
--- /dev/null
+++ b/lib/tools/file/file_io.dart
@@ -0,0 +1,29 @@
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:path_provider/path_provider.dart';
+
+class FileTool {
+ static Future read(String path) async {
+ final directory = await getApplicationDocumentsDirectory();
+ var file = File(directory.path + path);
+ if (!await file.exists()) return null;
+ return await file.readAsBytes();
+ }
+
+ static Future write(String path, Uint8List bytes) async {
+ final directory = await getApplicationDocumentsDirectory();
+ var file = File(directory.path + path);
+ if (!await file.exists()) {
+ file = await file.create(recursive: true);
+ }
+ await file.writeAsBytes(bytes, flush: true);
+ }
+
+ static Future delete(String path) async {
+ final directory = await getApplicationDocumentsDirectory();
+ var file = File(directory.path + path);
+ if (!await file.exists()) return;
+ file.deleteSync();
+ }
+}
diff --git a/lib/tools/file/file_web.dart b/lib/tools/file/file_web.dart
new file mode 100644
index 0000000..4e4f0f7
--- /dev/null
+++ b/lib/tools/file/file_web.dart
@@ -0,0 +1,23 @@
+import 'dart:typed_data';
+
+import '../hive_tool.dart';
+import '../image/ui_image_tool.dart';
+
+class FileTool {
+ static Future read(String path) async {
+ if (!HiveTool().inited) await HiveTool().init();
+ final value = await HiveTool().box.get(path);
+ if (value == null) return null;
+ return (value as String).toBytes();
+ }
+
+ static Future write(String path, Uint8List bytes) async {
+ if (!HiveTool().inited) await HiveTool().init();
+ await HiveTool().box.put(path, bytes.toBase64());
+ }
+
+ static Future delete(String path) async {
+ if (!HiveTool().inited) await HiveTool().init();
+ await HiveTool().box.delete(path);
+ }
+}
diff --git a/lib/tools/file_tool.dart b/lib/tools/file_tool.dart
new file mode 100644
index 0000000..08855bc
--- /dev/null
+++ b/lib/tools/file_tool.dart
@@ -0,0 +1 @@
+export 'file/file_io.dart' if (dart.library.html) 'file/file_web.dart';
diff --git a/lib/tools/hive_tool.dart b/lib/tools/hive_tool.dart
new file mode 100644
index 0000000..722c0ae
--- /dev/null
+++ b/lib/tools/hive_tool.dart
@@ -0,0 +1,35 @@
+import 'package:flutter/foundation.dart';
+import 'package:hive/hive.dart';
+import 'package:path_provider/path_provider.dart';
+
+class HiveTool {
+ factory HiveTool() => _singleton;
+ HiveTool._();
+ static final HiveTool _singleton = HiveTool._();
+
+ Box box;
+ String boxName = 'app';
+
+ bool get inited => box != null;
+
+ Future init() async {
+ if (kIsWeb) {
+ //nothing
+ } else {
+ final directory = await getApplicationDocumentsDirectory();
+ Hive.init(directory.path);
+ }
+ box = await Hive.openBox(boxName);
+ }
+
+ Future get(String key) async {
+ if (!inited) await HiveTool().init();
+ final value = await HiveTool().box.get(key);
+ return value as T;
+ }
+
+ Future set(String key, T value) async {
+ if (!HiveTool().inited) await HiveTool().init();
+ await HiveTool().box.put(key, value);
+ }
+}
diff --git a/lib/tools/image/image_crop_tool.dart b/lib/tools/image/image_crop_tool.dart
new file mode 100644
index 0000000..0858de6
--- /dev/null
+++ b/lib/tools/image/image_crop_tool.dart
@@ -0,0 +1,23 @@
+import 'package:flutter/foundation.dart';
+import 'package:image/image.dart';
+
+import '../../pages/setting/image_crop_page.dart';
+import '../../tools/dialog_tool.dart';
+import '../navigator_tool.dart';
+import 'image_pick_tool.dart';
+import 'image_tool.dart';
+
+class ImageCropTool {
+ static Future crop() async {
+ if (kIsWeb) {
+ DialogTool.loading('加载中');
+ final bytes = await ImagePickTool.pickImageAsBytes();
+ final image = decodeImage(bytes);
+ final croped = copyCropCircle(image);
+ final path = await ImageTool.saveBytes(encodePng(croped));
+ await DialogTool.close();
+ return path;
+ }
+ return await NavigatorTool.push(ImageCropPage());
+ }
+}
diff --git a/lib/tools/image/image_pick/image_pick_io.dart b/lib/tools/image/image_pick/image_pick_io.dart
new file mode 100644
index 0000000..773d606
--- /dev/null
+++ b/lib/tools/image/image_pick/image_pick_io.dart
@@ -0,0 +1,19 @@
+import 'dart:typed_data';
+import 'package:image_picker/image_picker.dart';
+
+import '../image_tool.dart';
+
+class ImagePickTool {
+ static final picker = ImagePicker();
+
+ static Future pickImageAsBytes() async {
+ final pickedFile = await picker.getImage(source: ImageSource.gallery);
+ return await pickedFile?.readAsBytes();
+ }
+
+ static Future pickImageAndSave() async {
+ final bytes = await pickImageAsBytes();
+ if (bytes == null) return null;
+ return await ImageTool.saveBytes(bytes);
+ }
+}
diff --git a/lib/tools/image/image_pick/image_pick_web.dart b/lib/tools/image/image_pick/image_pick_web.dart
new file mode 100644
index 0000000..e421489
--- /dev/null
+++ b/lib/tools/image/image_pick/image_pick_web.dart
@@ -0,0 +1,20 @@
+import 'dart:typed_data';
+import 'package:flutter/foundation.dart';
+import 'package:image_picker_web_redux/image_picker_web_redux.dart';
+
+import '../image_tool.dart';
+
+class ImagePickTool {
+ static Future pickImageAsBytes() async {
+ if (kIsWeb) {
+ return await ImagePickerWeb.getImage(outputType: ImageType.bytes);
+ }
+ return null;
+ }
+
+ static Future pickImageAndSave() async {
+ final bytes = await pickImageAsBytes();
+ if (bytes == null) return null;
+ return await ImageTool.saveBytes(bytes);
+ }
+}
diff --git a/lib/tools/image/image_pick_tool.dart b/lib/tools/image/image_pick_tool.dart
new file mode 100644
index 0000000..2ca77c3
--- /dev/null
+++ b/lib/tools/image/image_pick_tool.dart
@@ -0,0 +1 @@
+export 'image_pick/image_pick_io.dart' if (dart.library.html) 'image_pick/image_pick_web.dart';
\ No newline at end of file
diff --git a/lib/tools/image/image_tool.dart b/lib/tools/image/image_tool.dart
new file mode 100644
index 0000000..0325379
--- /dev/null
+++ b/lib/tools/image/image_tool.dart
@@ -0,0 +1,66 @@
+import 'dart:typed_data';
+import 'dart:ui';
+
+import '../../game/level/levels.dart';
+import '../../game/level/levels_inner.dart';
+import '../../tools/cache/image_caches.dart';
+import '../file_tool.dart';
+
+import 'ui_image_tool.dart';
+
+class ImageTool {
+ static ImageCaches imageCaches = ImageCaches();
+
+ static void loadAll() async {
+ if (!Levels.inited) {
+ await Levels.init();
+ }
+ final levelImages = Levels.kLevels.map((e) => e.image).toList();
+ await imageCaches.loadAll([
+ 'bg.png',
+ 'shine.png',
+ 'setting.png',
+ 'dead_line.png',
+ ...levelImages,
+ Levels.background,
+ ]);
+ }
+
+ static void loadInnerLevels() async {
+ if (!Levels.inited) {
+ await Levels.init();
+ }
+ final levelsImages = [];
+ LevelsInner.levels.values.forEach((levels) {
+ final images = levels.map((e) => e.image).toList();
+ levelsImages.addAll(images);
+ });
+ await imageCaches.loadAll(levelsImages);
+ }
+
+ static Image image(String path) => imageCaches.fromCache(path);
+
+ static Future imageBytes(String path) async =>
+ image(path).toBytes();
+
+ static Future saveImage(Image img) async {
+ if (img == null) return null;
+ final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
+ final path = '/images/$timestamp.m';
+ final bytes = await img.toBytes();
+ await imageCaches.loadImage(path, img); //直接加载到内存
+ await FileTool.write(path, bytes); //持久化到本地
+ // await imageCaches.load(path); //从本地加载到内存
+ return path;
+ }
+
+ static Future saveBytes(Uint8List bytes) async {
+ if (bytes == null || bytes.isEmpty) return null;
+ final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
+ final path = '/images/$timestamp.m';
+ await imageCaches.loadBytes(path, bytes); //直接加载到内存
+ await FileTool.write(path, bytes); //持久化到本地
+ // await imageCaches.load(path); //从本地加载到内存
+ return path;
+ }
+}
diff --git a/lib/tools/image/ui_image_tool.dart b/lib/tools/image/ui_image_tool.dart
new file mode 100644
index 0000000..b010ce9
--- /dev/null
+++ b/lib/tools/image/ui_image_tool.dart
@@ -0,0 +1,18 @@
+import 'dart:typed_data';
+import 'dart:convert';
+import 'dart:ui';
+
+extension UiImageToBytes on Image {
+ Future toBytes() async {
+ final byteData = await toByteData(format: ImageByteFormat.png);
+ return byteData.buffer.asUint8List();
+ }
+}
+
+extension Base64ToBytes on String {
+ Uint8List toBytes() => base64Decode(this);
+}
+
+extension BytesToBase64 on Uint8List {
+ String toBase64() => base64Encode(this);
+}
diff --git a/lib/tools/navigator_tool.dart b/lib/tools/navigator_tool.dart
new file mode 100644
index 0000000..04a4ba6
--- /dev/null
+++ b/lib/tools/navigator_tool.dart
@@ -0,0 +1,41 @@
+import 'package:flutter/widgets.dart';
+
+class NavigatorTool extends NavigatorObserver {
+ factory NavigatorTool() => _instance;
+ NavigatorTool._();
+ static final NavigatorTool _instance = NavigatorTool._();
+
+ static BuildContext get gContext => _instance.navigator.context;
+
+ static void pop([T result]) =>
+ _instance.navigator.pop(result);
+
+ static Future push(Widget child) => _instance.navigator.push(_page(child));
+
+ static Future replace(Widget child) =>
+ _instance.navigator.pushReplacement(_page(child));
+
+ static Route _page(Widget child) {
+ return PageRouteBuilder(
+ opaque: false,
+ pageBuilder: (_, animation, __) => _animationPage(animation, child),
+ transitionsBuilder: (_, animation, __, child) =>
+ _animationPage(animation, child),
+ );
+ }
+
+ static Widget _animationPage(Animation animation, Widget child) {
+ final value = Tween(begin: 0.0, end: 1.0).animate(
+ CurvedAnimation(
+ parent: animation,
+ curve: Curves.fastOutSlowIn,
+ ),
+ );
+ return FadeTransition(
+ opacity: value,
+ child: ScaleTransition(
+ scale: value,
+ child: child,
+ ));
+ }
+}
diff --git a/lib/tools/screen/screen_config.dart b/lib/tools/screen/screen_config.dart
new file mode 100644
index 0000000..b26fe79
--- /dev/null
+++ b/lib/tools/screen/screen_config.dart
@@ -0,0 +1,36 @@
+import 'package:flutter/widgets.dart';
+
+import 'screen_tool.dart';
+
+class ScreenConfig extends StatelessWidget {
+ ScreenConfig({
+ @required this.builder,
+ this.designSize = ScreenTool.defaultSize,
+ this.allowFontScaling = false,
+ Key key,
+ }) : super(key: key);
+
+ final Widget Function() builder;
+
+ /// 设计稿尺寸,单位px
+ final Size designSize;
+
+ /// 是否允许字体大小缩放
+ final bool allowFontScaling;
+
+ @override
+ Widget build(BuildContext context) {
+ return LayoutBuilder(
+ builder: (_, BoxConstraints constraints) {
+ if (constraints.maxWidth != 0) {
+ ScreenTool().init(
+ constraints,
+ designSize: designSize,
+ allowFontScaling: allowFontScaling,
+ );
+ }
+ return builder();
+ },
+ );
+ }
+}
diff --git a/lib/tools/screen/screen_extension.dart b/lib/tools/screen/screen_extension.dart
new file mode 100644
index 0000000..447de1b
--- /dev/null
+++ b/lib/tools/screen/screen_extension.dart
@@ -0,0 +1,51 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+
+import 'screen_tool.dart';
+
+extension SizeExtension on num {
+ double get w => ScreenTool().width(this);
+ double get h => ScreenTool().height(this);
+ double get vw => ScreenTool().screenWidth * this * 0.01;
+ double get vh => ScreenTool().screenHeight * this * 0.01;
+ double get r => ScreenTool().radius(this);
+ double get px => ScreenTool().width(this); //px2dp
+ double get sp => ScreenTool().font(this);
+ double get ssp => ScreenTool().font(this, allowScaling: true);
+ double get nsp => ScreenTool().font(this, allowScaling: false);
+}
+
+extension ContextExtensionss on BuildContext {
+ Orientation get orientation => MediaQuery.of(this).orientation;
+
+ ///横屏
+ bool get isLandscape => orientation == Orientation.landscape;
+
+ ///竖屏
+ bool get isPortrait => orientation == Orientation.portrait;
+
+ ///最短边
+ double get shortestSide => mediaQuery.size.shortestSide;
+ double get height => mediaQuery.size.height;
+ double get width => mediaQuery.size.width;
+ ThemeData get theme => Theme.of(this);
+ bool get isDarkMode => theme.brightness == Brightness.dark;
+ Color get iconColor => theme.iconTheme.color;
+ TextTheme get textTheme => theme.textTheme;
+ MediaQueryData get mediaQuery => MediaQuery.of(this);
+ bool get showNavbar => (width > 800);
+ double get devicePixelRatio => MediaQuery.of(this).devicePixelRatio;
+ double get textScaleFactor => MediaQuery.of(this).textScaleFactor;
+
+ T responsiveValue({
+ T mobile,
+ T tablet,
+ T desktop,
+ T watch,
+ }) {
+ if (shortestSide >= 1200 && desktop != null) return desktop;
+ if (shortestSide >= 600 && tablet != null) return tablet;
+ if (shortestSide < 300 && watch != null) return watch;
+ return mobile;
+ }
+}
diff --git a/lib/tools/screen/screen_tool.dart b/lib/tools/screen/screen_tool.dart
new file mode 100644
index 0000000..5f01f42
--- /dev/null
+++ b/lib/tools/screen/screen_tool.dart
@@ -0,0 +1,97 @@
+import 'dart:math';
+import 'dart:ui' as ui;
+
+import 'package:flutter/material.dart';
+
+class ScreenTool {
+ factory ScreenTool() => _instance;
+ ScreenTool._();
+ static final ScreenTool _instance = ScreenTool._();
+
+ static const Size defaultSize = Size(1080, 1920);
+
+ /// 设计稿尺寸,单位px
+ Size uiSize;
+
+ /// 是否允许字体大小缩放
+ bool allowFontScaling;
+
+ double _pixelRatio = 1;
+ double _textScaleFactor = 1;
+ double _screenWidth = 0;
+ double _screenHeight = 0;
+ double _statusBarHeight = 0;
+ double _bottomBarHeight = 0;
+
+ void init(
+ BoxConstraints constraints, {
+ Size designSize,
+ bool allowFontScaling,
+ }) {
+ designSize ??= defaultSize;
+ allowFontScaling ??= false;
+ uiSize = designSize;
+ this.allowFontScaling = allowFontScaling;
+
+ _screenWidth = constraints.maxWidth; //此处是当前组件的最大宽度
+ _screenHeight = constraints.maxHeight; //此处是当前组件的最大高度度
+
+ var window = WidgetsBinding.instance?.window ?? ui.window;
+
+ _pixelRatio = window.devicePixelRatio;
+ _statusBarHeight = window.padding.top;
+ _bottomBarHeight = window.padding.bottom;
+ _textScaleFactor = window.textScaleFactor;
+ }
+
+ /// 根据UI设计的设备宽度适配
+ double width(num width) => width * scaleWidth;
+
+ /// 根据UI设计的设备高度适配
+ double height(num height) => height * scaleHeight;
+
+ /// 根据宽度或高度中的较小值进行适配
+ double radius(num r) => r * scaleText;
+
+ /// 字体大小适配方法,单位px.
+ double font(num fontSize, {bool allowScaling}) => allowScaling == null
+ ? (allowFontScaling
+ ? (fontSize * scaleText) * _textScaleFactor
+ : (fontSize * scaleText))
+ : (allowScaling
+ ? (fontSize * scaleText) * _textScaleFactor
+ : (fontSize * scaleText));
+
+ /// 每个逻辑像素的字体像素数,字体的缩放比例
+ double get textScaleFactor => _textScaleFactor;
+
+ /// 设备的像素密度
+ double get pixelRatio => _pixelRatio;
+
+ /// 当前设备宽度 dp
+ double get screenWidth => _screenWidth;
+
+ /// 当前设备高度 dp
+ double get screenHeight => _screenHeight;
+
+ /// 当前设备最短边 dp
+ double get screenMin => min(_screenWidth, _screenHeight);
+
+ /// 当前设备最长边 dp
+ double get screenMax => max(_screenWidth, _screenHeight);
+
+ /// 状态栏高度 dp 刘海屏会更高
+ double get statusBarHeight => _statusBarHeight / _pixelRatio;
+
+ /// 底部安全区距离 dp
+ double get bottomBarHeight => _bottomBarHeight / _pixelRatio;
+
+ /// 实际宽度与设计宽度的比例
+ double get scaleWidth => _screenWidth / uiSize.width;
+
+ /// 实际高度与设计高度的比例
+ double get scaleHeight => _screenHeight / uiSize.height;
+
+ /// 实际字体大小与设计字体大小的比例
+ double get scaleText => min(scaleWidth, scaleHeight);
+}
diff --git a/lib/tools/sensor_tool.dart b/lib/tools/sensor_tool.dart
new file mode 100644
index 0000000..0d4f7aa
--- /dev/null
+++ b/lib/tools/sensor_tool.dart
@@ -0,0 +1,34 @@
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:sensors/sensors.dart';
+
+class XYZ {
+ XYZ(this.x, this.y, this.z);
+ final double x;
+ final double y;
+ final double z;
+
+ @override
+ String toString() => 'x: $x, y: $y, z: $z';
+}
+
+class SensorTool {
+ static StreamSubscription _stream;
+
+ static XYZ _xyz;
+
+ static XYZ get xyz => _xyz ?? XYZ(0, 0, 0);
+
+ static Future start() async {
+ if (kIsWeb) return;
+ await stop();
+ _stream = accelerometerEvents.listen((data) {
+ _xyz = XYZ(data.x, data.y, data.z);
+ });
+ }
+
+ static Future stop() async {
+ if (!kIsWeb) await _stream?.cancel();
+ }
+}
diff --git a/lib/tools/share_tool.dart b/lib/tools/share_tool.dart
new file mode 100644
index 0000000..2877db9
--- /dev/null
+++ b/lib/tools/share_tool.dart
@@ -0,0 +1,5 @@
+import 'package:share_plus/share_plus.dart';
+
+class ShareTool {
+ static Future share(String text) async => Share.share('$text');
+}
diff --git a/lib/tools/size_tool.dart b/lib/tools/size_tool.dart
new file mode 100644
index 0000000..26dc22e
--- /dev/null
+++ b/lib/tools/size_tool.dart
@@ -0,0 +1,27 @@
+import 'package:flame/game.dart';
+import 'package:flame_forge2d/viewport.dart';
+import 'package:flutter/widgets.dart' hide Viewport;
+
+extension Size2Vector on Size {
+ Vector2 get toVector => Vector2(width, height);
+}
+
+extension Offset2Vector on Offset {
+ Vector2 get toVector => Vector2(dx, dy);
+}
+
+extension Vector2Offset on Vector2 {
+ Offset get toVector => Offset(x, y);
+ double get gravitySize => y / 600 * 50;
+ double get velocitySize => x / 6;
+}
+
+extension SizeVector on BuildContext {
+ Size get size => MediaQuery.of(this).size;
+ Vector2 get vector => Vector2(size.width, size.height);
+}
+
+extension VW on Viewport {
+ double vw(double percent) => percent * (size.x / 100);
+ double vh(double percent) => percent * (size.y / 100);
+}
diff --git a/lib/tools/system_tool.dart b/lib/tools/system_tool.dart
new file mode 100644
index 0000000..c5b50d2
--- /dev/null
+++ b/lib/tools/system_tool.dart
@@ -0,0 +1,23 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+
+class SystemTool {
+ static void changeStatusBarColor([Color color]) {
+ color ??= Colors.white;
+ final style = SystemUiOverlayStyle(
+ statusBarColor: color,
+ systemNavigationBarDividerColor: null,
+ systemNavigationBarColor: color,
+ systemNavigationBarIconBrightness: Brightness.dark,
+ statusBarIconBrightness: Brightness.dark,
+ );
+ SystemChrome.setSystemUIOverlayStyle(style);
+ }
+
+ static void keepPortrait() {
+ SystemChrome.setPreferredOrientations([
+ DeviceOrientation.portraitUp,
+ DeviceOrientation.portraitDown,
+ ]);
+ }
+}
diff --git a/lib/tools/url_tool.dart b/lib/tools/url_tool.dart
new file mode 100644
index 0000000..d5293d3
--- /dev/null
+++ b/lib/tools/url_tool.dart
@@ -0,0 +1,9 @@
+import 'package:url_launcher/url_launcher.dart';
+
+class UrlTool {
+ static Future open(String url) async {
+ if (await canLaunch(url)) {
+ await launch(url);
+ }
+ }
+}
diff --git a/lib/widgets/base_widget.dart b/lib/widgets/base_widget.dart
new file mode 100644
index 0000000..289a1ab
--- /dev/null
+++ b/lib/widgets/base_widget.dart
@@ -0,0 +1,133 @@
+import 'package:flutter/material.dart';
+
+TextStyle lTextStyle({
+ Color color,
+ double size,
+ bool bold,
+}) {
+ size ??= 14;
+ bold ??= false;
+ color ??= Colors.black;
+ return TextStyle(
+ color: color,
+ fontSize: size,
+ fontWeight: bold ? FontWeight.bold : FontWeight.normal,
+ );
+}
+
+Widget lText(
+ String text, {
+ Color color,
+ double size,
+ bool bold,
+ TextOverflow overflow,
+}) {
+ size ??= 14;
+ bold ??= false;
+ color ??= Colors.black;
+ return Text(
+ '$text',
+ overflow: overflow,
+ style: TextStyle(
+ color: color,
+ fontSize: size,
+ fontWeight: bold ? FontWeight.bold : FontWeight.normal,
+ ),
+ );
+}
+
+Widget lHeight(double height) {
+ height ??= 10;
+ return SizedBox(height: height);
+}
+
+Widget lWidth(double width) {
+ width ??= 10;
+ return SizedBox(width: width);
+}
+
+Widget lBlank() {
+ return Container();
+}
+
+Widget lExpanded({int flex, Widget child}) {
+ flex ??= 1;
+ child ??= Container();
+ return Expanded(flex: flex, child: Center(child: child));
+}
+
+Widget lTextField({
+ String hintText,
+ double padding,
+ Function(String) onChanged,
+ Function(String) onSubmitted,
+ TextInputType keyboardType,
+}) {
+ hintText ??= '请输入...';
+ padding ??= 10;
+ onChanged ??= (_) {};
+ onSubmitted ??= (_) {};
+ keyboardType ??= TextInputType.number;
+ return Container(
+ child: TextField(
+ onChanged: onChanged,
+ onSubmitted: onSubmitted,
+ keyboardType: keyboardType,
+ decoration: InputDecoration(
+ contentPadding: EdgeInsets.all(padding),
+ hintText: hintText,
+ border: OutlineInputBorder(),
+ ),
+ ),
+ );
+}
+
+Widget lButton(
+ String text, {
+ void Function() onTap,
+ double height,
+ double fontSize,
+ double width,
+ double radius,
+ Color colorText,
+ Color colorBg,
+}) {
+ height ??= 30;
+ width ??= height * 2 / 0.7;
+ radius ??= height / 2;
+ fontSize ??= 14;
+ colorText ??= Colors.black;
+ colorBg ??= Colors.grey[100];
+ return GestureDetector(
+ onTap: onTap,
+ child: Container(
+ width: width,
+ height: height,
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ color: colorBg,
+ borderRadius: BorderRadiusDirectional.all(Radius.circular(radius))),
+ child: lText(text,
+ color: colorText ?? Colors.black, size: fontSize, bold: true),
+ ),
+ );
+}
+
+Widget lIconButton(
+ IconData icon, {
+ double size,
+ Color color,
+ Function onTap,
+}) {
+ size ??= 36;
+ color ??= Colors.white;
+ return IconButton(
+ iconSize: size,
+ icon: Icon(
+ icon,
+ size: size,
+ color: color,
+ ),
+ onPressed: onTap,
+ );
+}
diff --git a/lib/widgets/pulse.dart b/lib/widgets/pulse.dart
new file mode 100644
index 0000000..0aae709
--- /dev/null
+++ b/lib/widgets/pulse.dart
@@ -0,0 +1,42 @@
+
+import 'package:flutter/material.dart';
+
+class Pulse extends StatefulWidget {
+ final Widget child;
+ Pulse({this.child});
+ @override
+ _PulseState createState() => _PulseState();
+}
+
+class _PulseState extends State with SingleTickerProviderStateMixin {
+ AnimationController _controller;
+
+ @override
+ void initState() {
+ super.initState();
+ _controller = AnimationController(
+ duration: Duration(milliseconds: 1200),
+ vsync: this,
+ )..repeat();
+ }
+
+ @override
+ void dispose() {
+ _controller.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return AnimatedBuilder(
+ animation: _controller,
+ child: widget.child,
+ builder: (BuildContext context, Widget child) {
+ return Transform.scale(
+ scale: 0.6 + 0.4 * (_controller.value % 0.5),
+ child: child,
+ );
+ },
+ );
+ }
+}
diff --git a/lib/widgets/spinner.dart b/lib/widgets/spinner.dart
new file mode 100644
index 0000000..9c633d7
--- /dev/null
+++ b/lib/widgets/spinner.dart
@@ -0,0 +1,43 @@
+import 'dart:math';
+
+import 'package:flutter/material.dart';
+
+class Spinner extends StatefulWidget {
+ final Widget child;
+ Spinner({this.child});
+ @override
+ _SpinnerState createState() => _SpinnerState();
+}
+
+class _SpinnerState extends State with SingleTickerProviderStateMixin {
+ AnimationController _controller;
+
+ @override
+ void initState() {
+ super.initState();
+ _controller = AnimationController(
+ duration: const Duration(seconds: 10),
+ vsync: this,
+ )..repeat();
+ }
+
+ @override
+ void dispose() {
+ _controller.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return AnimatedBuilder(
+ animation: _controller,
+ child: widget.child,
+ builder: (BuildContext context, Widget child) {
+ return Transform.rotate(
+ angle: _controller.value * 2.0 * pi,
+ child: child,
+ );
+ },
+ );
+ }
+}
diff --git a/lib/widgets/ui_image.dart b/lib/widgets/ui_image.dart
new file mode 100644
index 0000000..7758bdc
--- /dev/null
+++ b/lib/widgets/ui_image.dart
@@ -0,0 +1,29 @@
+import 'dart:typed_data';
+
+import 'package:flutter/material.dart';
+
+import '../tools/image/image_tool.dart';
+
+class UiImage extends StatelessWidget {
+ final String path;
+ final Function(Uint8List) builder;
+
+ UiImage(this.path, {@required this.builder});
+
+ @override
+ Widget build(BuildContext context) {
+ // 等价于
+ // return RawImage(image: ImageTool.image(path));
+ return FutureBuilder(
+ future: ImageTool.imageBytes(path),
+ builder: (context, snapshot) {
+ switch (snapshot.connectionState) {
+ case ConnectionState.done:
+ return builder(snapshot.data);
+ default:
+ return Container();
+ }
+ },
+ );
+ }
+}
diff --git a/logo.png b/logo.png
new file mode 100644
index 0000000..e5abd07
Binary files /dev/null and b/logo.png differ
diff --git a/macos/.DS_Store b/macos/.DS_Store
new file mode 100644
index 0000000..98237d5
Binary files /dev/null and b/macos/.DS_Store differ
diff --git a/macos/.gitignore b/macos/.gitignore
new file mode 100644
index 0000000..d2fd377
--- /dev/null
+++ b/macos/.gitignore
@@ -0,0 +1,6 @@
+# Flutter-related
+**/Flutter/ephemeral/
+**/Pods/
+
+# Xcode-related
+**/xcuserdata/
diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig
new file mode 100644
index 0000000..4b81f9b
--- /dev/null
+++ b/macos/Flutter/Flutter-Debug.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig
new file mode 100644
index 0000000..5caa9d1
--- /dev/null
+++ b/macos/Flutter/Flutter-Release.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift
new file mode 100644
index 0000000..d2b1699
--- /dev/null
+++ b/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -0,0 +1,16 @@
+//
+// Generated file. Do not edit.
+//
+
+import FlutterMacOS
+import Foundation
+
+import audioplayers
+import path_provider_macos
+import url_launcher_macos
+
+func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
+ AudioplayersPlugin.register(with: registry.registrar(forPlugin: "AudioplayersPlugin"))
+ PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
+ UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
+}
diff --git a/macos/Podfile b/macos/Podfile
new file mode 100644
index 0000000..dade8df
--- /dev/null
+++ b/macos/Podfile
@@ -0,0 +1,40 @@
+platform :osx, '10.11'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_macos_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+ use_modular_headers!
+
+ flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_macos_build_settings(target)
+ end
+end
diff --git a/macos/Podfile.lock b/macos/Podfile.lock
new file mode 100644
index 0000000..e18c39b
--- /dev/null
+++ b/macos/Podfile.lock
@@ -0,0 +1,34 @@
+PODS:
+ - audioplayers (0.0.1):
+ - FlutterMacOS
+ - FlutterMacOS (1.0.0)
+ - path_provider_macos (0.0.1):
+ - FlutterMacOS
+ - url_launcher_macos (0.0.1):
+ - FlutterMacOS
+
+DEPENDENCIES:
+ - audioplayers (from `Flutter/ephemeral/.symlinks/plugins/audioplayers/macos`)
+ - FlutterMacOS (from `Flutter/ephemeral`)
+ - path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
+ - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
+
+EXTERNAL SOURCES:
+ audioplayers:
+ :path: Flutter/ephemeral/.symlinks/plugins/audioplayers/macos
+ FlutterMacOS:
+ :path: Flutter/ephemeral
+ path_provider_macos:
+ :path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
+ url_launcher_macos:
+ :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
+
+SPEC CHECKSUMS:
+ audioplayers: 882556ed7be47c58b26f1098177c7e814d8a1f2a
+ FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
+ path_provider_macos: a0a3fd666cb7cd0448e936fb4abad4052961002b
+ url_launcher_macos: 45af3d61de06997666568a7149c1be98b41c95d4
+
+PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c
+
+COCOAPODS: 1.9.3
diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..4edef71
--- /dev/null
+++ b/macos/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,644 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 51;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
+ buildPhases = (
+ 33CC111E2044C6BF0003C045 /* ShellScript */,
+ );
+ dependencies = (
+ );
+ name = "Flutter Assemble";
+ productName = FLX;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
+ CF2ACE8E1975D9AE44035182 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 823B980B951A3F1A74A7A904 /* Pods_Runner.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 33CC111A2044C6BA0003C045;
+ remoteInfo = FLX;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 33CC110E2044A8840003C045 /* Bundle Framework */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Bundle Framework";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; };
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; };
+ 33CC10ED2044A3C60003C045 /* watermelon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = watermelon.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; };
+ 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
+ 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; };
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; };
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; };
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; };
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; };
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; };
+ 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; };
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; };
+ 5AD90A13C074106A052038D4 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; };
+ 823B980B951A3F1A74A7A904 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 8BB218566A43A6FA03D917FD /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; };
+ A3D59C8347626AC9BE5A4D13 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 33CC10EA2044A3C60003C045 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ CF2ACE8E1975D9AE44035182 /* Pods_Runner.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 33BA886A226E78AF003329D5 /* Configs */ = {
+ isa = PBXGroup;
+ children = (
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
+ );
+ path = Configs;
+ sourceTree = "";
+ };
+ 33CC10E42044A3C60003C045 = {
+ isa = PBXGroup;
+ children = (
+ 33FAB671232836740065AC1E /* Runner */,
+ 33CEB47122A05771004F2AC0 /* Flutter */,
+ 33CC10EE2044A3C60003C045 /* Products */,
+ D73912EC22F37F3D000D13A0 /* Frameworks */,
+ 5EAD3A561E2AE6D947D06927 /* Pods */,
+ );
+ sourceTree = "";
+ };
+ 33CC10EE2044A3C60003C045 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10ED2044A3C60003C045 /* watermelon.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 33CC11242044D66E0003C045 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */,
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */,
+ 33CC10F72044A3C60003C045 /* Info.plist */,
+ );
+ name = Resources;
+ path = ..;
+ sourceTree = "";
+ };
+ 33CEB47122A05771004F2AC0 /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
+ );
+ path = Flutter;
+ sourceTree = "";
+ };
+ 33FAB671232836740065AC1E /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */,
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */,
+ 33E51914231749380026EE4D /* Release.entitlements */,
+ 33CC11242044D66E0003C045 /* Resources */,
+ 33BA886A226E78AF003329D5 /* Configs */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ 5EAD3A561E2AE6D947D06927 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ A3D59C8347626AC9BE5A4D13 /* Pods-Runner.debug.xcconfig */,
+ 5AD90A13C074106A052038D4 /* Pods-Runner.release.xcconfig */,
+ 8BB218566A43A6FA03D917FD /* Pods-Runner.profile.xcconfig */,
+ );
+ name = Pods;
+ path = Pods;
+ sourceTree = "";
+ };
+ D73912EC22F37F3D000D13A0 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 823B980B951A3F1A74A7A904 /* Pods_Runner.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 33CC10EC2044A3C60003C045 /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ EBC39B516F739D4C97913235 /* [CP] Check Pods Manifest.lock */,
+ 33CC10E92044A3C60003C045 /* Sources */,
+ 33CC10EA2044A3C60003C045 /* Frameworks */,
+ 33CC10EB2044A3C60003C045 /* Resources */,
+ 33CC110E2044A8840003C045 /* Bundle Framework */,
+ 3399D490228B24CF009A79C7 /* ShellScript */,
+ 2077C5ABFC9CBA472D96CBE6 /* [CP] Embed Pods Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */,
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 33CC10ED2044A3C60003C045 /* watermelon.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 33CC10E52044A3C60003C045 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 0920;
+ LastUpgradeCheck = 0930;
+ ORGANIZATIONNAME = "The Flutter Authors";
+ TargetAttributes = {
+ 33CC10EC2044A3C60003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ LastSwiftMigration = 1100;
+ ProvisioningStyle = Automatic;
+ SystemCapabilities = {
+ com.apple.Sandbox = {
+ enabled = 1;
+ };
+ };
+ };
+ 33CC111A2044C6BA0003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Manual;
+ };
+ };
+ };
+ buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 8.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 33CC10E42044A3C60003C045;
+ productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 33CC10EC2044A3C60003C045 /* Runner */,
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 33CC10EB2044A3C60003C045 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 2077C5ABFC9CBA472D96CBE6 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 3399D490228B24CF009A79C7 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
+ };
+ 33CC111E2044C6BF0003C045 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ Flutter/ephemeral/FlutterInputs.xcfilelist,
+ );
+ inputPaths = (
+ Flutter/ephemeral/tripwire,
+ );
+ outputFileListPaths = (
+ Flutter/ephemeral/FlutterOutputs.xcfilelist,
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
+ };
+ EBC39B516F739D4C97913235 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 33CC10E92044A3C60003C045 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
+ targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 33CC10F52044A3C60003C045 /* Base */,
+ );
+ name = MainMenu.xib;
+ path = Runner;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 338D0CE9231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ 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_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ 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_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Profile;
+ };
+ 338D0CEA231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter/ephemeral",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Profile;
+ };
+ 338D0CEB231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Profile;
+ };
+ 33CC10F92044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ 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_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ 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_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ 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_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 33CC10FA2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ 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_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ 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_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 33CC10FC2044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter/ephemeral",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 33CC10FD2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter/ephemeral",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+ 33CC111C2044C6BA0003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 33CC111D2044C6BA0003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10F92044A3C60003C045 /* Debug */,
+ 33CC10FA2044A3C60003C045 /* Release */,
+ 338D0CE9231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10FC2044A3C60003C045 /* Debug */,
+ 33CC10FD2044A3C60003C045 /* Release */,
+ 338D0CEA231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC111C2044C6BA0003C045 /* Debug */,
+ 33CC111D2044C6BA0003C045 /* Release */,
+ 338D0CEB231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 33CC10E52044A3C60003C045 /* Project object */;
+}
diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..12cf116
--- /dev/null
+++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..21a3cc1
--- /dev/null
+++ b/macos/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift
new file mode 100644
index 0000000..d53ef64
--- /dev/null
+++ b/macos/Runner/AppDelegate.swift
@@ -0,0 +1,9 @@
+import Cocoa
+import FlutterMacOS
+
+@NSApplicationMain
+class AppDelegate: FlutterAppDelegate {
+ override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
+ return true
+ }
+}
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..a2ec33f
--- /dev/null
+++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+ "images" : [
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_16.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_64.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_128.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_1024.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
new file mode 100644
index 0000000..3c4935a
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
new file mode 100644
index 0000000..ed4cc16
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
new file mode 100644
index 0000000..483be61
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
new file mode 100644
index 0000000..bcbf36d
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
new file mode 100644
index 0000000..9c0a652
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
new file mode 100644
index 0000000..e71a726
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
new file mode 100644
index 0000000..8a31fe2
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ
diff --git a/macos/Runner/Base.lproj/MainMenu.xib b/macos/Runner/Base.lproj/MainMenu.xib
new file mode 100644
index 0000000..537341a
--- /dev/null
+++ b/macos/Runner/Base.lproj/MainMenu.xib
@@ -0,0 +1,339 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig
new file mode 100644
index 0000000..10cad7e
--- /dev/null
+++ b/macos/Runner/Configs/AppInfo.xcconfig
@@ -0,0 +1,14 @@
+// Application-level settings for the Runner target.
+//
+// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
+// future. If not, the values below would default to using the project name when this becomes a
+// 'flutter create' template.
+
+// The application's name. By default this is also the title of the Flutter window.
+PRODUCT_NAME = watermelon
+
+// The application's bundle identifier
+PRODUCT_BUNDLE_IDENTIFIER = cat.love.mix
+
+// The copyright displayed in application information
+PRODUCT_COPYRIGHT = Copyright © 2021 com.example. All rights reserved.
diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig
new file mode 100644
index 0000000..36b0fd9
--- /dev/null
+++ b/macos/Runner/Configs/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Debug.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig
new file mode 100644
index 0000000..dff4f49
--- /dev/null
+++ b/macos/Runner/Configs/Release.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Release.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig
new file mode 100644
index 0000000..42bcbf4
--- /dev/null
+++ b/macos/Runner/Configs/Warnings.xcconfig
@@ -0,0 +1,13 @@
+WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
+GCC_WARN_UNDECLARED_SELECTOR = YES
+CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
+CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
+CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
+CLANG_WARN_PRAGMA_PACK = YES
+CLANG_WARN_STRICT_PROTOTYPES = YES
+CLANG_WARN_COMMA = YES
+GCC_WARN_STRICT_SELECTOR_MATCH = YES
+CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
+CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
+GCC_WARN_SHADOW = YES
+CLANG_WARN_UNREACHABLE_CODE = YES
diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements
new file mode 100644
index 0000000..dddb8a3
--- /dev/null
+++ b/macos/Runner/DebugProfile.entitlements
@@ -0,0 +1,12 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.network.server
+
+
+
diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist
new file mode 100644
index 0000000..4789daa
--- /dev/null
+++ b/macos/Runner/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSMinimumSystemVersion
+ $(MACOSX_DEPLOYMENT_TARGET)
+ NSHumanReadableCopyright
+ $(PRODUCT_COPYRIGHT)
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift
new file mode 100644
index 0000000..2722837
--- /dev/null
+++ b/macos/Runner/MainFlutterWindow.swift
@@ -0,0 +1,15 @@
+import Cocoa
+import FlutterMacOS
+
+class MainFlutterWindow: NSWindow {
+ override func awakeFromNib() {
+ let flutterViewController = FlutterViewController.init()
+ let windowFrame = self.frame
+ self.contentViewController = flutterViewController
+ self.setFrame(windowFrame, display: true)
+
+ RegisterGeneratedPlugins(registry: flutterViewController)
+
+ super.awakeFromNib()
+ }
+}
diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements
new file mode 100644
index 0000000..852fa1a
--- /dev/null
+++ b/macos/Runner/Release.entitlements
@@ -0,0 +1,8 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+
+
diff --git a/pubspec.lock b/pubspec.lock
new file mode 100644
index 0000000..cd25b2d
--- /dev/null
+++ b/pubspec.lock
@@ -0,0 +1,515 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ archive:
+ dependency: transitive
+ description:
+ name: archive
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.0.13"
+ args:
+ dependency: transitive
+ description:
+ name: args
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.6.0"
+ async:
+ dependency: transitive
+ description:
+ name: async
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.5.0"
+ audioplayers:
+ dependency: transitive
+ description:
+ name: audioplayers
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.15.1"
+ boolean_selector:
+ dependency: transitive
+ description:
+ name: boolean_selector
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.1.0"
+ characters:
+ dependency: transitive
+ description:
+ name: characters
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.1.0"
+ charcode:
+ dependency: transitive
+ description:
+ name: charcode
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.2.0"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.1.0"
+ collection:
+ dependency: transitive
+ description:
+ name: collection
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.15.0"
+ convert:
+ dependency: transitive
+ description:
+ name: convert
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.1.1"
+ crop:
+ dependency: "direct main"
+ description:
+ path: "."
+ ref: circle
+ resolved-ref: "5dbcadaf6bdcb10463de0a49c52f96525299c601"
+ url: "https://github.com/idootop/flutter_crop.git"
+ source: git
+ version: "0.4.2"
+ crypto:
+ dependency: transitive
+ description:
+ name: crypto
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.1.5"
+ fake_async:
+ dependency: transitive
+ description:
+ name: fake_async
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.2.0"
+ ffi:
+ dependency: transitive
+ description:
+ name: ffi
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.1.3"
+ file:
+ dependency: transitive
+ description:
+ name: file
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "5.2.1"
+ flame:
+ dependency: "direct main"
+ description:
+ name: flame
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.0.0-rc6"
+ flame_audio:
+ dependency: "direct main"
+ description:
+ path: "."
+ ref: "576c7cd"
+ resolved-ref: "576c7cd4e31460751f8c834e2298fced94fb4f7e"
+ url: "https://github.com/idootop/flame_audio.git"
+ source: git
+ version: "0.1.0-rc3"
+ flame_forge2d:
+ dependency: "direct main"
+ description:
+ name: flame_forge2d
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.6.2-rc3"
+ flutter:
+ dependency: "direct main"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_plugin_android_lifecycle:
+ dependency: transitive
+ description:
+ name: flutter_plugin_android_lifecycle
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.0.11"
+ flutter_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_web_plugins:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ forge2d:
+ dependency: transitive
+ description:
+ name: forge2d
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.6.2"
+ hive:
+ dependency: "direct main"
+ description:
+ name: hive
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.4.4+1"
+ http:
+ dependency: transitive
+ description:
+ name: http
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.12.2"
+ http_parser:
+ dependency: transitive
+ description:
+ name: http_parser
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "3.1.4"
+ image:
+ dependency: "direct main"
+ description:
+ path: "."
+ ref: crop
+ resolved-ref: d25d422edb328fb9234e8c562ddde715daa735f5
+ url: "https://github.com/idootop/image.git"
+ source: git
+ version: "2.1.19"
+ image_picker:
+ dependency: "direct main"
+ description:
+ name: image_picker
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.6.7+22"
+ image_picker_platform_interface:
+ dependency: transitive
+ description:
+ name: image_picker_platform_interface
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.1.6"
+ image_picker_web_redux:
+ dependency: "direct main"
+ description:
+ name: image_picker_web_redux
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.1.3"
+ intl:
+ dependency: transitive
+ description:
+ name: intl
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.16.1"
+ js:
+ dependency: transitive
+ description:
+ name: js
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.6.3"
+ matcher:
+ dependency: transitive
+ description:
+ name: matcher
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.12.10"
+ meta:
+ dependency: transitive
+ description:
+ name: meta
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.3.0"
+ mime:
+ dependency: transitive
+ description:
+ name: mime
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.9.7"
+ ordered_set:
+ dependency: transitive
+ description:
+ name: ordered_set
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.0.1"
+ path:
+ dependency: transitive
+ description:
+ name: path
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.8.0"
+ path_provider:
+ dependency: "direct main"
+ description:
+ name: path_provider
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.6.27"
+ path_provider_linux:
+ dependency: transitive
+ description:
+ name: path_provider_linux
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.0.1+2"
+ path_provider_macos:
+ dependency: transitive
+ description:
+ name: path_provider_macos
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.0.4+8"
+ path_provider_platform_interface:
+ dependency: transitive
+ description:
+ name: path_provider_platform_interface
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.0.4"
+ path_provider_windows:
+ dependency: transitive
+ description:
+ name: path_provider_windows
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.0.4+3"
+ pedantic:
+ dependency: "direct main"
+ description:
+ name: pedantic
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.10.0-nullsafety.3"
+ petitparser:
+ dependency: transitive
+ description:
+ name: petitparser
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "3.1.0"
+ platform:
+ dependency: transitive
+ description:
+ name: platform
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "3.0.0"
+ plugin_platform_interface:
+ dependency: transitive
+ description:
+ name: plugin_platform_interface
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.0.3"
+ process:
+ dependency: transitive
+ description:
+ name: process
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "3.0.13"
+ sensors:
+ dependency: "direct main"
+ description:
+ name: sensors
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.4.2+6"
+ share_plus:
+ dependency: "direct main"
+ description:
+ name: share_plus
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.2.0"
+ share_plus_linux:
+ dependency: transitive
+ description:
+ name: share_plus_linux
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.1.0"
+ share_plus_platform_interface:
+ dependency: transitive
+ description:
+ name: share_plus_platform_interface
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.2.0"
+ share_plus_web:
+ dependency: transitive
+ description:
+ name: share_plus_web
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.1.0"
+ share_plus_windows:
+ dependency: transitive
+ description:
+ name: share_plus_windows
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.1.0"
+ sky_engine:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.99"
+ source_span:
+ dependency: transitive
+ description:
+ name: source_span
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.8.0"
+ stack_trace:
+ dependency: transitive
+ description:
+ name: stack_trace
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.10.0"
+ stream_channel:
+ dependency: transitive
+ description:
+ name: stream_channel
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.1.0"
+ string_scanner:
+ dependency: transitive
+ description:
+ name: string_scanner
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.1.0"
+ synchronized:
+ dependency: transitive
+ description:
+ name: synchronized
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.2.0+2"
+ term_glyph:
+ dependency: transitive
+ description:
+ name: term_glyph
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.2.0"
+ test_api:
+ dependency: transitive
+ description:
+ name: test_api
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.2.19"
+ typed_data:
+ dependency: transitive
+ description:
+ name: typed_data
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.3.0"
+ url_launcher:
+ dependency: "direct main"
+ description:
+ name: url_launcher
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "5.7.10"
+ url_launcher_linux:
+ dependency: transitive
+ description:
+ name: url_launcher_linux
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.0.1+4"
+ url_launcher_macos:
+ dependency: transitive
+ description:
+ name: url_launcher_macos
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.0.1+9"
+ url_launcher_platform_interface:
+ dependency: transitive
+ description:
+ name: url_launcher_platform_interface
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.0.9"
+ url_launcher_web:
+ dependency: transitive
+ description:
+ name: url_launcher_web
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.1.5+3"
+ url_launcher_windows:
+ dependency: transitive
+ description:
+ name: url_launcher_windows
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.0.1+3"
+ uuid:
+ dependency: transitive
+ description:
+ name: uuid
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.2.2"
+ vector_math:
+ dependency: transitive
+ description:
+ name: vector_math
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.1.0"
+ win32:
+ dependency: transitive
+ description:
+ name: win32
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.7.4+1"
+ xdg_directories:
+ dependency: transitive
+ description:
+ name: xdg_directories
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.1.2"
+ xml:
+ dependency: transitive
+ description:
+ name: xml
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "4.5.1"
+sdks:
+ dart: ">=2.12.0-0.0 <3.0.0"
+ flutter: ">=1.22.0"
diff --git a/pubspec.yaml b/pubspec.yaml
new file mode 100644
index 0000000..70d565a
--- /dev/null
+++ b/pubspec.yaml
@@ -0,0 +1,55 @@
+name: watermelon
+description: Flutter版合成大西瓜
+
+publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+
+version: 1.0.0+1
+
+environment:
+ sdk: ">=2.7.0 <3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ pedantic: ^1.9.2
+ flame: ^1.0.0-rc6
+ flame_forge2d: ^0.6.2-rc3
+ path_provider: ^1.6.27
+ hive: ^1.4.4+1
+ sensors: ^0.4.2+6
+ url_launcher: ^5.7.10
+ image_picker: ^0.6.7+22
+ image_picker_web_redux: ^1.1.3
+ share_plus: ^1.2.0
+ flame_audio:
+ git:
+ url: https://github.com/idootop/flame_audio.git
+ ref: 576c7cd
+ crop:
+ git:
+ url: https://github.com/idootop/flutter_crop.git
+ ref: circle
+ image:
+ git:
+ url: https://github.com/idootop/image.git
+ ref: crop
+
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+
+flutter:
+ uses-material-design: true
+ assets:
+ - assets/audio/
+ - assets/images/
+ - assets/images/friuts/
+ - assets/images/qq/
+ - assets/images/985/
+ - assets/images/shandong/
+
+flutter_icons:
+ image_path: "logo.png"
+ android: true
+ ios: true
\ No newline at end of file
diff --git a/screenshots/img.jpg b/screenshots/img.jpg
new file mode 100644
index 0000000..684ce93
Binary files /dev/null and b/screenshots/img.jpg differ
diff --git a/screenshots/inner.jpg b/screenshots/inner.jpg
new file mode 100644
index 0000000..e698d51
Binary files /dev/null and b/screenshots/inner.jpg differ
diff --git a/screenshots/logo.png b/screenshots/logo.png
new file mode 100644
index 0000000..553630a
Binary files /dev/null and b/screenshots/logo.png differ
diff --git a/screenshots/play.jpg b/screenshots/play.jpg
new file mode 100644
index 0000000..c54c8b6
Binary files /dev/null and b/screenshots/play.jpg differ
diff --git a/screenshots/win.jpg b/screenshots/win.jpg
new file mode 100644
index 0000000..904813d
Binary files /dev/null and b/screenshots/win.jpg differ
diff --git a/watermelon.iml b/watermelon.iml
new file mode 100644
index 0000000..e5c8371
--- /dev/null
+++ b/watermelon.iml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web/favicon.png b/web/favicon.png
new file mode 100644
index 0000000..8aaa46a
Binary files /dev/null and b/web/favicon.png differ
diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png
new file mode 100644
index 0000000..b749bfe
Binary files /dev/null and b/web/icons/Icon-192.png differ
diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png
new file mode 100644
index 0000000..88cfd48
Binary files /dev/null and b/web/icons/Icon-512.png differ
diff --git a/web/index.html b/web/index.html
new file mode 100644
index 0000000..4e242ce
--- /dev/null
+++ b/web/index.html
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 合成大瓜
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web/manifest.json b/web/manifest.json
new file mode 100644
index 0000000..9fec97b
--- /dev/null
+++ b/web/manifest.json
@@ -0,0 +1,23 @@
+{
+ "name": "watermelon",
+ "short_name": "watermelon",
+ "start_url": ".",
+ "display": "standalone",
+ "background_color": "#0175C2",
+ "theme_color": "#0175C2",
+ "description": "A new Flutter project.",
+ "orientation": "portrait-primary",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "icons/Icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ]
+}