Browse Source

更新到dart3.0

liuyuqi-dellpc 2 years ago
parent
commit
0fcdcfe56f
78 changed files with 1274 additions and 960 deletions
  1. 15 44
      .gitignore
  2. 25 2
      .metadata
  3. 1 0
      analysis_options.yaml
  4. 6 0
      android/.gitignore
  5. 22 16
      android/app/build.gradle
  6. 3 3
      android/app/src/debug/AndroidManifest.xml
  7. 11 12
      android/app/src/main/AndroidManifest.xml
  8. BIN
      android/app/src/main/ic_launcher-web.png
  9. 0 6
      android/app/src/main/java/tech/soit/flutter_tetris/MainActivity.java
  10. 0 6
      android/app/src/main/kotlin/tech/soit/flutter_tetris/MainActivity.kt
  11. 12 0
      android/app/src/main/res/drawable-v21/launch_background.xml
  12. 0 5
      android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  13. 0 5
      android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  14. BIN
      android/app/src/main/res/mipmap-hdpi/ic_launcher.png
  15. BIN
      android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
  16. BIN
      android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
  17. BIN
      android/app/src/main/res/mipmap-mdpi/ic_launcher.png
  18. BIN
      android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
  19. BIN
      android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
  20. BIN
      android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
  21. BIN
      android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
  22. BIN
      android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
  23. BIN
      android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  24. BIN
      android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
  25. BIN
      android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
  26. BIN
      android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  27. BIN
      android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
  28. BIN
      android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
  29. 18 0
      android/app/src/main/res/values-night/styles.xml
  30. 0 4
      android/app/src/main/res/values/ic_launcher_background.xml
  31. 12 2
      android/app/src/main/res/values/styles.xml
  32. 3 3
      android/app/src/profile/AndroidManifest.xml
  33. 4 4
      android/build.gradle
  34. 0 1
      android/gradle.properties
  35. 1 2
      android/gradle/wrapper/gradle-wrapper.properties
  36. 16 11
      android/settings.gradle
  37. 2 0
      ios/.gitignore
  38. 2 2
      ios/Flutter/AppFrameworkInfo.plist
  39. 0 1
      ios/Flutter/Debug.xcconfig
  40. 0 1
      ios/Flutter/Release.xcconfig
  41. 0 90
      ios/Podfile
  42. 146 116
      ios/Runner.xcodeproj/project.pbxproj
  43. 1 1
      ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  44. 8 0
      ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  45. 8 0
      ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  46. 14 7
      ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
  47. 0 3
      ios/Runner.xcworkspace/contents.xcworkspacedata
  48. 8 0
      ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  49. 8 0
      ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  50. 6 2
      ios/Runner/Info.plist
  51. 12 0
      ios/RunnerTests/RunnerTests.swift
  52. 13 13
      lib/gamer/block.dart
  53. 77 74
      lib/gamer/gamer.dart
  54. 11 16
      lib/gamer/keyboard.dart
  55. 0 130
      lib/generated/i18n.dart
  56. 67 0
      lib/generated/intl/messages_all.dart
  57. 34 0
      lib/generated/intl/messages_en.dart
  58. 34 0
      lib/generated/intl/messages_zh_CN.dart
  59. 159 0
      lib/generated/l10n.dart
  60. 0 118
      lib/income/donation_dialog.dart
  61. 0 0
      lib/l10n/intl_en.arb
  62. 0 0
      lib/l10n/intl_zh_CN.arb
  63. 37 9
      lib/main.dart
  64. 8 8
      lib/material/audios.dart
  65. 15 18
      lib/material/briks.dart
  66. 34 33
      lib/material/images.dart
  67. 8 7
      lib/material/material.dart
  68. 0 11
      lib/model/config.dart
  69. 0 13
      lib/pages/home_page.dart
  70. 56 47
      lib/panel/controller.dart
  71. 6 16
      lib/panel/page_land.dart
  72. 18 30
      lib/panel/page_portrait.dart
  73. 11 7
      lib/panel/player_panel.dart
  74. 21 14
      lib/panel/screen.dart
  75. 21 22
      lib/panel/status_panel.dart
  76. 0 18
      lib/utils/app_util.dart
  77. 271 0
      pubspec.lock
  78. 9 7
      pubspec.yaml

+ 15 - 44
.gitignore

@@ -1,6 +1,5 @@
 # Miscellaneous
 # Miscellaneous
 *.class
 *.class
-*.lock
 *.log
 *.log
 *.pyc
 *.pyc
 *.swp
 *.swp
@@ -9,6 +8,7 @@
 .buildlog/
 .buildlog/
 .history
 .history
 .svn/
 .svn/
+migrate_working_dir/
 
 
 # IntelliJ related
 # IntelliJ related
 *.iml
 *.iml
@@ -16,58 +16,29 @@
 *.iws
 *.iws
 .idea/
 .idea/
 
 
-# Visual Studio Code related
-.vscode/
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
 
 
 # Flutter/Dart/Pub related
 # Flutter/Dart/Pub related
 **/doc/api/
 **/doc/api/
+**/ios/Flutter/.last_build_id
 .dart_tool/
 .dart_tool/
 .flutter-plugins
 .flutter-plugins
 .flutter-plugins-dependencies
 .flutter-plugins-dependencies
 .packages
 .packages
 .pub-cache/
 .pub-cache/
 .pub/
 .pub/
-build/
-lib/generated_plugin_registrant.dart
+/build/
 
 
-# Android related
-**/android/**/gradle-wrapper.jar
-**/android/.gradle
-**/android/captures/
-**/android/gradlew
-**/android/gradlew.bat
-**/android/local.properties
-**/android/**/GeneratedPluginRegistrant.java
+# Symbolication related
+app.*.symbols
 
 
-# iOS/XCode related
-**/ios/**/*.mode1v3
-**/ios/**/*.mode2v3
-**/ios/**/*.moved-aside
-**/ios/**/*.pbxuser
-**/ios/**/*.perspectivev3
-**/ios/**/*sync/
-**/ios/**/.sconsign.dblite
-**/ios/**/.tags*
-**/ios/**/.vagrant/
-**/ios/**/DerivedData/
-**/ios/**/Icon?
-**/ios/**/Pods/
-**/ios/**/.symlinks/
-**/ios/**/profile
-**/ios/**/xcuserdata
-**/ios/.generated/
-**/ios/Flutter/App.framework
-**/ios/Flutter/Flutter.framework
-**/ios/Flutter/Generated.xcconfig
-**/ios/Flutter/app.flx
-**/ios/Flutter/app.zip
-**/ios/Flutter/flutter_assets/
-**/ios/ServiceDefinitions.json
-**/ios/Runner/GeneratedPluginRegistrant.*
+# Obfuscation related
+app.*.map.json
 
 
-# Exceptions to above rules.
-!**/ios/**/default.mode1v3
-!**/ios/**/default.mode2v3
-!**/ios/**/default.pbxuser
-!**/ios/**/default.perspectivev3
-!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release

+ 25 - 2
.metadata

@@ -4,7 +4,30 @@
 # This file should be version controlled and should not be manually edited.
 # This file should be version controlled and should not be manually edited.
 
 
 version:
 version:
-  revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b
-  channel: beta
+  revision: "2f708eb8396e362e280fac22cf171c2cb467343c"
+  channel: "stable"
 
 
 project_type: app
 project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+  platforms:
+    - platform: root
+      create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c
+      base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c
+    - platform: android
+      create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c
+      base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c
+    - platform: ios
+      create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c
+      base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c
+
+  # User provided section
+
+  # List of Local paths (relative to this file) that should be
+  # ignored by the migrate tool.
+  #
+  # Files that are not part of the templates will be ignored by default.
+  unmanaged_files:
+    - 'lib/main.dart'
+    - 'ios/Runner.xcodeproj/project.pbxproj'

+ 1 - 0
analysis_options.yaml

@@ -0,0 +1 @@
+include: package:flutter_lints/flutter.yaml

+ 6 - 0
android/.gitignore

@@ -5,3 +5,9 @@ gradle-wrapper.jar
 /gradlew.bat
 /gradlew.bat
 /local.properties
 /local.properties
 GeneratedPluginRegistrant.java
 GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+key.properties
+**/*.keystore
+**/*.jks

+ 22 - 16
android/app/build.gradle

@@ -1,3 +1,9 @@
+plugins {
+    id "com.android.application"
+    id "kotlin-android"
+    id "dev.flutter.flutter-gradle-plugin"
+}
+
 def localProperties = new Properties()
 def localProperties = new Properties()
 def localPropertiesFile = rootProject.file('local.properties')
 def localPropertiesFile = rootProject.file('local.properties')
 if (localPropertiesFile.exists()) {
 if (localPropertiesFile.exists()) {
@@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) {
     }
     }
 }
 }
 
 
-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')
 def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
 if (flutterVersionCode == null) {
 if (flutterVersionCode == null) {
     flutterVersionCode = '1'
     flutterVersionCode = '1'
@@ -21,29 +22,33 @@ if (flutterVersionName == null) {
     flutterVersionName = '1.0'
     flutterVersionName = '1.0'
 }
 }
 
 
-apply plugin: 'com.android.application'
-//apply plugin: 'kotlin-android'
-apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
-
 android {
 android {
+    namespace "tech.soit.flutter_tetris"
     compileSdkVersion 33
     compileSdkVersion 33
+    ndkVersion flutter.ndkVersion
 
 
-    sourceSets {
-        main.java.srcDirs += 'src/main/kotlin'
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    kotlinOptions {
+        jvmTarget = '1.8'
     }
     }
 
 
-    lintOptions {
-        disable 'InvalidPackage'
+    sourceSets {
+        main.java.srcDirs += 'src/main/kotlin'
     }
     }
 
 
     defaultConfig {
     defaultConfig {
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
         applicationId "tech.soit.flutter_tetris"
         applicationId "tech.soit.flutter_tetris"
-        minSdkVersion 21
-        targetSdkVersion 33
+        // You can update the following values to match your application needs.
+        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
+        minSdkVersion flutter.minSdkVersion
+        targetSdkVersion flutter.targetSdkVersion
         versionCode flutterVersionCode.toInteger()
         versionCode flutterVersionCode.toInteger()
         versionName flutterVersionName
         versionName flutterVersionName
-        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     }
     }
 
 
     buildTypes {
     buildTypes {
@@ -59,3 +64,4 @@ flutter {
     source '../..'
     source '../..'
 }
 }
 
 
+dependencies {}

+ 3 - 3
android/app/src/debug/AndroidManifest.xml

@@ -1,6 +1,6 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="tech.soit.flutter_tetris">
-    <!-- Flutter needs it to communicate with the running application
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- The INTERNET permission is required for development. Specifically,
+         the Flutter tool needs it to communicate with the running application
          to allow setting breakpoints, to provide hot reload, etc.
          to allow setting breakpoints, to provide hot reload, etc.
     -->
     -->
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.INTERNET"/>

+ 11 - 12
android/app/src/main/AndroidManifest.xml

@@ -1,25 +1,24 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="tech.soit.flutter_tetris">
-
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
-    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
-    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
-
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
     <application
     <application
-        
         android:label="flutter_tetris"
         android:label="flutter_tetris"
+        android:name="${applicationName}"
         android:icon="@mipmap/ic_launcher">
         android:icon="@mipmap/ic_launcher">
         <activity
         <activity
             android:name=".MainActivity"
             android:name=".MainActivity"
-            android:launchMode="singleTop"
             android:exported="true"
             android:exported="true"
+            android:launchMode="singleTop"
             android:theme="@style/LaunchTheme"
             android:theme="@style/LaunchTheme"
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
             android:hardwareAccelerated="true"
             android:hardwareAccelerated="true"
             android:windowSoftInputMode="adjustResize">
             android:windowSoftInputMode="adjustResize">
+            <!-- Specifies an Android theme to apply to this Activity as soon as
+                 the Android process has started. This theme is visible to the user
+                 while the Flutter UI initializes. After that, this theme continues
+                 to determine the Window background behind the Flutter UI. -->
+            <meta-data
+              android:name="io.flutter.embedding.android.NormalTheme"
+              android:resource="@style/NormalTheme"
+              />
             <intent-filter>
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
                 <category android:name="android.intent.category.LAUNCHER"/>

BIN
android/app/src/main/ic_launcher-web.png


+ 0 - 6
android/app/src/main/java/tech/soit/flutter_tetris/MainActivity.java

@@ -1,6 +0,0 @@
-package tech.soit.flutter_tetris;
-
-import io.flutter.embedding.android.FlutterActivity;
-
-public class MainActivity extends FlutterActivity {
-}

+ 0 - 6
android/app/src/main/kotlin/tech/soit/flutter_tetris/MainActivity.kt

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

+ 12 - 0
android/app/src/main/res/drawable-v21/launch_background.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="?android:colorBackground" />
+
+    <!-- You can insert your own image assets here -->
+    <!-- <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@mipmap/launch_image" />
+    </item> -->
+</layer-list>

+ 0 - 5
android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml

@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
-    <background android:drawable="@color/ic_launcher_background"/>
-    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
-</adaptive-icon>

+ 0 - 5
android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml

@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
-    <background android:drawable="@color/ic_launcher_background"/>
-    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
-</adaptive-icon>

BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png


BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png


BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png


BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png


BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png


BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png


BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png


BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png


BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png


BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png


+ 18 - 0
android/app/src/main/res/values-night/styles.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
+    <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
+        <!-- Show a splash screen on the activity. Automatically removed when
+             the Flutter engine draws its first frame -->
+        <item name="android:windowBackground">@drawable/launch_background</item>
+    </style>
+    <!-- Theme applied to the Android Window as soon as the process has started.
+         This theme determines the color of the Android Window while your
+         Flutter UI initializes, as well as behind your Flutter UI while its
+         running.
+
+         This Theme is only used starting with V2 of Flutter's Android embedding. -->
+    <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
+        <item name="android:windowBackground">?android:colorBackground</item>
+    </style>
+</resources>

+ 0 - 4
android/app/src/main/res/values/ic_launcher_background.xml

@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <color name="ic_launcher_background">#26A69A</color>
-</resources>

+ 12 - 2
android/app/src/main/res/values/styles.xml

@@ -1,8 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
 <resources>
-    <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
+    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
+    <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
         <!-- Show a splash screen on the activity. Automatically removed when
         <!-- Show a splash screen on the activity. Automatically removed when
-             Flutter draws its first frame -->
+             the Flutter engine draws its first frame -->
         <item name="android:windowBackground">@drawable/launch_background</item>
         <item name="android:windowBackground">@drawable/launch_background</item>
     </style>
     </style>
+    <!-- Theme applied to the Android Window as soon as the process has started.
+         This theme determines the color of the Android Window while your
+         Flutter UI initializes, as well as behind your Flutter UI while its
+         running.
+
+         This Theme is only used starting with V2 of Flutter's Android embedding. -->
+    <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
+        <item name="android:windowBackground">?android:colorBackground</item>
+    </style>
 </resources>
 </resources>

+ 3 - 3
android/app/src/profile/AndroidManifest.xml

@@ -1,6 +1,6 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="tech.soit.flutter_tetris">
-    <!-- Flutter needs it to communicate with the running application
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- The INTERNET permission is required for development. Specifically,
+         the Flutter tool needs it to communicate with the running application
          to allow setting breakpoints, to provide hot reload, etc.
          to allow setting breakpoints, to provide hot reload, etc.
     -->
     -->
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.INTERNET"/>

+ 4 - 4
android/build.gradle

@@ -1,13 +1,13 @@
 buildscript {
 buildscript {
-//    ext.kotlin_version = '1.7.10'
+    ext.kotlin_version = '1.7.10'
     repositories {
     repositories {
         google()
         google()
         mavenCentral()
         mavenCentral()
     }
     }
 
 
     dependencies {
     dependencies {
-        classpath 'com.android.tools.build:gradle:7.2.2'
-//        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+        classpath 'com.android.tools.build:gradle:7.2.3'
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
     }
     }
 }
 }
 
 
@@ -26,6 +26,6 @@ subprojects {
     project.evaluationDependsOn(':app')
     project.evaluationDependsOn(':app')
 }
 }
 
 
-task clean(type: Delete) {
+tasks.register("clean", Delete) {
     delete rootProject.buildDir
     delete rootProject.buildDir
 }
 }

+ 0 - 1
android/gradle.properties

@@ -1,4 +1,3 @@
 org.gradle.jvmargs=-Xmx1536M
 org.gradle.jvmargs=-Xmx1536M
-android.enableR8=true
 android.useAndroidX=true
 android.useAndroidX=true
 android.enableJetifier=true
 android.enableJetifier=true

+ 1 - 2
android/gradle/wrapper/gradle-wrapper.properties

@@ -1,6 +1,5 @@
-#Fri Jun 23 08:50:38 CEST 2017
 distributionBase=GRADLE_USER_HOME
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip

+ 16 - 11
android/settings.gradle

@@ -1,15 +1,20 @@
-include ':app'
+pluginManagement {
+    def flutterSdkPath = {
+        def properties = new Properties()
+        file("local.properties").withInputStream { properties.load(it) }
+        def flutterSdkPath = properties.getProperty("flutter.sdk")
+        assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+        return flutterSdkPath
+    }
+    settings.ext.flutterSdkPath = flutterSdkPath()
 
 
-def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
+    includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
 
 
-def plugins = new Properties()
-def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
-if (pluginsFile.exists()) {
-    pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
+    plugins {
+        id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
+    }
 }
 }
 
 
-plugins.each { name, path ->
-    def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
-    include ":$name"
-    project(":$name").projectDir = pluginDirectory
-}
+include ":app"
+
+apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"

+ 2 - 0
ios/.gitignore

@@ -1,3 +1,4 @@
+**/dgph
 *.mode1v3
 *.mode1v3
 *.mode2v3
 *.mode2v3
 *.moved-aside
 *.moved-aside
@@ -18,6 +19,7 @@ Flutter/App.framework
 Flutter/Flutter.framework
 Flutter/Flutter.framework
 Flutter/Flutter.podspec
 Flutter/Flutter.podspec
 Flutter/Generated.xcconfig
 Flutter/Generated.xcconfig
+Flutter/ephemeral/
 Flutter/app.flx
 Flutter/app.flx
 Flutter/app.zip
 Flutter/app.zip
 Flutter/flutter_assets/
 Flutter/flutter_assets/

+ 2 - 2
ios/Flutter/AppFrameworkInfo.plist

@@ -3,7 +3,7 @@
 <plist version="1.0">
 <plist version="1.0">
 <dict>
 <dict>
   <key>CFBundleDevelopmentRegion</key>
   <key>CFBundleDevelopmentRegion</key>
-  <string>$(DEVELOPMENT_LANGUAGE)</string>
+  <string>en</string>
   <key>CFBundleExecutable</key>
   <key>CFBundleExecutable</key>
   <string>App</string>
   <string>App</string>
   <key>CFBundleIdentifier</key>
   <key>CFBundleIdentifier</key>
@@ -21,6 +21,6 @@
   <key>CFBundleVersion</key>
   <key>CFBundleVersion</key>
   <string>1.0</string>
   <string>1.0</string>
   <key>MinimumOSVersion</key>
   <key>MinimumOSVersion</key>
-  <string>8.0</string>
+  <string>11.0</string>
 </dict>
 </dict>
 </plist>
 </plist>

+ 0 - 1
ios/Flutter/Debug.xcconfig

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

+ 0 - 1
ios/Flutter/Release.xcconfig

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

+ 0 - 90
ios/Podfile

@@ -1,90 +0,0 @@
-# Uncomment this line to define a global platform for your project
-# platform :ios, '9.0'
-
-# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
-ENV['COCOAPODS_DISABLE_STATS'] = 'true'
-
-project 'Runner', {
-  'Debug' => :debug,
-  'Profile' => :release,
-  'Release' => :release,
-}
-
-def parse_KV_file(file, separator='=')
-  file_abs_path = File.expand_path(file)
-  if !File.exists? file_abs_path
-    return [];
-  end
-  generated_key_values = {}
-  skip_line_start_symbols = ["#", "/"]
-  File.foreach(file_abs_path) do |line|
-    next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
-    plugin = line.split(pattern=separator)
-    if plugin.length == 2
-      podname = plugin[0].strip()
-      path = plugin[1].strip()
-      podpath = File.expand_path("#{path}", file_abs_path)
-      generated_key_values[podname] = podpath
-    else
-      puts "Invalid plugin specification: #{line}"
-    end
-  end
-  generated_key_values
-end
-
-target 'Runner' do
-  use_frameworks!
-  use_modular_headers!
-
-  # Flutter Pod
-
-  copied_flutter_dir = File.join(__dir__, 'Flutter')
-  copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
-  copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
-  unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
-    # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
-    # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
-    # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
-
-    generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
-    unless File.exist?(generated_xcode_build_settings_path)
-      raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
-    end
-    generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
-    cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];
-
-    unless File.exist?(copied_framework_path)
-      FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
-    end
-    unless File.exist?(copied_podspec_path)
-      FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
-    end
-  end
-
-  # Keep pod path relative so it can be checked into Podfile.lock.
-  pod 'Flutter', :path => 'Flutter'
-
-  # Plugin Pods
-
-  # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
-  # referring to absolute paths on developers' machines.
-  system('rm -rf .symlinks')
-  system('mkdir -p .symlinks/plugins')
-  plugin_pods = parse_KV_file('../.flutter-plugins')
-  plugin_pods.each do |name, path|
-    symlink = File.join('.symlinks', 'plugins', name)
-    File.symlink(path, symlink)
-    pod name, :path => File.join(symlink, 'ios')
-  end
-end
-
-# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
-install! 'cocoapods', :disable_input_output_paths => true
-
-post_install do |installer|
-  installer.pods_project.targets.each do |target|
-    target.build_configurations.each do |config|
-      config.build_settings['ENABLE_BITCODE'] = 'NO'
-    end
-  end
-end

+ 146 - 116
ios/Runner.xcodeproj/project.pbxproj

@@ -3,23 +3,29 @@
 	archiveVersion = 1;
 	archiveVersion = 1;
 	classes = {
 	classes = {
 	};
 	};
-	objectVersion = 46;
+	objectVersion = 54;
 	objects = {
 	objects = {
 
 
 /* Begin PBXBuildFile section */
 /* Begin PBXBuildFile section */
 		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
 		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
 		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
 		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
-		3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
-		3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
-		5C0CE568E15E98E07D116769 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A61363D6699EDC5B3E8CF46 /* Pods_Runner.framework */; };
 		74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
 		74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
-		9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
-		9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
 		97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
 		97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
 		97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
 		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
 		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+		331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
 /* End PBXBuildFile section */
 /* End PBXBuildFile section */
 
 
+/* Begin PBXContainerItemProxy section */
+		331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 97C146ED1CF9000F007C117D;
+			remoteInfo = Runner;
+		};
+/* End PBXContainerItemProxy section */
+
 /* Begin PBXCopyFilesBuildPhase section */
 /* Begin PBXCopyFilesBuildPhase section */
 		9705A1C41CF9048500538489 /* Embed Frameworks */ = {
 		9705A1C41CF9048500538489 /* Embed Frameworks */ = {
 			isa = PBXCopyFilesBuildPhase;
 			isa = PBXCopyFilesBuildPhase;
@@ -27,8 +33,6 @@
 			dstPath = "";
 			dstPath = "";
 			dstSubfolderSpec = 10;
 			dstSubfolderSpec = 10;
 			files = (
 			files = (
-				3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
-				9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
 			);
 			);
 			name = "Embed Frameworks";
 			name = "Embed Frameworks";
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
@@ -38,23 +42,19 @@
 /* Begin PBXFileReference section */
 /* Begin PBXFileReference section */
 		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
 		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
 		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
 		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
-		354D2140C348863997D61F09 /* 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 = "<group>"; };
 		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
 		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
-		3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
-		521300D6E9C3A09991429CD2 /* 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 = "<group>"; };
-		742DD36AAD6926A795A797C2 /* 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 = "<group>"; };
 		74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
 		74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
 		74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 		74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
 		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
-		8A61363D6699EDC5B3E8CF46 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
 		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
 		9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
 		9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
-		9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
 		97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
 		97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
 		97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 		97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 		97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
 		97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
 		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
+		331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 /* End PBXFileReference section */
 /* End PBXFileReference section */
 
 
 /* Begin PBXFrameworksBuildPhase section */
 /* Begin PBXFrameworksBuildPhase section */
@@ -62,32 +62,16 @@
 			isa = PBXFrameworksBuildPhase;
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
 			files = (
 			files = (
-				9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
-				3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
-				5C0CE568E15E98E07D116769 /* Pods_Runner.framework in Frameworks */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};
 /* End PBXFrameworksBuildPhase section */
 /* End PBXFrameworksBuildPhase section */
 
 
 /* Begin PBXGroup section */
 /* Begin PBXGroup section */
-		10C7F7AA9FF17201C63670BF /* Pods */ = {
-			isa = PBXGroup;
-			children = (
-				742DD36AAD6926A795A797C2 /* Pods-Runner.debug.xcconfig */,
-				354D2140C348863997D61F09 /* Pods-Runner.release.xcconfig */,
-				521300D6E9C3A09991429CD2 /* Pods-Runner.profile.xcconfig */,
-			);
-			name = Pods;
-			path = Pods;
-			sourceTree = "<group>";
-		};
 		9740EEB11CF90186004384FC /* Flutter */ = {
 		9740EEB11CF90186004384FC /* Flutter */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
-				3B80C3931E831B6300D905FE /* App.framework */,
 				3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
 				3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
-				9740EEBA1CF902C7004384FC /* Flutter.framework */,
 				9740EEB21CF90195004384FC /* Debug.xcconfig */,
 				9740EEB21CF90195004384FC /* Debug.xcconfig */,
 				7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
 				7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
 				9740EEB31CF90195004384FC /* Generated.xcconfig */,
 				9740EEB31CF90195004384FC /* Generated.xcconfig */,
@@ -95,14 +79,21 @@
 			name = Flutter;
 			name = Flutter;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
+		331C8082294A63A400263BE5 /* RunnerTests */ = {
+			isa = PBXGroup;
+			children = (
+				331C807B294A618700263BE5 /* RunnerTests.swift */,
+			);
+			path = RunnerTests;
+			sourceTree = "<group>";
+		};
 		97C146E51CF9000F007C117D = {
 		97C146E51CF9000F007C117D = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
 				9740EEB11CF90186004384FC /* Flutter */,
 				9740EEB11CF90186004384FC /* Flutter */,
 				97C146F01CF9000F007C117D /* Runner */,
 				97C146F01CF9000F007C117D /* Runner */,
 				97C146EF1CF9000F007C117D /* Products */,
 				97C146EF1CF9000F007C117D /* Products */,
-				10C7F7AA9FF17201C63670BF /* Pods */,
-				DB3CC3345158B8C6AA46BEF5 /* Frameworks */,
+				331C8082294A63A400263BE5 /* RunnerTests */,
 			);
 			);
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
@@ -110,6 +101,7 @@
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
 				97C146EE1CF9000F007C117D /* Runner.app */,
 				97C146EE1CF9000F007C117D /* Runner.app */,
+				331C8081294A63A400263BE5 /* RunnerTests.xctest */,
 			);
 			);
 			name = Products;
 			name = Products;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -121,7 +113,6 @@
 				97C146FD1CF9000F007C117D /* Assets.xcassets */,
 				97C146FD1CF9000F007C117D /* Assets.xcassets */,
 				97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
 				97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
 				97C147021CF9000F007C117D /* Info.plist */,
 				97C147021CF9000F007C117D /* Info.plist */,
-				97C146F11CF9000F007C117D /* Supporting Files */,
 				1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
 				1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
 				1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
 				1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
 				74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
 				74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
@@ -130,36 +121,37 @@
 			path = Runner;
 			path = Runner;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
-		97C146F11CF9000F007C117D /* Supporting Files */ = {
-			isa = PBXGroup;
-			children = (
-			);
-			name = "Supporting Files";
-			sourceTree = "<group>";
-		};
-		DB3CC3345158B8C6AA46BEF5 /* Frameworks */ = {
-			isa = PBXGroup;
-			children = (
-				8A61363D6699EDC5B3E8CF46 /* Pods_Runner.framework */,
-			);
-			name = Frameworks;
-			sourceTree = "<group>";
-		};
 /* End PBXGroup section */
 /* End PBXGroup section */
 
 
 /* Begin PBXNativeTarget section */
 /* Begin PBXNativeTarget section */
+		331C8080294A63A400263BE5 /* RunnerTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+			buildPhases = (
+				331C807D294A63A400263BE5 /* Sources */,
+				331C807E294A63A400263BE5 /* Frameworks */,
+				331C807F294A63A400263BE5 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				331C8086294A63A400263BE5 /* PBXTargetDependency */,
+			);
+			name = RunnerTests;
+			productName = RunnerTests;
+			productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
 		97C146ED1CF9000F007C117D /* Runner */ = {
 		97C146ED1CF9000F007C117D /* Runner */ = {
 			isa = PBXNativeTarget;
 			isa = PBXNativeTarget;
 			buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
 			buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
 			buildPhases = (
 			buildPhases = (
-				B804686302B84009417001E6 /* [CP] Check Pods Manifest.lock */,
 				9740EEB61CF901F6004384FC /* Run Script */,
 				9740EEB61CF901F6004384FC /* Run Script */,
 				97C146EA1CF9000F007C117D /* Sources */,
 				97C146EA1CF9000F007C117D /* Sources */,
 				97C146EB1CF9000F007C117D /* Frameworks */,
 				97C146EB1CF9000F007C117D /* Frameworks */,
 				97C146EC1CF9000F007C117D /* Resources */,
 				97C146EC1CF9000F007C117D /* Resources */,
 				9705A1C41CF9048500538489 /* Embed Frameworks */,
 				9705A1C41CF9048500538489 /* Embed Frameworks */,
 				3B06AD1E1E4923F5004D2608 /* Thin Binary */,
 				3B06AD1E1E4923F5004D2608 /* Thin Binary */,
-				8D305A54E8A98A1D867EA98C /* [CP] Embed Pods Frameworks */,
 			);
 			);
 			buildRules = (
 			buildRules = (
 			);
 			);
@@ -176,9 +168,14 @@
 		97C146E61CF9000F007C117D /* Project object */ = {
 		97C146E61CF9000F007C117D /* Project object */ = {
 			isa = PBXProject;
 			isa = PBXProject;
 			attributes = {
 			attributes = {
-				LastUpgradeCheck = 1020;
+				BuildIndependentTargetsInParallel = YES;
+				LastUpgradeCheck = 1430;
 				ORGANIZATIONNAME = "";
 				ORGANIZATIONNAME = "";
 				TargetAttributes = {
 				TargetAttributes = {
+					331C8080294A63A400263BE5 = {
+						CreatedOnToolsVersion = 14.0;
+						TestTargetID = 97C146ED1CF9000F007C117D;
+					};
 					97C146ED1CF9000F007C117D = {
 					97C146ED1CF9000F007C117D = {
 						CreatedOnToolsVersion = 7.3.1;
 						CreatedOnToolsVersion = 7.3.1;
 						LastSwiftMigration = 1100;
 						LastSwiftMigration = 1100;
@@ -186,7 +183,7 @@
 				};
 				};
 			};
 			};
 			buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
 			buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
-			compatibilityVersion = "Xcode 3.2";
+			compatibilityVersion = "Xcode 9.3";
 			developmentRegion = en;
 			developmentRegion = en;
 			hasScannedForEncodings = 0;
 			hasScannedForEncodings = 0;
 			knownRegions = (
 			knownRegions = (
@@ -199,11 +196,19 @@
 			projectRoot = "";
 			projectRoot = "";
 			targets = (
 			targets = (
 				97C146ED1CF9000F007C117D /* Runner */,
 				97C146ED1CF9000F007C117D /* Runner */,
+				331C8080294A63A400263BE5 /* RunnerTests */,
 			);
 			);
 		};
 		};
 /* End PBXProject section */
 /* End PBXProject section */
 
 
 /* Begin PBXResourcesBuildPhase section */
 /* Begin PBXResourcesBuildPhase section */
+		331C807F294A63A400263BE5 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		97C146EC1CF9000F007C117D /* Resources */ = {
 		97C146EC1CF9000F007C117D /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
@@ -220,35 +225,23 @@
 /* Begin PBXShellScriptBuildPhase section */
 /* Begin PBXShellScriptBuildPhase section */
 		3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
 		3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
 			isa = PBXShellScriptBuildPhase;
 			isa = PBXShellScriptBuildPhase;
+			alwaysOutOfDate = 1;
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
 			files = (
 			files = (
 			);
 			);
 			inputPaths = (
 			inputPaths = (
+				"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
 			);
 			);
 			name = "Thin Binary";
 			name = "Thin Binary";
 			outputPaths = (
 			outputPaths = (
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
 			shellPath = /bin/sh;
-			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
-		};
-		8D305A54E8A98A1D867EA98C /* [CP] Embed Pods Frameworks */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputPaths = (
-			);
-			name = "[CP] Embed Pods Frameworks";
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
-			showEnvVarsInLog = 0;
+			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
 		};
 		};
 		9740EEB61CF901F6004384FC /* Run Script */ = {
 		9740EEB61CF901F6004384FC /* Run Script */ = {
 			isa = PBXShellScriptBuildPhase;
 			isa = PBXShellScriptBuildPhase;
+			alwaysOutOfDate = 1;
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
 			files = (
 			files = (
 			);
 			);
@@ -261,31 +254,17 @@
 			shellPath = /bin/sh;
 			shellPath = /bin/sh;
 			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
 			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
 		};
 		};
-		B804686302B84009417001E6 /* [CP] Check Pods Manifest.lock */ = {
-			isa = PBXShellScriptBuildPhase;
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		331C807D294A63A400263BE5 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
 			files = (
 			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",
+				331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			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 */
 		97C146EA1CF9000F007C117D /* Sources */ = {
 		97C146EA1CF9000F007C117D /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
@@ -297,6 +276,14 @@
 		};
 		};
 /* End PBXSourcesBuildPhase section */
 /* End PBXSourcesBuildPhase section */
 
 
+/* Begin PBXTargetDependency section */
+		331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 97C146ED1CF9000F007C117D /* Runner */;
+			targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
 /* Begin PBXVariantGroup section */
 /* Begin PBXVariantGroup section */
 		97C146FA1CF9000F007C117D /* Main.storyboard */ = {
 		97C146FA1CF9000F007C117D /* Main.storyboard */ = {
 			isa = PBXVariantGroup;
 			isa = PBXVariantGroup;
@@ -319,7 +306,6 @@
 /* Begin XCBuildConfiguration section */
 /* Begin XCBuildConfiguration section */
 		249021D3217E4FDB00AE95B9 /* Profile */ = {
 		249021D3217E4FDB00AE95B9 /* Profile */ = {
 			isa = XCBuildConfiguration;
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
 			buildSettings = {
 			buildSettings = {
 				ALWAYS_SEARCH_USER_PATHS = NO;
 				ALWAYS_SEARCH_USER_PATHS = NO;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NONNULL = YES;
@@ -359,7 +345,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = iphoneos;
 				SDKROOT = iphoneos;
 				SUPPORTED_PLATFORMS = iphoneos;
 				SUPPORTED_PLATFORMS = iphoneos;
@@ -376,15 +362,10 @@
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_MODULES = YES;
 				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
 				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
 				ENABLE_BITCODE = NO;
 				ENABLE_BITCODE = NO;
-				FRAMEWORK_SEARCH_PATHS = (
-					"$(inherited)",
-					"$(PROJECT_DIR)/Flutter",
-				);
 				INFOPLIST_FILE = Runner/Info.plist;
 				INFOPLIST_FILE = Runner/Info.plist;
-				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
-				LIBRARY_SEARCH_PATHS = (
+				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(inherited)",
-					"$(PROJECT_DIR)/Flutter",
+					"@executable_path/Frameworks",
 				);
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = tech.soit.flutterTetris;
 				PRODUCT_BUNDLE_IDENTIFIER = tech.soit.flutterTetris;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -394,9 +375,58 @@
 			};
 			};
 			name = Profile;
 			name = Profile;
 		};
 		};
+		331C8088294A63A400263BE5 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* Pods-RunnerTests.debug.xcconfig */;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CODE_SIGN_STYLE = Automatic;
+				CURRENT_PROJECT_VERSION = 1;
+				GENERATE_INFOPLIST_FILE = YES;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = tech.soit.flutterTetris.RunnerTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 5.0;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+			};
+			name = Debug;
+		};
+		331C8089294A63A400263BE5 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 89B67EB44CE7B6631473024E /* Pods-RunnerTests.release.xcconfig */;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CODE_SIGN_STYLE = Automatic;
+				CURRENT_PROJECT_VERSION = 1;
+				GENERATE_INFOPLIST_FILE = YES;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = tech.soit.flutterTetris.RunnerTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+			};
+			name = Release;
+		};
+		331C808A294A63A400263BE5 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 640959BDD8F10B91D80A66BE /* Pods-RunnerTests.profile.xcconfig */;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CODE_SIGN_STYLE = Automatic;
+				CURRENT_PROJECT_VERSION = 1;
+				GENERATE_INFOPLIST_FILE = YES;
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = tech.soit.flutterTetris.RunnerTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+			};
+			name = Profile;
+		};
 		97C147031CF9000F007C117D /* Debug */ = {
 		97C147031CF9000F007C117D /* Debug */ = {
 			isa = XCBuildConfiguration;
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
 			buildSettings = {
 			buildSettings = {
 				ALWAYS_SEARCH_USER_PATHS = NO;
 				ALWAYS_SEARCH_USER_PATHS = NO;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NONNULL = YES;
@@ -442,7 +472,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = iphoneos;
 				SDKROOT = iphoneos;
@@ -452,7 +482,6 @@
 		};
 		};
 		97C147041CF9000F007C117D /* Release */ = {
 		97C147041CF9000F007C117D /* Release */ = {
 			isa = XCBuildConfiguration;
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
 			buildSettings = {
 			buildSettings = {
 				ALWAYS_SEARCH_USER_PATHS = NO;
 				ALWAYS_SEARCH_USER_PATHS = NO;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NONNULL = YES;
@@ -492,11 +521,12 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = iphoneos;
 				SDKROOT = iphoneos;
 				SUPPORTED_PLATFORMS = iphoneos;
 				SUPPORTED_PLATFORMS = iphoneos;
-				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+				SWIFT_COMPILATION_MODE = wholemodule;
+				SWIFT_OPTIMIZATION_LEVEL = "-O";
 				TARGETED_DEVICE_FAMILY = "1,2";
 				TARGETED_DEVICE_FAMILY = "1,2";
 				VALIDATE_PRODUCT = YES;
 				VALIDATE_PRODUCT = YES;
 			};
 			};
@@ -510,15 +540,10 @@
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_MODULES = YES;
 				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
 				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
 				ENABLE_BITCODE = NO;
 				ENABLE_BITCODE = NO;
-				FRAMEWORK_SEARCH_PATHS = (
-					"$(inherited)",
-					"$(PROJECT_DIR)/Flutter",
-				);
 				INFOPLIST_FILE = Runner/Info.plist;
 				INFOPLIST_FILE = Runner/Info.plist;
-				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
-				LIBRARY_SEARCH_PATHS = (
+				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(inherited)",
-					"$(PROJECT_DIR)/Flutter",
+					"@executable_path/Frameworks",
 				);
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = tech.soit.flutterTetris;
 				PRODUCT_BUNDLE_IDENTIFIER = tech.soit.flutterTetris;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -537,15 +562,10 @@
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_MODULES = YES;
 				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
 				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
 				ENABLE_BITCODE = NO;
 				ENABLE_BITCODE = NO;
-				FRAMEWORK_SEARCH_PATHS = (
-					"$(inherited)",
-					"$(PROJECT_DIR)/Flutter",
-				);
 				INFOPLIST_FILE = Runner/Info.plist;
 				INFOPLIST_FILE = Runner/Info.plist;
-				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
-				LIBRARY_SEARCH_PATHS = (
+				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(inherited)",
-					"$(PROJECT_DIR)/Flutter",
+					"@executable_path/Frameworks",
 				);
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = tech.soit.flutterTetris;
 				PRODUCT_BUNDLE_IDENTIFIER = tech.soit.flutterTetris;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -558,6 +578,16 @@
 /* End XCBuildConfiguration section */
 /* End XCBuildConfiguration section */
 
 
 /* Begin XCConfigurationList section */
 /* Begin XCConfigurationList section */
+		331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				331C8088294A63A400263BE5 /* Debug */,
+				331C8089294A63A400263BE5 /* Release */,
+				331C808A294A63A400263BE5 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 		97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
 		97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
 			isa = XCConfigurationList;
 			isa = XCConfigurationList;
 			buildConfigurations = (
 			buildConfigurations = (

+ 1 - 1
ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -2,6 +2,6 @@
 <Workspace
 <Workspace
    version = "1.0">
    version = "1.0">
    <FileRef
    <FileRef
-      location = "group:Runner.xcodeproj">
+      location = "self:">
    </FileRef>
    </FileRef>
 </Workspace>
 </Workspace>

+ 8 - 0
ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>

+ 8 - 0
ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>PreviewsEnabled</key>
+	<false/>
+</dict>
+</plist>

+ 14 - 7
ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
 <Scheme
-   LastUpgradeVersion = "1020"
+   LastUpgradeVersion = "1430"
    version = "1.3">
    version = "1.3">
    <BuildAction
    <BuildAction
       parallelizeBuildables = "YES"
       parallelizeBuildables = "YES"
@@ -27,8 +27,6 @@
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       shouldUseLaunchSchemeArgsEnv = "YES">
       shouldUseLaunchSchemeArgsEnv = "YES">
-      <Testables>
-      </Testables>
       <MacroExpansion>
       <MacroExpansion>
          <BuildableReference
          <BuildableReference
             BuildableIdentifier = "primary"
             BuildableIdentifier = "primary"
@@ -38,8 +36,19 @@
             ReferencedContainer = "container:Runner.xcodeproj">
             ReferencedContainer = "container:Runner.xcodeproj">
          </BuildableReference>
          </BuildableReference>
       </MacroExpansion>
       </MacroExpansion>
-      <AdditionalOptions>
-      </AdditionalOptions>
+      <Testables>
+         <TestableReference
+            skipped = "NO"
+            parallelizable = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "331C8080294A63A400263BE5"
+               BuildableName = "RunnerTests.xctest"
+               BlueprintName = "RunnerTests"
+               ReferencedContainer = "container:Runner.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
    </TestAction>
    </TestAction>
    <LaunchAction
    <LaunchAction
       buildConfiguration = "Debug"
       buildConfiguration = "Debug"
@@ -61,8 +70,6 @@
             ReferencedContainer = "container:Runner.xcodeproj">
             ReferencedContainer = "container:Runner.xcodeproj">
          </BuildableReference>
          </BuildableReference>
       </BuildableProductRunnable>
       </BuildableProductRunnable>
-      <AdditionalOptions>
-      </AdditionalOptions>
    </LaunchAction>
    </LaunchAction>
    <ProfileAction
    <ProfileAction
       buildConfiguration = "Profile"
       buildConfiguration = "Profile"

+ 0 - 3
ios/Runner.xcworkspace/contents.xcworkspacedata

@@ -4,7 +4,4 @@
    <FileRef
    <FileRef
       location = "group:Runner.xcodeproj">
       location = "group:Runner.xcodeproj">
    </FileRef>
    </FileRef>
-   <FileRef
-      location = "group:Pods/Pods.xcodeproj">
-   </FileRef>
 </Workspace>
 </Workspace>

+ 8 - 0
ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>

+ 8 - 0
ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>PreviewsEnabled</key>
+	<false/>
+</dict>
+</plist>

+ 6 - 2
ios/Runner/Info.plist

@@ -4,6 +4,8 @@
 <dict>
 <dict>
 	<key>CFBundleDevelopmentRegion</key>
 	<key>CFBundleDevelopmentRegion</key>
 	<string>$(DEVELOPMENT_LANGUAGE)</string>
 	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleDisplayName</key>
+	<string>Flutter Tetris</string>
 	<key>CFBundleExecutable</key>
 	<key>CFBundleExecutable</key>
 	<string>$(EXECUTABLE_NAME)</string>
 	<string>$(EXECUTABLE_NAME)</string>
 	<key>CFBundleIdentifier</key>
 	<key>CFBundleIdentifier</key>
@@ -39,7 +41,9 @@
 		<string>UIInterfaceOrientationLandscapeLeft</string>
 		<string>UIInterfaceOrientationLandscapeLeft</string>
 		<string>UIInterfaceOrientationLandscapeRight</string>
 		<string>UIInterfaceOrientationLandscapeRight</string>
 	</array>
 	</array>
-	<key>UIViewControllerBasedStatusBarAppearance</key>
-	<false/>
+	<key>CADisableMinimumFrameDurationOnPhone</key>
+	<true/>
+	<key>UIApplicationSupportsIndirectInputEvents</key>
+	<true/>
 </dict>
 </dict>
 </plist>
 </plist>

+ 12 - 0
ios/RunnerTests/RunnerTests.swift

@@ -0,0 +1,12 @@
+import Flutter
+import UIKit
+import XCTest
+
+class RunnerTests: XCTestCase {
+
+  func testExample() {
+    // If you add code to the Runner application, consider adding tests here.
+    // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
+  }
+
+}

+ 13 - 13
lib/gamer/block.dart

@@ -1,7 +1,7 @@
 import 'gamer.dart';
 import 'gamer.dart';
 import 'dart:math' as math;
 import 'dart:math' as math;
 
 
-const BLOCK_SHAPES = {
+const blockShapes = {
   BlockType.I: [
   BlockType.I: [
     [1, 1, 1, 1]
     [1, 1, 1, 1]
   ],
   ],
@@ -32,7 +32,7 @@ const BLOCK_SHAPES = {
 };
 };
 
 
 ///方块初始化时的位置
 ///方块初始化时的位置
-const START_XY = {
+const startXy = {
   BlockType.I: [3, 0],
   BlockType.I: [3, 0],
   BlockType.L: [4, -1],
   BlockType.L: [4, -1],
   BlockType.J: [4, -1],
   BlockType.J: [4, -1],
@@ -43,7 +43,7 @@ const START_XY = {
 };
 };
 
 
 ///方块变换时的中心点
 ///方块变换时的中心点
-const ORIGIN = {
+const origin = {
   BlockType.I: [
   BlockType.I: [
     [1, -1],
     [1, -1],
     [-1, 1],
     [-1, 1],
@@ -95,29 +95,29 @@ class Block {
 
 
   Block rotate() {
   Block rotate() {
     List<List<int>> result =
     List<List<int>> result =
-        List.filled(shape[0].length, null, growable: false);
+        List.filled(shape[0].length, const [], growable: false);
     for (int row = 0; row < shape.length; row++) {
     for (int row = 0; row < shape.length; row++) {
       for (int col = 0; col < shape[row].length; col++) {
       for (int col = 0; col < shape[row].length; col++) {
-        if (result[col] == null) {
+        if (result[col].isEmpty) {
           result[col] = List.filled(shape.length, 0, growable: false);
           result[col] = List.filled(shape.length, 0, growable: false);
         }
         }
         result[col][row] = shape[shape.length - 1 - row][col];
         result[col][row] = shape[shape.length - 1 - row][col];
       }
       }
     }
     }
     final nextXy = [
     final nextXy = [
-      this.xy[0] + ORIGIN[type][rotateIndex][0],
-      this.xy[1] + ORIGIN[type][rotateIndex][1]
+      xy[0] + origin[type]![rotateIndex][0],
+      xy[1] + origin[type]![rotateIndex][1]
     ];
     ];
     final nextRotateIndex =
     final nextRotateIndex =
-        rotateIndex + 1 >= ORIGIN[this.type].length ? 0 : rotateIndex + 1;
+        rotateIndex + 1 >= origin[type]!.length ? 0 : rotateIndex + 1;
 
 
     return Block(type, result, nextXy, nextRotateIndex);
     return Block(type, result, nextXy, nextRotateIndex);
   }
   }
 
 
   bool isValidInMatrix(List<List<int>> matrix) {
   bool isValidInMatrix(List<List<int>> matrix) {
-    if (xy[1] + shape.length > GAME_PAD_MATRIX_H ||
+    if (xy[1] + shape.length > gamePadMatrixH ||
         xy[0] < 0 ||
         xy[0] < 0 ||
-        xy[0] + shape[0].length > GAME_PAD_MATRIX_W) {
+        xy[0] + shape[0].length > gamePadMatrixW) {
       return false;
       return false;
     }
     }
     for (var i = 0; i < matrix.length; i++) {
     for (var i = 0; i < matrix.length; i++) {
@@ -133,7 +133,7 @@ class Block {
 
 
   ///return null if do not show at [x][y]
   ///return null if do not show at [x][y]
   ///return 1 if show at [x,y]
   ///return 1 if show at [x,y]
-  int get(int x, int y) {
+  int? get(int x, int y) {
     x -= xy[0];
     x -= xy[0];
     y -= xy[1];
     y -= xy[1];
     if (x < 0 || x >= shape[0].length || y < 0 || y >= shape.length) {
     if (x < 0 || x >= shape[0].length || y < 0 || y >= shape.length) {
@@ -143,8 +143,8 @@ class Block {
   }
   }
 
 
   static Block fromType(BlockType type) {
   static Block fromType(BlockType type) {
-    final shape = BLOCK_SHAPES[type];
-    return Block(type, shape, START_XY[type], 0);
+    final shape = blockShapes[type];
+    return Block(type, shape!, startXy[type]!, 0);
   }
   }
 
 
   static Block getRandom() {
   static Block getRandom() {

+ 77 - 74
lib/gamer/gamer.dart

@@ -2,14 +2,14 @@ import 'dart:async';
 
 
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:tetris/gamer/block.dart';
 import 'package:tetris/gamer/block.dart';
+import 'package:tetris/main.dart';
 import 'package:tetris/material/audios.dart';
 import 'package:tetris/material/audios.dart';
-import 'package:tetris/model/config.dart';
 
 
 ///the height of game pad
 ///the height of game pad
-const GAME_PAD_MATRIX_H = 20;
+const gamePadMatrixH = 20;
 
 
 ///the width of game pad
 ///the width of game pad
-const GAME_PAD_MATRIX_W = 10;
+const gamePadMatrixW = 10;
 
 
 ///state of [GameControl]
 ///state of [GameControl]
 enum GameStates {
 enum GameStates {
@@ -42,9 +42,7 @@ enum GameStates {
 class Game extends StatefulWidget {
 class Game extends StatefulWidget {
   final Widget child;
   final Widget child;
 
 
-  const Game({Key key, @required this.child})
-      : assert(child != null),
-        super(key: key);
+  const Game({Key? key, required this.child}) : super(key: key);
 
 
   @override
   @override
   State<StatefulWidget> createState() {
   State<StatefulWidget> createState() {
@@ -54,39 +52,39 @@ class Game extends StatefulWidget {
   static GameControl of(BuildContext context) {
   static GameControl of(BuildContext context) {
     final state = context.findAncestorStateOfType<GameControl>();
     final state = context.findAncestorStateOfType<GameControl>();
     assert(state != null, "must wrap this context with [Game]");
     assert(state != null, "must wrap this context with [Game]");
-    return state;
+    return state!;
   }
   }
 }
 }
 
 
 ///duration for show a line when reset
 ///duration for show a line when reset
-const _REST_LINE_DURATION = const Duration(milliseconds: 50);
+const restLineDuration = Duration(milliseconds: 50);
 
 
-const _LEVEL_MAX = 6;
+const levelMax = 6;
 
 
-const _LEVEL_MIN = 1;
+const levelMin = 1;
 
 
-const _SPEED = [
-  const Duration(milliseconds: 800),
-  const Duration(milliseconds: 650),
-  const Duration(milliseconds: 500),
-  const Duration(milliseconds: 370),
-  const Duration(milliseconds: 250),
-  const Duration(milliseconds: 160),
+const speed = [
+  Duration(milliseconds: 800),
+  Duration(milliseconds: 650),
+  Duration(milliseconds: 500),
+  Duration(milliseconds: 370),
+  Duration(milliseconds: 250),
+  Duration(milliseconds: 160),
 ];
 ];
 
 
 class GameControl extends State<Game> with RouteAware {
 class GameControl extends State<Game> with RouteAware {
   GameControl() {
   GameControl() {
     //inflate game pad data
     //inflate game pad data
-    for (int i = 0; i < GAME_PAD_MATRIX_H; i++) {
-      _data.add(List.filled(GAME_PAD_MATRIX_W, 0));
-      _mask.add(List.filled(GAME_PAD_MATRIX_W, 0));
+    for (int i = 0; i < gamePadMatrixH; i++) {
+      _data.add(List.filled(gamePadMatrixW, 0));
+      _mask.add(List.filled(gamePadMatrixW, 0));
     }
     }
   }
   }
 
 
   @override
   @override
   void didChangeDependencies() {
   void didChangeDependencies() {
     super.didChangeDependencies();
     super.didChangeDependencies();
-    routeObserver.subscribe(this, ModalRoute.of(context));
+    routeObserver.subscribe(this, ModalRoute.of(context)!);
   }
   }
 
 
   @override
   @override
@@ -119,7 +117,7 @@ class GameControl extends State<Game> with RouteAware {
 
 
   int _cleared = 0;
   int _cleared = 0;
 
 
-  Block _current;
+  Block? _current;
 
 
   Block _next = Block.getRandom();
   Block _next = Block.getRandom();
 
 
@@ -134,9 +132,9 @@ class GameControl extends State<Game> with RouteAware {
   SoundState get _sound => Sound.of(context);
   SoundState get _sound => Sound.of(context);
 
 
   void rotate() {
   void rotate() {
-    if (_states == GameStates.running && _current != null) {
-      final next = _current.rotate();
-      if (next.isValidInMatrix(_data)) {
+    if (_states == GameStates.running) {
+      final next = _current?.rotate();
+      if (next != null && next.isValidInMatrix(_data)) {
         _current = next;
         _current = next;
         _sound.rotate();
         _sound.rotate();
       }
       }
@@ -145,11 +143,11 @@ class GameControl extends State<Game> with RouteAware {
   }
   }
 
 
   void right() {
   void right() {
-    if (_states == GameStates.none && _level < _LEVEL_MAX) {
+    if (_states == GameStates.none && _level < levelMax) {
       _level++;
       _level++;
-    } else if (_states == GameStates.running && _current != null) {
-      final next = _current.right();
-      if (next.isValidInMatrix(_data)) {
+    } else if (_states == GameStates.running) {
+      final next = _current?.right();
+      if (next != null && next.isValidInMatrix(_data)) {
         _current = next;
         _current = next;
         _sound.move();
         _sound.move();
       }
       }
@@ -158,11 +156,11 @@ class GameControl extends State<Game> with RouteAware {
   }
   }
 
 
   void left() {
   void left() {
-    if (_states == GameStates.none && _level > _LEVEL_MIN) {
+    if (_states == GameStates.none && _level > levelMin) {
       _level--;
       _level--;
-    } else if (_states == GameStates.running && _current != null) {
-      final next = _current.left();
-      if (next.isValidInMatrix(_data)) {
+    } else if (_states == GameStates.running) {
+      final next = _current?.left();
+      if (next != null && next.isValidInMatrix(_data)) {
         _current = next;
         _current = next;
         _sound.move();
         _sound.move();
       }
       }
@@ -171,11 +169,11 @@ class GameControl extends State<Game> with RouteAware {
   }
   }
 
 
   void drop() async {
   void drop() async {
-    if (_states == GameStates.running && _current != null) {
-      for (int i = 0; i < GAME_PAD_MATRIX_H; i++) {
-        final fall = _current.fall(step: i + 1);
-        if (!fall.isValidInMatrix(_data)) {
-          _current = _current.fall(step: i);
+    if (_states == GameStates.running) {
+      for (int i = 0; i < gamePadMatrixH; i++) {
+        final fall = _current?.fall(step: i + 1);
+        if (fall != null && !fall.isValidInMatrix(_data)) {
+          _current = _current?.fall(step: i);
           _states = GameStates.drop;
           _states = GameStates.drop;
           setState(() {});
           setState(() {});
           await Future.delayed(const Duration(milliseconds: 100));
           await Future.delayed(const Duration(milliseconds: 100));
@@ -190,9 +188,9 @@ class GameControl extends State<Game> with RouteAware {
   }
   }
 
 
   void down({bool enableSounds = true}) {
   void down({bool enableSounds = true}) {
-    if (_states == GameStates.running && _current != null) {
-      final next = _current.fall();
-      if (next.isValidInMatrix(_data)) {
+    if (_states == GameStates.running) {
+      final next = _current?.fall();
+      if (next != null && next.isValidInMatrix(_data)) {
         _current = next;
         _current = next;
         if (enableSounds) {
         if (enableSounds) {
           _sound.move();
           _sound.move();
@@ -204,21 +202,21 @@ class GameControl extends State<Game> with RouteAware {
     setState(() {});
     setState(() {});
   }
   }
 
 
-  Timer _autoFallTimer;
+  Timer? _autoFallTimer;
 
 
   ///mix current into [_data]
   ///mix current into [_data]
-  Future<void> _mixCurrentIntoData({void mixSound()}) async {
+  Future<void> _mixCurrentIntoData({VoidCallback? mixSound}) async {
     if (_current == null) {
     if (_current == null) {
       return;
       return;
     }
     }
     //cancel the auto falling task
     //cancel the auto falling task
     _autoFall(false);
     _autoFall(false);
 
 
-    _forTable((i, j) => _data[i][j] = _current.get(j, i) ?? _data[i][j]);
+    _forTable((i, j) => _data[i][j] = _current?.get(j, i) ?? _data[i][j]);
 
 
     //消除行
     //消除行
     final clearLines = [];
     final clearLines = [];
-    for (int i = 0; i < GAME_PAD_MATRIX_H; i++) {
+    for (int i = 0; i < gamePadMatrixH; i++) {
       if (_data[i].every((d) => d == 1)) {
       if (_data[i].every((d) => d == 1)) {
         clearLines.add(i);
         clearLines.add(i);
       }
       }
@@ -232,18 +230,17 @@ class GameControl extends State<Game> with RouteAware {
       ///消除效果动画
       ///消除效果动画
       for (int count = 0; count < 5; count++) {
       for (int count = 0; count < 5; count++) {
         clearLines.forEach((line) {
         clearLines.forEach((line) {
-          _mask[line].fillRange(0, GAME_PAD_MATRIX_W, count % 2 == 0 ? -1 : 1);
+          _mask[line].fillRange(0, gamePadMatrixW, count % 2 == 0 ? -1 : 1);
         });
         });
         setState(() {});
         setState(() {});
-        await Future.delayed(Duration(milliseconds: 100));
+        await Future.delayed(const Duration(milliseconds: 100));
       }
       }
-      clearLines
-          .forEach((line) => _mask[line].fillRange(0, GAME_PAD_MATRIX_W, 0));
+      clearLines.forEach((line) => _mask[line].fillRange(0, gamePadMatrixW, 0));
 
 
       //移除所有被消除的行
       //移除所有被消除的行
       clearLines.forEach((line) {
       clearLines.forEach((line) {
         _data.setRange(1, line + 1, _data);
         _data.setRange(1, line + 1, _data);
-        _data[0] = List.filled(GAME_PAD_MATRIX_W, 0);
+        _data[0] = List.filled(gamePadMatrixW, 0);
       });
       });
       debugPrint("clear lines : $clearLines");
       debugPrint("clear lines : $clearLines");
 
 
@@ -251,12 +248,12 @@ class GameControl extends State<Game> with RouteAware {
       _points += clearLines.length * _level * 5;
       _points += clearLines.length * _level * 5;
 
 
       //up level possible when cleared
       //up level possible when cleared
-      int level = (_cleared ~/ 50) + _LEVEL_MIN;
-      _level = level <= _LEVEL_MAX && level > _level ? level : _level;
+      int level = (_cleared ~/ 50) + levelMin;
+      _level = level <= levelMax && level > _level ? level : _level;
     } else {
     } else {
       _states = GameStates.mixing;
       _states = GameStates.mixing;
-      if (mixSound != null) mixSound();
-      _forTable((i, j) => _mask[i][j] = _current.get(j, i) ?? _mask[i][j]);
+      mixSound?.call();
+      _forTable((i, j) => _mask[i][j] = _current?.get(j, i) ?? _mask[i][j]);
       setState(() {});
       setState(() {});
       await Future.delayed(const Duration(milliseconds: 200));
       await Future.delayed(const Duration(milliseconds: 200));
       _forTable((i, j) => _mask[i][j] = 0);
       _forTable((i, j) => _mask[i][j] = 0);
@@ -280,8 +277,8 @@ class GameControl extends State<Game> with RouteAware {
   ///i 为 row
   ///i 为 row
   ///j 为 column
   ///j 为 column
   static void _forTable(dynamic function(int row, int column)) {
   static void _forTable(dynamic function(int row, int column)) {
-    for (int i = 0; i < GAME_PAD_MATRIX_H; i++) {
-      for (int j = 0; j < GAME_PAD_MATRIX_W; j++) {
+    for (int i = 0; i < gamePadMatrixH; i++) {
+      for (int j = 0; j < gamePadMatrixW; j++) {
         final b = function(i, j);
         final b = function(i, j);
         if (b is bool && b) {
         if (b is bool && b) {
           break;
           break;
@@ -291,13 +288,13 @@ class GameControl extends State<Game> with RouteAware {
   }
   }
 
 
   void _autoFall(bool enable) {
   void _autoFall(bool enable) {
-    if (!enable && _autoFallTimer != null) {
-      _autoFallTimer.cancel();
+    if (!enable) {
+      _autoFallTimer?.cancel();
       _autoFallTimer = null;
       _autoFallTimer = null;
     } else if (enable) {
     } else if (enable) {
       _autoFallTimer?.cancel();
       _autoFallTimer?.cancel();
       _current = _current ?? _getNext();
       _current = _current ?? _getNext();
-      _autoFallTimer = Timer.periodic(_SPEED[_level - 1], (t) {
+      _autoFallTimer = Timer.periodic(speed[_level - 1], (t) {
         down(enableSounds: false);
         down(enableSounds: false);
       });
       });
     }
     }
@@ -330,14 +327,14 @@ class GameControl extends State<Game> with RouteAware {
     _sound.start();
     _sound.start();
     _states = GameStates.reset;
     _states = GameStates.reset;
     () async {
     () async {
-      int line = GAME_PAD_MATRIX_H;
+      int line = gamePadMatrixH;
       await Future.doWhile(() async {
       await Future.doWhile(() async {
         line--;
         line--;
-        for (int i = 0; i < GAME_PAD_MATRIX_W; i++) {
+        for (int i = 0; i < gamePadMatrixW; i++) {
           _data[line][i] = 1;
           _data[line][i] = 1;
         }
         }
         setState(() {});
         setState(() {});
-        await Future.delayed(_REST_LINE_DURATION);
+        await Future.delayed(restLineDuration);
         return line != 0;
         return line != 0;
       });
       });
       _current = null;
       _current = null;
@@ -345,13 +342,13 @@ class GameControl extends State<Game> with RouteAware {
       _points = 0;
       _points = 0;
       _cleared = 0;
       _cleared = 0;
       await Future.doWhile(() async {
       await Future.doWhile(() async {
-        for (int i = 0; i < GAME_PAD_MATRIX_W; i++) {
+        for (int i = 0; i < gamePadMatrixW; i++) {
           _data[line][i] = 0;
           _data[line][i] = 0;
         }
         }
         setState(() {});
         setState(() {});
         line++;
         line++;
-        await Future.delayed(_REST_LINE_DURATION);
-        return line != GAME_PAD_MATRIX_H;
+        await Future.delayed(restLineDuration);
+        return line != gamePadMatrixH;
       });
       });
       setState(() {
       setState(() {
         _states = GameStates.none;
         _states = GameStates.none;
@@ -371,9 +368,9 @@ class GameControl extends State<Game> with RouteAware {
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     List<List<int>> mixed = [];
     List<List<int>> mixed = [];
-    for (var i = 0; i < GAME_PAD_MATRIX_H; i++) {
-      mixed.add(List.filled(GAME_PAD_MATRIX_W, 0));
-      for (var j = 0; j < GAME_PAD_MATRIX_W; j++) {
+    for (var i = 0; i < gamePadMatrixH; i++) {
+      mixed.add(List.filled(gamePadMatrixW, 0));
+      for (var j = 0; j < gamePadMatrixW; j++) {
         int value = _current?.get(j, i) ?? _data[i][j];
         int value = _current?.get(j, i) ?? _data[i][j];
         if (_mask[i][j] == -1) {
         if (_mask[i][j] == -1) {
           value = 0;
           value = 0;
@@ -397,10 +394,17 @@ class GameControl extends State<Game> with RouteAware {
 }
 }
 
 
 class GameState extends InheritedWidget {
 class GameState extends InheritedWidget {
-  GameState(this.data, this.states, this.level, this.muted, this.points,
-      this.cleared, this.next,
-      {Key key, this.child})
-      : super(key: key, child: child);
+  const GameState(
+    this.data,
+    this.states,
+    this.level,
+    this.muted,
+    this.points,
+    this.cleared,
+    this.next, {
+    Key? key,
+    required this.child,
+  }) : super(key: key, child: child);
 
 
   final Widget child;
   final Widget child;
 
 
@@ -423,8 +427,7 @@ class GameState extends InheritedWidget {
   final Block next;
   final Block next;
 
 
   static GameState of(BuildContext context) {
   static GameState of(BuildContext context) {
-    return (context.dependOnInheritedWidgetOfExactType(aspect: GameState)
-        as GameState);
+    return context.dependOnInheritedWidgetOfExactType<GameState>()!;
   }
   }
 
 
   @override
   @override

+ 11 - 16
lib/gamer/keyboard.dart

@@ -6,46 +6,42 @@ import 'gamer.dart';
 ///keyboard controller to play game
 ///keyboard controller to play game
 class KeyboardController extends StatefulWidget {
 class KeyboardController extends StatefulWidget {
   final Widget child;
   final Widget child;
-
-  KeyboardController({this.child});
+  const KeyboardController({super.key, required this.child});
 
 
   @override
   @override
-  _KeyboardControllerState createState() => _KeyboardControllerState();
+  State<KeyboardController> createState() => _KeyboardControllerState();
 }
 }
 
 
 class _KeyboardControllerState extends State<KeyboardController> {
 class _KeyboardControllerState extends State<KeyboardController> {
-
   @override
   @override
   void initState() {
   void initState() {
     super.initState();
     super.initState();
-    // 监听按钮
     RawKeyboard.instance.addListener(_onKey);
     RawKeyboard.instance.addListener(_onKey);
   }
   }
 
 
   void _onKey(RawKeyEvent event) {
   void _onKey(RawKeyEvent event) {
-
     if (event is RawKeyUpEvent) {
     if (event is RawKeyUpEvent) {
       return;
       return;
     }
     }
 
 
-    final key = event.data.physicalKey;
+    final key = event.data.logicalKey;
     final game = Game.of(context);
     final game = Game.of(context);
 
 
-    if (key == PhysicalKeyboardKey.arrowUp) {
+    if (key == LogicalKeyboardKey.arrowUp) {
       game.rotate();
       game.rotate();
-    } else if (key == PhysicalKeyboardKey.arrowDown) {
+    } else if (key == LogicalKeyboardKey.arrowDown) {
       game.down();
       game.down();
-    } else if (key == PhysicalKeyboardKey.arrowLeft) {
+    } else if (key == LogicalKeyboardKey.arrowLeft) {
       game.left();
       game.left();
-    } else if (key == PhysicalKeyboardKey.arrowRight) {
+    } else if (key == LogicalKeyboardKey.arrowRight) {
       game.right();
       game.right();
-    } else if (key == PhysicalKeyboardKey.space) {
+    } else if (key == LogicalKeyboardKey.space) {
       game.drop();
       game.drop();
-    } else if (key == PhysicalKeyboardKey.keyP) {
+    } else if (key == LogicalKeyboardKey.keyP) {
       game.pauseOrResume();
       game.pauseOrResume();
-    } else if (key == PhysicalKeyboardKey.keyS) {
+    } else if (key == LogicalKeyboardKey.keyS) {
       game.soundSwitch();
       game.soundSwitch();
-    } else if (key == PhysicalKeyboardKey.keyR) {
+    } else if (key == LogicalKeyboardKey.keyR) {
       game.reset();
       game.reset();
     }
     }
   }
   }
@@ -60,5 +56,4 @@ class _KeyboardControllerState extends State<KeyboardController> {
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     return widget.child;
     return widget.child;
   }
   }
-
 }
 }

+ 0 - 130
lib/generated/i18n.dart

@@ -1,130 +0,0 @@
-import 'dart:async';
-
-import 'package:flutter/foundation.dart';
-import 'package:flutter/material.dart';
-
-// ignore_for_file: non_constant_identifier_names
-// ignore_for_file: camel_case_types
-// ignore_for_file: prefer_single_quotes
-
-//This file is automatically generated. DO NOT EDIT, all your changes would be lost.
-class S implements WidgetsLocalizations {
-  const S();
-
-  static const GeneratedLocalizationsDelegate delegate =
-    GeneratedLocalizationsDelegate();
-
-  static S of(BuildContext context) => Localizations.of<S>(context, S);
-
-  @override
-  TextDirection get textDirection => TextDirection.ltr;
-
-  String get cleans => "Cleans";
-  String get level => "Level";
-  String get next => "Next";
-  String get pause_resume => "PAUSE/RESUME";
-  String get points => "Points";
-  String get reset => "RESET";
-  String get reward => "Reward";
-  String get sounds => "SOUNDS";
-}
-
-class $en extends S {
-  const $en();
-}
-
-class $zh_CN extends S {
-  const $zh_CN();
-
-  @override
-  TextDirection get textDirection => TextDirection.ltr;
-
-  @override
-  String get next => "下一个";
-  @override
-  String get reward => "赞赏";
-  @override
-  String get sounds => "声音";
-  @override
-  String get pause_resume => "暂停/恢复";
-  @override
-  String get level => "级别";
-  @override
-  String get reset => "重置";
-  @override
-  String get cleans => "消除";
-  @override
-  String get points => "分数";
-}
-
-class GeneratedLocalizationsDelegate extends LocalizationsDelegate<S> {
-  const GeneratedLocalizationsDelegate();
-
-  List<Locale> get supportedLocales {
-    return const <Locale>[
-      Locale("en", ""),
-      Locale("zh", "CN"),
-    ];
-  }
-
-  LocaleListResolutionCallback listResolution({Locale fallback}) {
-    return (List<Locale> locales, Iterable<Locale> supported) {
-      if (locales == null || locales.isEmpty) {
-        return fallback ?? supported.first;
-      } else {
-        return _resolve(locales.first, fallback, supported);
-      }
-    };
-  }
-
-  LocaleResolutionCallback resolution({Locale fallback}) {
-    return (Locale locale, Iterable<Locale> supported) {
-      return _resolve(locale, fallback, supported);
-    };
-  }
-
-  Locale _resolve(Locale locale, Locale fallback, Iterable<Locale> supported) {
-    if (locale == null || !isSupported(locale)) {
-      return fallback ?? supported.first;
-    }
-
-    final Locale languageLocale = Locale(locale.languageCode, "");
-    if (supported.contains(locale)) {
-      return locale;
-    } else if (supported.contains(languageLocale)) {
-      return languageLocale;
-    } else {
-      final Locale fallbackLocale = fallback ?? supported.first;
-      return fallbackLocale;
-    }
-  }
-
-  @override
-  Future<S> load(Locale locale) {
-    final String lang = getLang(locale);
-    if (lang != null) {
-      switch (lang) {
-        case "en":
-          return SynchronousFuture<S>(const $en());
-        case "zh_CN":
-          return SynchronousFuture<S>(const $zh_CN());
-        default:
-          // NO-OP.
-      }
-    }
-    return SynchronousFuture<S>(const S());
-  }
-
-  @override
-  bool isSupported(Locale locale) =>
-    locale != null && supportedLocales.contains(locale);
-
-  @override
-  bool shouldReload(GeneratedLocalizationsDelegate old) => false;
-}
-
-String getLang(Locale l) => l == null
-  ? null
-  : l.countryCode != null && l.countryCode.isEmpty
-    ? l.languageCode
-    : l.toString();

+ 67 - 0
lib/generated/intl/messages_all.dart

@@ -0,0 +1,67 @@
+// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
+// This is a library that looks up messages for specific locales by
+// delegating to the appropriate library.
+
+// Ignore issues from commonly used lints in this file.
+// ignore_for_file:implementation_imports, file_names, unnecessary_new
+// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering
+// ignore_for_file:argument_type_not_assignable, invalid_assignment
+// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases
+// ignore_for_file:comment_references
+
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:intl/intl.dart';
+import 'package:intl/message_lookup_by_library.dart';
+import 'package:intl/src/intl_helpers.dart';
+
+import 'messages_en.dart' as messages_en;
+import 'messages_zh_CN.dart' as messages_zh_cn;
+
+typedef Future<dynamic> LibraryLoader();
+Map<String, LibraryLoader> _deferredLibraries = {
+  'en': () => new SynchronousFuture(null),
+  'zh_CN': () => new SynchronousFuture(null),
+};
+
+MessageLookupByLibrary? _findExact(String localeName) {
+  switch (localeName) {
+    case 'en':
+      return messages_en.messages;
+    case 'zh_CN':
+      return messages_zh_cn.messages;
+    default:
+      return null;
+  }
+}
+
+/// User programs should call this before using [localeName] for messages.
+Future<bool> initializeMessages(String localeName) {
+  var availableLocale = Intl.verifiedLocale(
+      localeName, (locale) => _deferredLibraries[locale] != null,
+      onFailure: (_) => null);
+  if (availableLocale == null) {
+    return new SynchronousFuture(false);
+  }
+  var lib = _deferredLibraries[availableLocale];
+  lib == null ? new SynchronousFuture(false) : lib();
+  initializeInternalMessageLookup(() => new CompositeMessageLookup());
+  messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
+  return new SynchronousFuture(true);
+}
+
+bool _messagesExistFor(String locale) {
+  try {
+    return _findExact(locale) != null;
+  } catch (e) {
+    return false;
+  }
+}
+
+MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {
+  var actualLocale =
+      Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null);
+  if (actualLocale == null) return null;
+  return _findExact(actualLocale);
+}

+ 34 - 0
lib/generated/intl/messages_en.dart

@@ -0,0 +1,34 @@
+// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
+// This is a library that provides messages for a en locale. All the
+// messages from the main program should be duplicated here with the same
+// function name.
+
+// Ignore issues from commonly used lints in this file.
+// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
+// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
+// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
+// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
+// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes
+
+import 'package:intl/intl.dart';
+import 'package:intl/message_lookup_by_library.dart';
+
+final messages = new MessageLookup();
+
+typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
+
+class MessageLookup extends MessageLookupByLibrary {
+  String get localeName => 'en';
+
+  final messages = _notInlinedMessages(_notInlinedMessages);
+  static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
+        "cleans": MessageLookupByLibrary.simpleMessage("Cleans"),
+        "level": MessageLookupByLibrary.simpleMessage("Level"),
+        "next": MessageLookupByLibrary.simpleMessage("Next"),
+        "pause_resume": MessageLookupByLibrary.simpleMessage("PAUSE/RESUME"),
+        "points": MessageLookupByLibrary.simpleMessage("Points"),
+        "reset": MessageLookupByLibrary.simpleMessage("RESET"),
+        "reward": MessageLookupByLibrary.simpleMessage("Reward"),
+        "sounds": MessageLookupByLibrary.simpleMessage("SOUNDS")
+      };
+}

+ 34 - 0
lib/generated/intl/messages_zh_CN.dart

@@ -0,0 +1,34 @@
+// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
+// This is a library that provides messages for a zh_CN locale. All the
+// messages from the main program should be duplicated here with the same
+// function name.
+
+// Ignore issues from commonly used lints in this file.
+// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
+// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
+// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
+// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
+// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes
+
+import 'package:intl/intl.dart';
+import 'package:intl/message_lookup_by_library.dart';
+
+final messages = new MessageLookup();
+
+typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
+
+class MessageLookup extends MessageLookupByLibrary {
+  String get localeName => 'zh_CN';
+
+  final messages = _notInlinedMessages(_notInlinedMessages);
+  static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
+        "cleans": MessageLookupByLibrary.simpleMessage("消除"),
+        "level": MessageLookupByLibrary.simpleMessage("级别"),
+        "next": MessageLookupByLibrary.simpleMessage("下一个"),
+        "pause_resume": MessageLookupByLibrary.simpleMessage("暂停/恢复"),
+        "points": MessageLookupByLibrary.simpleMessage("分数"),
+        "reset": MessageLookupByLibrary.simpleMessage("重置"),
+        "reward": MessageLookupByLibrary.simpleMessage("赞赏"),
+        "sounds": MessageLookupByLibrary.simpleMessage("声音")
+      };
+}

+ 159 - 0
lib/generated/l10n.dart

@@ -0,0 +1,159 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+import 'package:flutter/material.dart';
+import 'package:intl/intl.dart';
+import 'intl/messages_all.dart';
+
+// **************************************************************************
+// Generator: Flutter Intl IDE plugin
+// Made by Localizely
+// **************************************************************************
+
+// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars
+// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each
+// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes
+
+class S {
+  S();
+
+  static S? _current;
+
+  static S get current {
+    assert(_current != null,
+        'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.');
+    return _current!;
+  }
+
+  static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
+
+  static Future<S> load(Locale locale) {
+    final name = (locale.countryCode?.isEmpty ?? false)
+        ? locale.languageCode
+        : locale.toString();
+    final localeName = Intl.canonicalizedLocale(name);
+    return initializeMessages(localeName).then((_) {
+      Intl.defaultLocale = localeName;
+      final instance = S();
+      S._current = instance;
+
+      return instance;
+    });
+  }
+
+  static S of(BuildContext context) {
+    final instance = S.maybeOf(context);
+    assert(instance != null,
+        'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?');
+    return instance!;
+  }
+
+  static S? maybeOf(BuildContext context) {
+    return Localizations.of<S>(context, S);
+  }
+
+  /// `PAUSE/RESUME`
+  String get pause_resume {
+    return Intl.message(
+      'PAUSE/RESUME',
+      name: 'pause_resume',
+      desc: '',
+      args: [],
+    );
+  }
+
+  /// `RESET`
+  String get reset {
+    return Intl.message(
+      'RESET',
+      name: 'reset',
+      desc: '',
+      args: [],
+    );
+  }
+
+  /// `SOUNDS`
+  String get sounds {
+    return Intl.message(
+      'SOUNDS',
+      name: 'sounds',
+      desc: '',
+      args: [],
+    );
+  }
+
+  /// `Points`
+  String get points {
+    return Intl.message(
+      'Points',
+      name: 'points',
+      desc: '',
+      args: [],
+    );
+  }
+
+  /// `Cleans`
+  String get cleans {
+    return Intl.message(
+      'Cleans',
+      name: 'cleans',
+      desc: '',
+      args: [],
+    );
+  }
+
+  /// `Level`
+  String get level {
+    return Intl.message(
+      'Level',
+      name: 'level',
+      desc: '',
+      args: [],
+    );
+  }
+
+  /// `Next`
+  String get next {
+    return Intl.message(
+      'Next',
+      name: 'next',
+      desc: '',
+      args: [],
+    );
+  }
+
+  /// `Reward`
+  String get reward {
+    return Intl.message(
+      'Reward',
+      name: 'reward',
+      desc: '',
+      args: [],
+    );
+  }
+}
+
+class AppLocalizationDelegate extends LocalizationsDelegate<S> {
+  const AppLocalizationDelegate();
+
+  List<Locale> get supportedLocales {
+    return const <Locale>[
+      Locale.fromSubtags(languageCode: 'en'),
+      Locale.fromSubtags(languageCode: 'zh', countryCode: 'CN'),
+    ];
+  }
+
+  @override
+  bool isSupported(Locale locale) => _isSupported(locale);
+  @override
+  Future<S> load(Locale locale) => S.load(locale);
+  @override
+  bool shouldReload(AppLocalizationDelegate old) => false;
+
+  bool _isSupported(Locale locale) {
+    for (var supportedLocale in supportedLocales) {
+      if (supportedLocale.languageCode == locale.languageCode) {
+        return true;
+      }
+    }
+    return false;
+  }
+}

+ 0 - 118
lib/income/donation_dialog.dart

@@ -1,118 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-import 'package:overlay_support/overlay_support.dart';
-
-const HONG_BAO = "打开支付宝首页搜“621412820”领红包,领到大红包的小伙伴赶紧使用哦!";
-
-class DonationDialog extends StatelessWidget {
-  @override
-  Widget build(BuildContext context) {
-    return SimpleDialog(
-      contentPadding:
-          const EdgeInsets.only(top: 8, left: 8, right: 8, bottom: 4),
-      children: <Widget>[
-        SizedBox(width: MediaQuery.of(context).size.width),
-        Container(
-            padding: const EdgeInsets.all(16), child: Text("开发不易,赞助一下开发者。")),
-        _ActionTile(
-          text: "微信捐赠",
-          onTap: () async {
-            await showDialog(
-                context: context,
-                builder: (context) => _ReceiptDialog.weChat());
-            Navigator.pop(context);
-          },
-        ),
-        _ActionTile(
-          text: "支付宝捐赠",
-          onTap: () async {
-            await showDialog(
-                context: context,
-                builder: (context) => _ReceiptDialog.aliPay());
-            Navigator.pop(context);
-          },
-        ),
-        _ActionTile(
-          text: "支付宝红包码",
-          onTap: () async {
-            await Clipboard.setData(ClipboardData(text: HONG_BAO));
-            final data = await Clipboard.getData(Clipboard.kTextPlain);
-            if (data.text == HONG_BAO) {
-              showSimpleNotification(Text("已复制到粘贴板 (≧y≦*)"));
-            } else {
-              await showDialog(
-                  context: context,
-                  builder: (context) => _SingleFieldDialog(text: HONG_BAO));
-            }
-            Navigator.of(context).pop();
-          },
-        ),
-      ],
-    );
-  }
-}
-
-class _SingleFieldDialog extends StatelessWidget {
-  final String text;
-
-  const _SingleFieldDialog({Key key, @required this.text}) : super(key: key);
-
-  @override
-  Widget build(BuildContext context) {
-    return Dialog(
-      child: Container(
-        padding: EdgeInsets.all(16),
-        child: TextField(
-          maxLines: 5,
-          autofocus: true,
-          controller: TextEditingController(text: text),
-        ),
-      ),
-    );
-  }
-}
-
-class _ReceiptDialog extends StatelessWidget {
-  final String image;
-
-  const _ReceiptDialog({Key key, this.image}) : super(key: key);
-
-  const _ReceiptDialog.weChat() : this(image: "assets/wechat.png");
-
-  const _ReceiptDialog.aliPay() : this(image: "assets/alipay.jpg");
-
-  static final borderRadius = BorderRadius.circular(5);
-
-  @override
-  Widget build(BuildContext context) {
-    return Dialog(
-      shape: RoundedRectangleBorder(borderRadius: borderRadius),
-      child: ClipRRect(borderRadius: borderRadius, child: Image.asset(image)),
-    );
-  }
-}
-
-class _ActionTile extends StatelessWidget {
-  final VoidCallback onTap;
-
-  final String text;
-
-  const _ActionTile({Key key, @required this.onTap, @required this.text})
-      : super(key: key);
-
-  @override
-  Widget build(BuildContext context) {
-    return InkWell(
-      onTap: onTap,
-      child: Container(
-        height: 40,
-        child: Row(
-          children: <Widget>[
-            SizedBox(width: 16),
-            Text(text, style: TextStyle(fontWeight: FontWeight.bold)),
-          ],
-        ),
-      ),
-    );
-  }
-}

+ 0 - 0
res/values/strings_en.arb → lib/l10n/intl_en.arb


+ 0 - 0
res/values/strings_zh_CN.arb → lib/l10n/intl_zh_CN.arb


+ 37 - 9
lib/main.dart

@@ -2,27 +2,42 @@ import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_localizations/flutter_localizations.dart';
 import 'package:flutter_localizations/flutter_localizations.dart';
 import 'package:tetris/gamer/gamer.dart';
 import 'package:tetris/gamer/gamer.dart';
-import 'package:tetris/generated/i18n.dart';
+import 'package:tetris/generated/l10n.dart';
 import 'package:tetris/material/audios.dart';
 import 'package:tetris/material/audios.dart';
-import 'package:tetris/pages/home_page.dart';
-import 'package:tetris/utils/app_util.dart';
+import 'package:tetris/panel/page_portrait.dart';
 
 
 import 'gamer/keyboard.dart';
 import 'gamer/keyboard.dart';
-import 'model/config.dart';
 
 
 void main() {
 void main() {
   debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
   debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
-  AppUtil.disableDebugPrint();
-  runApp(MyApp());
+  _disableDebugPrint();
+  runApp(const MainApp());
 }
 }
 
 
-class MyApp extends StatelessWidget {
+void _disableDebugPrint() {
+  bool debug = false;
+  assert(() {
+    debug = true;
+    return true;
+  }());
+  if (!debug) {
+    debugPrint = (message, {wrapWidth}) {
+      //disable log print when not in debug mode
+    };
+  }
+}
+
+final RouteObserver<ModalRoute> routeObserver = RouteObserver<ModalRoute>();
+
+class MainApp extends StatelessWidget {
+  const MainApp({super.key});
+
   // This widget is the root of your application.
   // This widget is the root of your application.
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     return MaterialApp(
     return MaterialApp(
       title: '俄罗斯方块',
       title: '俄罗斯方块',
-      localizationsDelegates: [
+      localizationsDelegates: const [
         S.delegate,
         S.delegate,
         GlobalMaterialLocalizations.delegate,
         GlobalMaterialLocalizations.delegate,
         GlobalWidgetsLocalizations.delegate
         GlobalWidgetsLocalizations.delegate
@@ -33,8 +48,21 @@ class MyApp extends StatelessWidget {
         primarySwatch: Colors.blue,
         primarySwatch: Colors.blue,
       ),
       ),
       home: Scaffold(
       home: Scaffold(
-        body: Sound(child: Game(child: KeyboardController(child: HomePage()))),
+        body: Sound(child: Game(child: KeyboardController(child: _HomePage()))),
       ),
       ),
     );
     );
   }
   }
 }
 }
+
+const screenBorderWidth = 3.0;
+
+const backgroundColor = Color(0xffefcc19);
+
+class _HomePage extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    //only Android/iOS support land mode
+    bool land = MediaQuery.of(context).orientation == Orientation.landscape;
+    return land ? const PageLand() : const PagePortrait();
+  }
+}

+ 8 - 8
lib/material/audios.dart

@@ -7,7 +7,7 @@ import 'package:soundpool/soundpool.dart';
 class Sound extends StatefulWidget {
 class Sound extends StatefulWidget {
   final Widget child;
   final Widget child;
 
 
-  const Sound({Key key, this.child}) : super(key: key);
+  const Sound({Key? key, required this.child}) : super(key: key);
 
 
   @override
   @override
   SoundState createState() => SoundState();
   SoundState createState() => SoundState();
@@ -15,11 +15,11 @@ class Sound extends StatefulWidget {
   static SoundState of(BuildContext context) {
   static SoundState of(BuildContext context) {
     final state = context.findAncestorStateOfType<SoundState>();
     final state = context.findAncestorStateOfType<SoundState>();
     assert(state != null, 'can not find Sound widget');
     assert(state != null, 'can not find Sound widget');
-    return state;
+    return state!;
   }
   }
 }
 }
 
 
-const _SOUNDS = [
+const sounds = [
   'clean.mp3',
   'clean.mp3',
   'drop.mp3',
   'drop.mp3',
   'explosion.mp3',
   'explosion.mp3',
@@ -29,9 +29,9 @@ const _SOUNDS = [
 ];
 ];
 
 
 class SoundState extends State<Sound> {
 class SoundState extends State<Sound> {
-  Soundpool _pool;
+  late Soundpool _pool;
 
 
-  Map<String, int> _soundIds;
+  final _soundIds = <String, int>{};
 
 
   bool mute = false;
   bool mute = false;
 
 
@@ -45,9 +45,9 @@ class SoundState extends State<Sound> {
   @override
   @override
   void initState() {
   void initState() {
     super.initState();
     super.initState();
-    _pool = Soundpool(streamType: StreamType.music, maxStreams: 4);
-    _soundIds = Map();
-    for (var value in _SOUNDS) {
+    _pool =
+        Soundpool.fromOptions(options: const SoundpoolOptions(maxStreams: 6));
+    for (var value in sounds) {
       scheduleMicrotask(() async {
       scheduleMicrotask(() async {
         final data = await rootBundle.load('assets/audios/$value');
         final data = await rootBundle.load('assets/audios/$value');
         _soundIds[value] = await _pool.load(data);
         _soundIds[value] = await _pool.load(data);

+ 15 - 18
lib/material/briks.dart

@@ -1,25 +1,22 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 
 
-const _COLOR_NORMAL = Colors.black87;
-
-const _COLOR_NULL = Colors.black12;
-
-const _COLOR_HIGHLIGHT = Color(0xFF560000);
+const colorNormal = Colors.black87;
+const colorNull = Colors.black12;
+const colorHighlight = Color(0xFF560000);
 
 
 class BrikSize extends InheritedWidget {
 class BrikSize extends InheritedWidget {
   const BrikSize({
   const BrikSize({
-    Key key,
-    @required this.size,
-    @required Widget child,
-  })  : assert(child != null),
-        super(key: key, child: child);
+    Key? key,
+    required this.size,
+    required Widget child,
+  }) : super(key: key, child: child);
 
 
   final Size size;
   final Size size;
 
 
   static BrikSize of(BuildContext context) {
   static BrikSize of(BuildContext context) {
-    final brikSize = context.dependOnInheritedWidgetOfExactType(aspect: BrikSize) as BrikSize;
+    final brikSize = context.dependOnInheritedWidgetOfExactType<BrikSize>();
     assert(brikSize != null, "....");
     assert(brikSize != null, "....");
-    return brikSize;
+    return brikSize!;
   }
   }
 
 
   @override
   @override
@@ -32,13 +29,13 @@ class BrikSize extends InheritedWidget {
 class Brik extends StatelessWidget {
 class Brik extends StatelessWidget {
   final Color color;
   final Color color;
 
 
-  const Brik._({Key key, this.color}) : super(key: key);
+  const Brik._({Key? key, required this.color}) : super(key: key);
 
 
-  const Brik.normal() : this._(color: _COLOR_NORMAL);
+  const Brik.normal() : this._(color: colorNormal);
 
 
-  const Brik.empty() : this._(color: _COLOR_NULL);
+  const Brik.empty() : this._(color: colorNull);
 
 
-  const Brik.highlight() : this._(color: _COLOR_HIGHLIGHT);
+  const Brik.highlight() : this._(color: colorHighlight);
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
@@ -48,8 +45,8 @@ class Brik extends StatelessWidget {
       child: Container(
       child: Container(
         margin: EdgeInsets.all(0.05 * width),
         margin: EdgeInsets.all(0.05 * width),
         padding: EdgeInsets.all(0.1 * width),
         padding: EdgeInsets.all(0.1 * width),
-        decoration:
-            BoxDecoration(border: Border.all(width: 0.10 * width, color: color)),
+        decoration: BoxDecoration(
+            border: Border.all(width: 0.10 * width, color: color)),
         child: Container(
         child: Container(
           color: color,
           color: color,
         ),
         ),

+ 34 - 33
lib/material/images.dart

@@ -1,11 +1,9 @@
 import 'dart:async';
 import 'dart:async';
 import 'dart:ui' as ui;
 import 'dart:ui' as ui;
-
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
-
 import 'material.dart';
 import 'material.dart';
 
 
-const _DIGITAL_ROW_SIZE = Size(14, 24);
+const digitalRowSize = Size(14, 24);
 
 
 class Number extends StatelessWidget {
 class Number extends StatelessWidget {
   final int length;
   final int length;
@@ -13,26 +11,25 @@ class Number extends StatelessWidget {
   ///the number to show
   ///the number to show
   ///could be null
   ///could be null
   final int number;
   final int number;
-
   final bool padWithZero;
   final bool padWithZero;
 
 
-  Number(
-      {Key key,
-      this.length = 5,
-      @required this.number,
-      this.padWithZero = false})
-      : super(key: key);
+  const Number({
+    Key? key,
+    this.length = 5,
+    required this.number,
+    this.padWithZero = false,
+  }) : super(key: key);
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
-    String digitalStr = number?.toString() ?? "";
+    String digitalStr = number.toString();
     if (digitalStr.length > length) {
     if (digitalStr.length > length) {
       digitalStr = digitalStr.substring(digitalStr.length - length);
       digitalStr = digitalStr.substring(digitalStr.length - length);
     }
     }
     digitalStr = digitalStr.padLeft(length, padWithZero ? "0" : " ");
     digitalStr = digitalStr.padLeft(length, padWithZero ? "0" : " ");
     List<Widget> children = [];
     List<Widget> children = [];
     for (int i = 0; i < length; i++) {
     for (int i = 0; i < length; i++) {
-      children.add(Digital(int.tryParse(digitalStr[i])));
+      children.add(Digital(int.tryParse(digitalStr[i]) ?? 0));
     }
     }
     return Row(
     return Row(
       mainAxisSize: MainAxisSize.min,
       mainAxisSize: MainAxisSize.min,
@@ -44,16 +41,14 @@ class Number extends StatelessWidget {
 class IconDragon extends StatefulWidget {
 class IconDragon extends StatefulWidget {
   final bool animate;
   final bool animate;
 
 
-  const IconDragon({Key key, this.animate = false}) : super(key: key);
+  const IconDragon({Key? key, this.animate = false}) : super(key: key);
 
 
   @override
   @override
-  _IconDragonState createState() {
-    return _IconDragonState();
-  }
+  State<IconDragon> createState() => _IconDragonState();
 }
 }
 
 
 class _IconDragonState extends State<IconDragon> {
 class _IconDragonState extends State<IconDragon> {
-  Timer _timer;
+  Timer? _timer;
 
 
   @override
   @override
   void didUpdateWidget(IconDragon oldWidget) {
   void didUpdateWidget(IconDragon oldWidget) {
@@ -118,7 +113,8 @@ class IconPause extends StatelessWidget {
   final bool enable;
   final bool enable;
   final Size size;
   final Size size;
 
 
-  const IconPause({Key key, this.enable = true, this.size = const Size(18, 16)})
+  const IconPause(
+      {Key? key, this.enable = true, this.size = const Size(18, 16)})
       : super(key: key);
       : super(key: key);
 
 
   @override
   @override
@@ -135,7 +131,8 @@ class IconSound extends StatelessWidget {
   final bool enable;
   final bool enable;
   final Size size;
   final Size size;
 
 
-  const IconSound({Key key, this.enable = true, this.size = const Size(18, 16)})
+  const IconSound(
+      {Key? key, this.enable = true, this.size = const Size(18, 16)})
       : super(key: key);
       : super(key: key);
 
 
   @override
   @override
@@ -153,7 +150,8 @@ class IconColon extends StatelessWidget {
 
 
   final Size size;
   final Size size;
 
 
-  const IconColon({Key key, this.enable = true, this.size = const Size(10, 17)})
+  const IconColon(
+      {Key? key, this.enable = true, this.size = const Size(10, 17)})
       : super(key: key);
       : super(key: key);
 
 
   @override
   @override
@@ -161,7 +159,7 @@ class IconColon extends StatelessWidget {
     return _Material(
     return _Material(
       size: size,
       size: size,
       srcOffset: enable ? const Offset(229, 25) : const Offset(243, 25),
       srcOffset: enable ? const Offset(229, 25) : const Offset(243, 25),
-      srcSize: _DIGITAL_ROW_SIZE,
+      srcSize: digitalRowSize,
     );
     );
   }
   }
 }
 }
@@ -174,8 +172,8 @@ class Digital extends StatelessWidget {
 
 
   final Size size;
   final Size size;
 
 
-  Digital(this.digital, {Key key, this.size = const Size(10, 17)})
-      : assert(digital == null || (digital <= 9 && digital >= 0)),
+  const Digital(this.digital, {Key? key, this.size = const Size(10, 17)})
+      : assert((digital <= 9 && digital >= 0)),
         super(key: key);
         super(key: key);
 
 
   @override
   @override
@@ -183,12 +181,12 @@ class Digital extends StatelessWidget {
     return _Material(
     return _Material(
       size: size,
       size: size,
       srcOffset: _getDigitalOffset(),
       srcOffset: _getDigitalOffset(),
-      srcSize: _DIGITAL_ROW_SIZE,
+      srcSize: digitalRowSize,
     );
     );
   }
   }
 
 
   Offset _getDigitalOffset() {
   Offset _getDigitalOffset() {
-    int offset = digital ?? 10;
+    int offset = digital;
     final dx = 75.0 + 14 * offset;
     final dx = 75.0 + 14 * offset;
     return Offset(dx, 25);
     return Offset(dx, 25);
   }
   }
@@ -202,18 +200,21 @@ class _Material extends StatelessWidget {
 
 
   final Offset srcOffset;
   final Offset srcOffset;
 
 
-  const _Material(
-      {Key key,
-      @required this.size,
-      @required this.srcSize,
-      @required this.srcOffset})
-      : super(key: key);
+  const _Material({
+    Key? key,
+    required this.size,
+    required this.srcSize,
+    required this.srcOffset,
+  }) : super(key: key);
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     return CustomPaint(
     return CustomPaint(
       foregroundPainter: _MaterialPainter(
       foregroundPainter: _MaterialPainter(
-          srcOffset, srcSize, GameMaterial.getMaterial(context)),
+        srcOffset,
+        srcSize,
+        GameMaterial.getMaterial(context),
+      ),
       child: SizedBox.fromSize(
       child: SizedBox.fromSize(
         size: size,
         size: size,
       ),
       ),
@@ -232,7 +233,7 @@ class _MaterialPainter extends CustomPainter {
 
 
   _MaterialPainter(this.offset, this.size, this.material);
   _MaterialPainter(this.offset, this.size, this.material);
 
 
-  Paint _paint = Paint();
+  final Paint _paint = Paint();
 
 
   @override
   @override
   void paint(Canvas canvas, Size size) {
   void paint(Canvas canvas, Size size) {

+ 8 - 7
lib/material/material.dart

@@ -1,27 +1,28 @@
 import 'dart:ui' as ui;
 import 'dart:ui' as ui;
-
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter/services.dart';
 
 
 class GameMaterial extends StatefulWidget {
 class GameMaterial extends StatefulWidget {
   final Widget child;
   final Widget child;
 
 
-  const GameMaterial({Key key, this.child}) : super(key: key);
+  const GameMaterial({
+    Key? key,
+    required this.child,
+  }) : super(key: key);
 
 
   @override
   @override
-  _GameMaterialState createState() => _GameMaterialState();
+  State<GameMaterial> createState() => _GameMaterialState();
 
 
   static ui.Image getMaterial(BuildContext context) {
   static ui.Image getMaterial(BuildContext context) {
-    final _GameMaterialState state =
-        context.findAncestorStateOfType<_GameMaterialState>();
+    final state = context.findAncestorStateOfType<_GameMaterialState>();
     assert(state != null, "can not find GameMaterial widget");
     assert(state != null, "can not find GameMaterial widget");
-    return state.material;
+    return state!.material!;
   }
   }
 }
 }
 
 
 class _GameMaterialState extends State<GameMaterial> {
 class _GameMaterialState extends State<GameMaterial> {
   ///the image data of /assets/material.png
   ///the image data of /assets/material.png
-  ui.Image material;
+  ui.Image? material;
 
 
   @override
   @override
   void initState() {
   void initState() {

+ 0 - 11
lib/model/config.dart

@@ -1,11 +0,0 @@
-
-import 'package:flutter/material.dart';
-
-final RouteObserver<ModalRoute> routeObserver = RouteObserver<ModalRoute>();
-//边框
-const SCREEN_BORDER_WIDTH = 3.0;
-const BACKGROUND_COLOR = const Color(0xffefcc19);
-
-class Config {
-
-}

+ 0 - 13
lib/pages/home_page.dart

@@ -1,13 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:tetris/panel/page_portrait.dart';
-
-class HomePage extends StatelessWidget {
-  const HomePage({Key key}) : super(key: key);
-
-  @override
-  Widget build(BuildContext context) {
-    //only Android/iOS support land mode
-    bool land = MediaQuery.of(context).orientation == Orientation.landscape;
-    return land ? PageLand() : PagePortrait();
-  }
-}

+ 56 - 47
lib/panel/controller.dart

@@ -3,12 +3,14 @@ import 'dart:math' as math;
 
 
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:tetris/gamer/gamer.dart';
 import 'package:tetris/gamer/gamer.dart';
-import 'package:tetris/generated/i18n.dart';
+import 'package:tetris/generated/l10n.dart';
 
 
 class GameController extends StatelessWidget {
 class GameController extends StatelessWidget {
+  const GameController({super.key});
+
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
-    return SizedBox(
+    return const SizedBox(
       height: 200,
       height: 200,
       child: Row(
       child: Row(
         children: <Widget>[
         children: <Widget>[
@@ -20,21 +22,23 @@ class GameController extends StatelessWidget {
   }
   }
 }
 }
 
 
-const Size _DIRECTION_BUTTON_SIZE = const Size(48, 48);
+const Size directionButtonSize = Size(48, 48);
 
 
-const Size _SYSTEM_BUTTON_SIZE = const Size(28, 28);
+const Size systemButtonSize = Size(28, 28);
 
 
-const double _DIRECTION_SPACE = 16;
+const double directionSpace = 16;
 
 
 const double _iconSize = 16;
 const double _iconSize = 16;
 
 
 class DirectionController extends StatelessWidget {
 class DirectionController extends StatelessWidget {
+  const DirectionController({super.key});
+
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     return Stack(
     return Stack(
       alignment: Alignment.center,
       alignment: Alignment.center,
       children: <Widget>[
       children: <Widget>[
-        SizedBox.fromSize(size: _DIRECTION_BUTTON_SIZE * 2.8),
+        SizedBox.fromSize(size: directionButtonSize * 2.8),
         Transform.rotate(
         Transform.rotate(
           angle: math.pi / 4,
           angle: math.pi / 4,
           child: Column(
           child: Column(
@@ -47,7 +51,7 @@ class DirectionController extends StatelessWidget {
                     scale: 1.5,
                     scale: 1.5,
                     child: Transform.rotate(
                     child: Transform.rotate(
                         angle: -math.pi / 4,
                         angle: -math.pi / 4,
-                        child: Icon(
+                        child: const Icon(
                           Icons.arrow_drop_up,
                           Icons.arrow_drop_up,
                           size: _iconSize,
                           size: _iconSize,
                         )),
                         )),
@@ -56,7 +60,7 @@ class DirectionController extends StatelessWidget {
                     scale: 1.5,
                     scale: 1.5,
                     child: Transform.rotate(
                     child: Transform.rotate(
                         angle: -math.pi / 4,
                         angle: -math.pi / 4,
-                        child: Icon(
+                        child: const Icon(
                           Icons.arrow_right,
                           Icons.arrow_right,
                           size: _iconSize,
                           size: _iconSize,
                         )),
                         )),
@@ -70,7 +74,7 @@ class DirectionController extends StatelessWidget {
                     scale: 1.5,
                     scale: 1.5,
                     child: Transform.rotate(
                     child: Transform.rotate(
                         angle: -math.pi / 4,
                         angle: -math.pi / 4,
-                        child: Icon(
+                        child: const Icon(
                           Icons.arrow_left,
                           Icons.arrow_left,
                           size: _iconSize,
                           size: _iconSize,
                         )),
                         )),
@@ -79,7 +83,7 @@ class DirectionController extends StatelessWidget {
                     scale: 1.5,
                     scale: 1.5,
                     child: Transform.rotate(
                     child: Transform.rotate(
                         angle: -math.pi / 4,
                         angle: -math.pi / 4,
-                        child: Icon(
+                        child: const Icon(
                           Icons.arrow_drop_down,
                           Icons.arrow_drop_down,
                           size: _iconSize,
                           size: _iconSize,
                         )),
                         )),
@@ -94,43 +98,43 @@ class DirectionController extends StatelessWidget {
           child: Column(
           child: Column(
             mainAxisSize: MainAxisSize.min,
             mainAxisSize: MainAxisSize.min,
             children: <Widget>[
             children: <Widget>[
-              SizedBox(height: _DIRECTION_SPACE),
+              const SizedBox(height: directionSpace),
               Row(
               Row(
                 mainAxisSize: MainAxisSize.min,
                 mainAxisSize: MainAxisSize.min,
                 children: <Widget>[
                 children: <Widget>[
                   _Button(
                   _Button(
                       enableLongPress: false,
                       enableLongPress: false,
-                      size: _DIRECTION_BUTTON_SIZE,
+                      size: directionButtonSize,
                       onTap: () {
                       onTap: () {
                         Game.of(context).rotate();
                         Game.of(context).rotate();
                       }),
                       }),
-                  SizedBox(width: _DIRECTION_SPACE),
+                  const SizedBox(width: directionSpace),
                   _Button(
                   _Button(
-                      size: _DIRECTION_BUTTON_SIZE,
+                      size: directionButtonSize,
                       onTap: () {
                       onTap: () {
                         Game.of(context).right();
                         Game.of(context).right();
                       }),
                       }),
                 ],
                 ],
               ),
               ),
-              SizedBox(height: _DIRECTION_SPACE),
+              const SizedBox(height: directionSpace),
               Row(
               Row(
                 mainAxisSize: MainAxisSize.min,
                 mainAxisSize: MainAxisSize.min,
                 children: <Widget>[
                 children: <Widget>[
                   _Button(
                   _Button(
-                      size: _DIRECTION_BUTTON_SIZE,
+                      size: directionButtonSize,
                       onTap: () {
                       onTap: () {
                         Game.of(context).left();
                         Game.of(context).left();
                       }),
                       }),
-                  SizedBox(width: _DIRECTION_SPACE),
+                  const SizedBox(width: directionSpace),
                   _Button(
                   _Button(
-                    size: _DIRECTION_BUTTON_SIZE,
+                    size: directionButtonSize,
                     onTap: () {
                     onTap: () {
                       Game.of(context).down();
                       Game.of(context).down();
                     },
                     },
                   ),
                   ),
                 ],
                 ],
               ),
               ),
-              SizedBox(height: _DIRECTION_SPACE),
+              const SizedBox(height: directionSpace),
             ],
             ],
           ),
           ),
         ),
         ),
@@ -140,7 +144,9 @@ class DirectionController extends StatelessWidget {
 }
 }
 
 
 class SystemButtonGroup extends StatelessWidget {
 class SystemButtonGroup extends StatelessWidget {
-  static const _systemButtonColor = const Color(0xFF2dc421);
+  static const _systemButtonColor = Color(0xFF2dc421);
+
+  const SystemButtonGroup({super.key});
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
@@ -150,7 +156,7 @@ class SystemButtonGroup extends StatelessWidget {
         _Description(
         _Description(
           text: S.of(context).sounds,
           text: S.of(context).sounds,
           child: _Button(
           child: _Button(
-              size: _SYSTEM_BUTTON_SIZE,
+              size: systemButtonSize,
               color: _systemButtonColor,
               color: _systemButtonColor,
               enableLongPress: false,
               enableLongPress: false,
               onTap: () {
               onTap: () {
@@ -160,7 +166,7 @@ class SystemButtonGroup extends StatelessWidget {
         _Description(
         _Description(
           text: S.of(context).pause_resume,
           text: S.of(context).pause_resume,
           child: _Button(
           child: _Button(
-              size: _SYSTEM_BUTTON_SIZE,
+              size: systemButtonSize,
               color: _systemButtonColor,
               color: _systemButtonColor,
               enableLongPress: false,
               enableLongPress: false,
               onTap: () {
               onTap: () {
@@ -170,7 +176,7 @@ class SystemButtonGroup extends StatelessWidget {
         _Description(
         _Description(
           text: S.of(context).reset,
           text: S.of(context).reset,
           child: _Button(
           child: _Button(
-              size: _SYSTEM_BUTTON_SIZE,
+              size: systemButtonSize,
               enableLongPress: false,
               enableLongPress: false,
               color: Colors.red,
               color: Colors.red,
               onTap: () {
               onTap: () {
@@ -183,13 +189,15 @@ class SystemButtonGroup extends StatelessWidget {
 }
 }
 
 
 class DropButton extends StatelessWidget {
 class DropButton extends StatelessWidget {
+  const DropButton({super.key});
+
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     return _Description(
     return _Description(
       text: 'drop',
       text: 'drop',
       child: _Button(
       child: _Button(
           enableLongPress: false,
           enableLongPress: false,
-          size: Size(90, 90),
+          size: const Size(90, 90),
           onTap: () {
           onTap: () {
             Game.of(context).drop();
             Game.of(context).drop();
           }),
           }),
@@ -198,9 +206,11 @@ class DropButton extends StatelessWidget {
 }
 }
 
 
 class LeftController extends StatelessWidget {
 class LeftController extends StatelessWidget {
+  const LeftController({super.key});
+
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
-    return Column(
+    return const Column(
       mainAxisSize: MainAxisSize.min,
       mainAxisSize: MainAxisSize.min,
       children: <Widget>[
       children: <Widget>[
         SystemButtonGroup(),
         SystemButtonGroup(),
@@ -216,7 +226,7 @@ class LeftController extends StatelessWidget {
 
 
 class _Button extends StatefulWidget {
 class _Button extends StatefulWidget {
   final Size size;
   final Size size;
-  final Widget icon;
+  final Widget? icon;
 
 
   final VoidCallback onTap;
   final VoidCallback onTap;
 
 
@@ -225,14 +235,14 @@ class _Button extends StatefulWidget {
 
 
   final bool enableLongPress;
   final bool enableLongPress;
 
 
-  const _Button(
-      {Key key,
-      @required this.size,
-      @required this.onTap,
-      this.icon,
-      this.color = Colors.blue,
-      this.enableLongPress = true})
-      : super(key: key);
+  const _Button({
+    Key? key,
+    required this.size,
+    required this.onTap,
+    this.icon,
+    this.color = Colors.blue,
+    this.enableLongPress = true,
+  }) : super(key: key);
 
 
   @override
   @override
   _ButtonState createState() {
   _ButtonState createState() {
@@ -249,12 +259,11 @@ class _Description extends StatelessWidget {
   final AxisDirection direction;
   final AxisDirection direction;
 
 
   const _Description({
   const _Description({
-    Key key,
-    this.text,
+    Key? key,
+    required this.text,
     this.direction = AxisDirection.down,
     this.direction = AxisDirection.down,
-    this.child,
-  })  : assert(direction != null),
-        super(key: key);
+    required this.child,
+  }) : super(key: key);
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
@@ -263,40 +272,40 @@ class _Description extends StatelessWidget {
       case AxisDirection.right:
       case AxisDirection.right:
         widget = Row(
         widget = Row(
             mainAxisSize: MainAxisSize.min,
             mainAxisSize: MainAxisSize.min,
-            children: <Widget>[child, SizedBox(width: 8), Text(text)]);
+            children: <Widget>[child, const SizedBox(width: 8), Text(text)]);
         break;
         break;
       case AxisDirection.left:
       case AxisDirection.left:
         widget = Row(
         widget = Row(
-          children: <Widget>[Text(text), SizedBox(width: 8), child],
           mainAxisSize: MainAxisSize.min,
           mainAxisSize: MainAxisSize.min,
+          children: <Widget>[Text(text), const SizedBox(width: 8), child],
         );
         );
         break;
         break;
       case AxisDirection.up:
       case AxisDirection.up:
         widget = Column(
         widget = Column(
-          children: <Widget>[Text(text), SizedBox(height: 8), child],
           mainAxisSize: MainAxisSize.min,
           mainAxisSize: MainAxisSize.min,
+          children: <Widget>[Text(text), const SizedBox(height: 8), child],
         );
         );
         break;
         break;
       case AxisDirection.down:
       case AxisDirection.down:
         widget = Column(
         widget = Column(
-          children: <Widget>[child, SizedBox(height: 8), Text(text)],
           mainAxisSize: MainAxisSize.min,
           mainAxisSize: MainAxisSize.min,
+          children: <Widget>[child, const SizedBox(height: 8), Text(text)],
         );
         );
         break;
         break;
     }
     }
     return DefaultTextStyle(
     return DefaultTextStyle(
+      style: const TextStyle(fontSize: 12, color: Colors.black),
       child: widget,
       child: widget,
-      style: TextStyle(fontSize: 12, color: Colors.black),
     );
     );
   }
   }
 }
 }
 
 
 class _ButtonState extends State<_Button> {
 class _ButtonState extends State<_Button> {
-  Timer _timer;
+  Timer? _timer;
 
 
   bool _tapEnded = false;
   bool _tapEnded = false;
 
 
-  Color _color;
+  late Color _color;
 
 
   @override
   @override
   void didUpdateWidget(_Button oldWidget) {
   void didUpdateWidget(_Button oldWidget) {
@@ -315,7 +324,7 @@ class _ButtonState extends State<_Button> {
     return Material(
     return Material(
       color: _color,
       color: _color,
       elevation: 2,
       elevation: 2,
-      shape: CircleBorder(),
+      shape: const CircleBorder(),
       child: GestureDetector(
       child: GestureDetector(
         behavior: HitTestBehavior.opaque,
         behavior: HitTestBehavior.opaque,
         onTapDown: (_) async {
         onTapDown: (_) async {

+ 6 - 16
lib/panel/page_land.dart

@@ -1,19 +1,21 @@
 part of 'page_portrait.dart';
 part of 'page_portrait.dart';
 
 
 class PageLand extends StatelessWidget {
 class PageLand extends StatelessWidget {
+  const PageLand({super.key});
+
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     var height = MediaQuery.of(context).size.height;
     var height = MediaQuery.of(context).size.height;
     height -= MediaQuery.of(context).viewInsets.vertical;
     height -= MediaQuery.of(context).viewInsets.vertical;
     return SizedBox.expand(
     return SizedBox.expand(
       child: Container(
       child: Container(
-        color: BACKGROUND_COLOR,
+        color: backgroundColor,
         child: Padding(
         child: Padding(
           padding: MediaQuery.of(context).padding,
           padding: MediaQuery.of(context).padding,
           child: Row(
           child: Row(
             mainAxisSize: MainAxisSize.min,
             mainAxisSize: MainAxisSize.min,
             children: <Widget>[
             children: <Widget>[
-              Expanded(
+              const Expanded(
                 child: Column(
                 child: Column(
                   crossAxisAlignment: CrossAxisAlignment.start,
                   crossAxisAlignment: CrossAxisAlignment.start,
                   children: <Widget>[
                   children: <Widget>[
@@ -21,28 +23,16 @@ class PageLand extends StatelessWidget {
                     SystemButtonGroup(),
                     SystemButtonGroup(),
                     Spacer(),
                     Spacer(),
                     Padding(
                     Padding(
-                      padding: const EdgeInsets.only(left: 40, bottom: 40),
+                      padding: EdgeInsets.only(left: 40, bottom: 40),
                       child: DropButton(),
                       child: DropButton(),
                     )
                     )
                   ],
                   ],
                 ),
                 ),
               ),
               ),
               _ScreenDecoration(child: Screen.fromHeight(height * 0.8)),
               _ScreenDecoration(child: Screen.fromHeight(height * 0.8)),
-              Expanded(
+              const Expanded(
                 child: Column(
                 child: Column(
                   children: <Widget>[
                   children: <Widget>[
-                    Row(
-                      children: <Widget>[
-                        Spacer(),
-                        TextButton(
-                            onPressed: () {
-                              showDialog(
-                                  context: context,
-                                  builder: (context) => DonationDialog());
-                            },
-                            child: Text(S.of(context).reward))
-                      ],
-                    ),
                     Spacer(),
                     Spacer(),
                     DirectionController(),
                     DirectionController(),
                     SizedBox(height: 30),
                     SizedBox(height: 30),

+ 18 - 30
lib/panel/page_portrait.dart

@@ -1,13 +1,13 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
-import 'package:tetris/generated/i18n.dart';
-import 'package:tetris/income/donation_dialog.dart';
-import 'package:tetris/model/config.dart';
+import 'package:tetris/main.dart';
 import 'package:tetris/panel/controller.dart';
 import 'package:tetris/panel/controller.dart';
 import 'package:tetris/panel/screen.dart';
 import 'package:tetris/panel/screen.dart';
 
 
 part 'page_land.dart';
 part 'page_land.dart';
 
 
 class PagePortrait extends StatelessWidget {
 class PagePortrait extends StatelessWidget {
+  const PagePortrait({super.key});
+
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     final size = MediaQuery.of(context).size;
     final size = MediaQuery.of(context).size;
@@ -15,27 +15,15 @@ class PagePortrait extends StatelessWidget {
 
 
     return SizedBox.expand(
     return SizedBox.expand(
       child: Container(
       child: Container(
-        color: BACKGROUND_COLOR,
+        color: backgroundColor,
         child: Padding(
         child: Padding(
           padding: MediaQuery.of(context).padding,
           padding: MediaQuery.of(context).padding,
           child: Column(
           child: Column(
             children: <Widget>[
             children: <Widget>[
-              Row(
-                children: <Widget>[
-                  Spacer(),
-                  TextButton(
-                      onPressed: () {
-                        showDialog(
-                            context: context,
-                            builder: (context) => DonationDialog());
-                      },
-                      child: Text(S.of(context).reward))
-                ],
-              ),
-              Spacer(),
+              const Spacer(),
               _ScreenDecoration(child: Screen(width: screenW)),
               _ScreenDecoration(child: Screen(width: screenW)),
-              Spacer(flex: 2),
-              GameController(),
+              const Spacer(flex: 2),
+              const GameController(),
             ],
             ],
           ),
           ),
         ),
         ),
@@ -47,28 +35,28 @@ class PagePortrait extends StatelessWidget {
 class _ScreenDecoration extends StatelessWidget {
 class _ScreenDecoration extends StatelessWidget {
   final Widget child;
   final Widget child;
 
 
-  const _ScreenDecoration({Key key, @required this.child}) : super(key: key);
+  const _ScreenDecoration({
+    Key? key,
+    required this.child,
+  }) : super(key: key);
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     return Container(
     return Container(
-      decoration: BoxDecoration(
+      decoration: const BoxDecoration(
         border: Border(
         border: Border(
-          top: BorderSide(
-              color: const Color(0xFF987f0f), width: SCREEN_BORDER_WIDTH),
-          left: BorderSide(
-              color: const Color(0xFF987f0f), width: SCREEN_BORDER_WIDTH),
-          right: BorderSide(
-              color: const Color(0xFFfae36c), width: SCREEN_BORDER_WIDTH),
-          bottom: BorderSide(
-              color: const Color(0xFFfae36c), width: SCREEN_BORDER_WIDTH),
+          top: BorderSide(color: Color(0xFF987f0f), width: screenBorderWidth),
+          left: BorderSide(color: Color(0xFF987f0f), width: screenBorderWidth),
+          right: BorderSide(color: Color(0xFFfae36c), width: screenBorderWidth),
+          bottom:
+              BorderSide(color: Color(0xFFfae36c), width: screenBorderWidth),
         ),
         ),
       ),
       ),
       child: Container(
       child: Container(
         decoration: BoxDecoration(border: Border.all(color: Colors.black54)),
         decoration: BoxDecoration(border: Border.all(color: Colors.black54)),
         child: Container(
         child: Container(
           padding: const EdgeInsets.all(3),
           padding: const EdgeInsets.all(3),
-          color: SCREEN_BACKGROUND,
+          color: screenBackground,
           child: child,
           child: child,
         ),
         ),
       ),
       ),

+ 11 - 7
lib/panel/player_panel.dart

@@ -3,10 +3,10 @@ import 'package:tetris/material/briks.dart';
 import 'package:tetris/material/images.dart';
 import 'package:tetris/material/images.dart';
 import 'package:tetris/gamer/gamer.dart';
 import 'package:tetris/gamer/gamer.dart';
 
 
-const _PLAYER_PANEL_PADDING = 6;
+const playerPanelPadding = 6;
 
 
 Size getBrikSizeForScreenWidth(double width) {
 Size getBrikSizeForScreenWidth(double width) {
-  return Size.square((width - _PLAYER_PANEL_PADDING) / GAME_PAD_MATRIX_W);
+  return Size.square((width - playerPanelPadding) / gamePadMatrixW);
 }
 }
 
 
 ///the matrix of player content
 ///the matrix of player content
@@ -14,8 +14,10 @@ class PlayerPanel extends StatelessWidget {
   //the size of player panel
   //the size of player panel
   final Size size;
   final Size size;
 
 
-  PlayerPanel({Key key, @required double width})
-      : assert(width != null && width != 0),
+  PlayerPanel({
+    Key? key,
+    required double width,
+  })  : assert(width != 0),
         size = Size(width, width * 2),
         size = Size(width, width * 2),
         super(key: key);
         super(key: key);
 
 
@@ -25,7 +27,7 @@ class PlayerPanel extends StatelessWidget {
     return SizedBox.fromSize(
     return SizedBox.fromSize(
       size: size,
       size: size,
       child: Container(
       child: Container(
-        padding: EdgeInsets.all(2),
+        padding: const EdgeInsets.all(2),
         decoration: BoxDecoration(
         decoration: BoxDecoration(
           border: Border.all(color: Colors.black),
           border: Border.all(color: Colors.black),
         ),
         ),
@@ -49,7 +51,9 @@ class _PlayerPad extends StatelessWidget {
           children: list.map((b) {
           children: list.map((b) {
             return b == 1
             return b == 1
                 ? const Brik.normal()
                 ? const Brik.normal()
-                : b == 2 ? const Brik.highlight() : const Brik.empty();
+                : b == 2
+                    ? const Brik.highlight()
+                    : const Brik.empty();
           }).toList(),
           }).toList(),
         );
         );
       }).toList(),
       }).toList(),
@@ -61,7 +65,7 @@ class _GameUninitialized extends StatelessWidget {
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     if (GameState.of(context).states == GameStates.none) {
     if (GameState.of(context).states == GameStates.none) {
-      return Center(
+      return const Center(
         child: Column(
         child: Column(
           mainAxisSize: MainAxisSize.min,
           mainAxisSize: MainAxisSize.min,
           children: <Widget>[
           children: <Widget>[

+ 21 - 14
lib/panel/screen.dart

@@ -9,16 +9,20 @@ import 'package:vector_math/vector_math_64.dart' as v;
 import 'player_panel.dart';
 import 'player_panel.dart';
 import 'status_panel.dart';
 import 'status_panel.dart';
 
 
-const Color SCREEN_BACKGROUND = Color(0xff9ead86);
+const Color screenBackground = Color(0xff9ead86);
 
 
 /// screen H : W;
 /// screen H : W;
 class Screen extends StatelessWidget {
 class Screen extends StatelessWidget {
   ///the with of screen
   ///the with of screen
   final double width;
   final double width;
 
 
-  const Screen({Key key, @required this.width}) : super(key: key);
+  const Screen({
+    Key? key,
+    required this.width,
+  }) : super(key: key);
 
 
-  Screen.fromHeight(double height) : this(width: ((height - 6) / 2 + 6) / 0.6);
+  const Screen.fromHeight(double height)
+      : this(width: ((height - 6) / 2 + 6) / 0.6);
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
@@ -30,7 +34,7 @@ class Screen extends StatelessWidget {
         height: (playerPanelWidth - 6) * 2 + 6,
         height: (playerPanelWidth - 6) * 2 + 6,
         width: width,
         width: width,
         child: Container(
         child: Container(
-          color: SCREEN_BACKGROUND,
+          color: screenBackground,
           child: GameMaterial(
           child: GameMaterial(
             child: BrikSize(
             child: BrikSize(
               size: getBrikSizeForScreenWidth(playerPanelWidth),
               size: getBrikSizeForScreenWidth(playerPanelWidth),
@@ -39,7 +43,7 @@ class Screen extends StatelessWidget {
                   PlayerPanel(width: playerPanelWidth),
                   PlayerPanel(width: playerPanelWidth),
                   SizedBox(
                   SizedBox(
                     width: width - playerPanelWidth,
                     width: width - playerPanelWidth,
-                    child: StatusPanel(),
+                    child: const StatusPanel(),
                   )
                   )
                 ],
                 ],
               ),
               ),
@@ -57,24 +61,27 @@ class Shake extends StatefulWidget {
   ///true to shake screen vertically
   ///true to shake screen vertically
   final bool shake;
   final bool shake;
 
 
-  const Shake({Key key, @required this.child, @required this.shake})
-      : super(key: key);
+  const Shake({
+    Key? key,
+    required this.child,
+    required this.shake,
+  }) : super(key: key);
 
 
   @override
   @override
-  _ShakeState createState() => _ShakeState();
+  State<Shake> createState() => _ShakeState();
 }
 }
 
 
 ///摇晃屏幕
 ///摇晃屏幕
 class _ShakeState extends State<Shake> with TickerProviderStateMixin {
 class _ShakeState extends State<Shake> with TickerProviderStateMixin {
-  AnimationController _controller;
+  late AnimationController _controller;
 
 
   @override
   @override
   void initState() {
   void initState() {
-    _controller =
-        AnimationController(vsync: this, duration: Duration(milliseconds: 150))
-          ..addListener(() {
-            setState(() {});
-          });
+    _controller = AnimationController(
+        vsync: this, duration: const Duration(milliseconds: 150))
+      ..addListener(() {
+        setState(() {});
+      });
     super.initState();
     super.initState();
   }
   }
 
 

+ 21 - 22
lib/panel/status_panel.dart

@@ -3,11 +3,13 @@ import 'dart:async';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:tetris/gamer/block.dart';
 import 'package:tetris/gamer/block.dart';
 import 'package:tetris/gamer/gamer.dart';
 import 'package:tetris/gamer/gamer.dart';
-import 'package:tetris/generated/i18n.dart';
+import 'package:tetris/generated/l10n.dart';
 import 'package:tetris/material/briks.dart';
 import 'package:tetris/material/briks.dart';
 import 'package:tetris/material/images.dart';
 import 'package:tetris/material/images.dart';
 
 
 class StatusPanel extends StatelessWidget {
 class StatusPanel extends StatelessWidget {
+  const StatusPanel({super.key});
+
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     return Container(
     return Container(
@@ -16,25 +18,25 @@ class StatusPanel extends StatelessWidget {
         crossAxisAlignment: CrossAxisAlignment.start,
         crossAxisAlignment: CrossAxisAlignment.start,
         children: <Widget>[
         children: <Widget>[
           Text(S.of(context).points,
           Text(S.of(context).points,
-              style: TextStyle(fontWeight: FontWeight.bold)),
-          SizedBox(height: 4),
+              style: const TextStyle(fontWeight: FontWeight.bold)),
+          const SizedBox(height: 4),
           Number(number: GameState.of(context).points),
           Number(number: GameState.of(context).points),
-          SizedBox(height: 10),
+          const SizedBox(height: 10),
           Text(S.of(context).cleans,
           Text(S.of(context).cleans,
-              style: TextStyle(fontWeight: FontWeight.bold)),
-          SizedBox(height: 4),
+              style: const TextStyle(fontWeight: FontWeight.bold)),
+          const SizedBox(height: 4),
           Number(number: GameState.of(context).cleared),
           Number(number: GameState.of(context).cleared),
-          SizedBox(height: 10),
+          const SizedBox(height: 10),
           Text(S.of(context).level,
           Text(S.of(context).level,
-              style: TextStyle(fontWeight: FontWeight.bold)),
-          SizedBox(height: 4),
+              style: const TextStyle(fontWeight: FontWeight.bold)),
+          const SizedBox(height: 4),
           Number(number: GameState.of(context).level),
           Number(number: GameState.of(context).level),
-          SizedBox(height: 10),
+          const SizedBox(height: 10),
           Text(S.of(context).next,
           Text(S.of(context).next,
-              style: TextStyle(fontWeight: FontWeight.bold)),
-          SizedBox(height: 4),
+              style: const TextStyle(fontWeight: FontWeight.bold)),
+          const SizedBox(height: 4),
           _NextBlock(),
           _NextBlock(),
-          Spacer(),
+          const Spacer(),
           _GameStatus(),
           _GameStatus(),
         ],
         ],
       ),
       ),
@@ -46,7 +48,7 @@ class _NextBlock extends StatelessWidget {
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     List<List<int>> data = [List.filled(4, 0), List.filled(4, 0)];
     List<List<int>> data = [List.filled(4, 0), List.filled(4, 0)];
-    final next = BLOCK_SHAPES[GameState.of(context).next.type];
+    final next = blockShapes[GameState.of(context).next.type]!;
     for (int i = 0; i < next.length; i++) {
     for (int i = 0; i < next.length; i++) {
       for (int j = 0; j < next[i].length; j++) {
       for (int j = 0; j < next[i].length; j++) {
         data[i][j] = next[i][j];
         data[i][j] = next[i][j];
@@ -72,13 +74,10 @@ class _GameStatus extends StatefulWidget {
 }
 }
 
 
 class _GameStatusState extends State<_GameStatus> {
 class _GameStatusState extends State<_GameStatus> {
-  Timer _timer;
-
+  Timer? _timer;
   bool _colonEnable = true;
   bool _colonEnable = true;
-
-  int _minute;
-
-  int _hour;
+  int _minute = 0;
+  int _hour = 0;
 
 
   @override
   @override
   void initState() {
   void initState() {
@@ -104,9 +103,9 @@ class _GameStatusState extends State<_GameStatus> {
     return Row(
     return Row(
       children: <Widget>[
       children: <Widget>[
         IconSound(enable: GameState.of(context).muted),
         IconSound(enable: GameState.of(context).muted),
-        SizedBox(width: 4),
+        const SizedBox(width: 4),
         IconPause(enable: GameState.of(context).states == GameStates.paused),
         IconPause(enable: GameState.of(context).states == GameStates.paused),
-        Spacer(),
+        const Spacer(),
         Number(number: _hour, length: 2, padWithZero: true),
         Number(number: _hour, length: 2, padWithZero: true),
         IconColon(enable: _colonEnable),
         IconColon(enable: _colonEnable),
         Number(number: _minute, length: 2, padWithZero: true),
         Number(number: _minute, length: 2, padWithZero: true),

+ 0 - 18
lib/utils/app_util.dart

@@ -1,18 +0,0 @@
-import 'package:flutter/foundation.dart';
-
-class AppUtil {
-
-  // 关闭打印
-  static void disableDebugPrint() {
-    bool debug = false;
-    assert(() {
-      debug = true;
-      return true;
-    }());
-    if (!debug) {
-      debugPrint = (String message, {int wrapWidth}) {
-        //disable log print when not in debug mode
-      };
-    }
-  }
-}

+ 271 - 0
pubspec.lock

@@ -0,0 +1,271 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+  async:
+    dependency: transitive
+    description:
+      name: async
+      sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.11.0"
+  boolean_selector:
+    dependency: transitive
+    description:
+      name: boolean_selector
+      sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.1.1"
+  characters:
+    dependency: transitive
+    description:
+      name: characters
+      sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.3.0"
+  clock:
+    dependency: transitive
+    description:
+      name: clock
+      sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.1.1"
+  collection:
+    dependency: transitive
+    description:
+      name: collection
+      sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.18.0"
+  fake_async:
+    dependency: transitive
+    description:
+      name: fake_async
+      sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.3.1"
+  flutter:
+    dependency: "direct main"
+    description: flutter
+    source: sdk
+    version: "0.0.0"
+  flutter_lints:
+    dependency: "direct dev"
+    description:
+      name: flutter_lints
+      sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.0.3"
+  flutter_localizations:
+    dependency: "direct main"
+    description: flutter
+    source: sdk
+    version: "0.0.0"
+  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"
+  http:
+    dependency: transitive
+    description:
+      name: http
+      sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.1.2"
+  http_parser:
+    dependency: transitive
+    description:
+      name: http_parser
+      sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "4.0.2"
+  intl:
+    dependency: transitive
+    description:
+      name: intl
+      sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "0.18.1"
+  lints:
+    dependency: transitive
+    description:
+      name: lints
+      sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.1.1"
+  matcher:
+    dependency: transitive
+    description:
+      name: matcher
+      sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "0.12.16"
+  material_color_utilities:
+    dependency: transitive
+    description:
+      name: material_color_utilities
+      sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "0.5.0"
+  meta:
+    dependency: transitive
+    description:
+      name: meta
+      sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.10.0"
+  overlay_support:
+    dependency: "direct main"
+    description:
+      name: overlay_support
+      sha256: fc39389bfd94e6985e1e13b2a88a125fc4027608485d2d4e2847afe1b2bb339c
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.1.0"
+  path:
+    dependency: transitive
+    description:
+      name: path
+      sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.8.3"
+  plugin_platform_interface:
+    dependency: transitive
+    description:
+      name: plugin_platform_interface
+      sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.1.7"
+  sky_engine:
+    dependency: transitive
+    description: flutter
+    source: sdk
+    version: "0.0.99"
+  soundpool:
+    dependency: "direct main"
+    description:
+      name: soundpool
+      sha256: fe7302005759d6a3561de1711e3ea818b1ba025a62375b469196dda5b654bd38
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.4.1"
+  soundpool_macos:
+    dependency: transitive
+    description:
+      name: soundpool_macos
+      sha256: e0440a19d4e8f344dace336923b369184e91eebbbd8348266f4434b675bd15db
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.3.0"
+  soundpool_platform_interface:
+    dependency: transitive
+    description:
+      name: soundpool_platform_interface
+      sha256: "7c6666e19319151b2036c4fc9b6da3a83f2ebf4097989e6ba1c2b0bfe3612e9f"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.2.0"
+  soundpool_web:
+    dependency: transitive
+    description:
+      name: soundpool_web
+      sha256: "3d1eb8d6cceb8a0aec38ff9aec4fbd11a9a8101d27b27a6eb29305b83d46aee5"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.3.0"
+  source_span:
+    dependency: transitive
+    description:
+      name: source_span
+      sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.10.0"
+  stack_trace:
+    dependency: transitive
+    description:
+      name: stack_trace
+      sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.11.1"
+  stream_channel:
+    dependency: transitive
+    description:
+      name: stream_channel
+      sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.1.2"
+  string_scanner:
+    dependency: transitive
+    description:
+      name: string_scanner
+      sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.2.0"
+  term_glyph:
+    dependency: transitive
+    description:
+      name: term_glyph
+      sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.2.1"
+  test_api:
+    dependency: transitive
+    description:
+      name: test_api
+      sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "0.6.1"
+  typed_data:
+    dependency: transitive
+    description:
+      name: typed_data
+      sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.3.2"
+  vector_math:
+    dependency: transitive
+    description:
+      name: vector_math
+      sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "2.1.4"
+  web:
+    dependency: transitive
+    description:
+      name: web
+      sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "0.3.0"
+sdks:
+  dart: ">=3.2.0 <4.0.0"
+  flutter: ">=3.10.0"

+ 9 - 7
pubspec.yaml

@@ -1,26 +1,28 @@
 name: tetris
 name: tetris
 description: A new Flutter project.
 description: A new Flutter project.
-version: 1.2.1+1
+publish_to: "none"
+version: 2.0.0+1
+
 environment:
 environment:
-  sdk: '>=2.7.0 <3.0.0'
+  sdk: ">=3.1.3 <4.0.0"
 
 
 dependencies:
 dependencies:
   flutter:
   flutter:
     sdk: flutter
     sdk: flutter
   flutter_localizations:
   flutter_localizations:
     sdk: flutter
     sdk: flutter
-
-  overlay_support: ^1.2.1
-  soundpool: ^2.3.0
+  overlay_support: ^2.1.0
+  soundpool: ^2.4.1
 
 
 dev_dependencies:
 dev_dependencies:
   flutter_test:
   flutter_test:
     sdk: flutter
     sdk: flutter
+  flutter_lints: ^2.0.0
 
 
 flutter:
 flutter:
   uses-material-design: true
   uses-material-design: true
   assets:
   assets:
     - assets/material.png
     - assets/material.png
     - assets/audios/
     - assets/audios/
-#    - assets/alipay.jpg
-#    - assets/wechat.png
+flutter_intl:
+  enabled: true