Browse Source

内置图增加到20张;增加预加载缓存20张;增加缓存清理

guoziyun 6 months ago
parent
commit
45c29760ff
84 changed files with 513 additions and 103 deletions
  1. 1 0
      .gitignore
  2. 6 0
      README.md
  3. 23 4
      android/app/build.gradle.kts
  4. BIN
      android/app/src/main/res/mipmap-hdpi/launcher_icon.png
  5. BIN
      android/app/src/main/res/mipmap-mdpi/launcher_icon.png
  6. BIN
      android/app/src/main/res/mipmap-xhdpi/launcher_icon.png
  7. BIN
      android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png
  8. BIN
      android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png
  9. BIN
      assets/builtin/5.jpeg
  10. BIN
      assets/builtin/691550964b99f02d1db7f6ed.jpeg
  11. BIN
      assets/builtin/691576da4b99f02d1db80e9c.jpeg
  12. BIN
      assets/builtin/691577834b99f02d1db811b3.jpeg
  13. BIN
      assets/builtin/691578314b99f02d1db81309.jpeg
  14. BIN
      assets/builtin/69157cd64b99f02d1db819a5.jpeg
  15. BIN
      assets/builtin/69157ea24b99f02d1db81e14.jpeg
  16. BIN
      assets/builtin/691580d84b99f02d1db821b3.jpeg
  17. BIN
      assets/builtin/691584ae4b99f02d1db82a1b.jpeg
  18. 0 0
      assets/builtin/691585964b99f02d1db82b44.jpeg
  19. BIN
      assets/builtin/6915869d4b99f02d1db82cf2.jpeg
  20. BIN
      assets/builtin/69158aab4b99f02d1db83576.jpeg
  21. BIN
      assets/builtin/69174ac74b99f02d1db91783.jpeg
  22. BIN
      assets/builtin/691b4c0d9b45d37f47dc668d.jpeg
  23. BIN
      assets/builtin/691bd6709b45d37f47dc7ad5.jpeg
  24. BIN
      assets/builtin/691bd8589b45d37f47dc7c21.jpeg
  25. BIN
      assets/builtin/691d397a9b45d37f47dcf7b8.jpeg
  26. BIN
      assets/builtin/691d3a809b45d37f47dcf8ca.jpeg
  27. BIN
      assets/builtin/691eea1f94c0827052e24617.jpeg
  28. BIN
      assets/builtin/691eebd194c0827052e24a13.jpeg
  29. BIN
      assets/builtin/692531f83c9826728f73cb73.jpeg
  30. BIN
      assets/builtin/6926966dede1f42c42e39a69.jpeg
  31. BIN
      assets/builtin/6929d6b7a4a70f6c6369cb5c.jpeg
  32. 19 1
      assets/builtin/collection.json
  33. 162 6
      assets/builtin/latest.json
  34. BIN
      assets/icons/icon.png
  35. BIN
      assets/icons/icon_round.png
  36. 1 0
      assets/images/title.svg
  37. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
  38. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
  39. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
  40. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
  41. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
  42. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
  43. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
  44. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
  45. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
  46. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
  47. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png
  48. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png
  49. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png
  50. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png
  51. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
  52. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
  53. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png
  54. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png
  55. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
  56. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
  57. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
  58. 2 5
      lib/config/config.dart
  59. 5 6
      lib/homepage/home_board.dart
  60. 46 46
      lib/homepage/home_board_play.dart
  61. 129 12
      lib/homepage/home_screen.dart
  62. 4 7
      lib/main.dart
  63. 1 1
      lib/models/api_helper.dart
  64. 12 1
      lib/models/cached_request.dart
  65. 36 0
      lib/models/data.dart
  66. 14 2
      lib/models/download.dart
  67. 2 4
      lib/play/board.dart
  68. 6 8
      lib/play/board_play.dart
  69. BIN
      macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
  70. BIN
      macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
  71. BIN
      macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
  72. BIN
      macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
  73. BIN
      macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
  74. BIN
      macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
  75. BIN
      macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
  76. 40 0
      pubspec.lock
  77. 1 0
      pubspec.yaml
  78. BIN
      web/favicon.png
  79. BIN
      web/icons/Icon-192.png
  80. BIN
      web/icons/Icon-512.png
  81. BIN
      web/icons/Icon-maskable-192.png
  82. BIN
      web/icons/Icon-maskable-512.png
  83. 3 0
      web/index.html
  84. BIN
      windows/runner/resources/app_icon.ico

+ 1 - 0
.gitignore

@@ -43,3 +43,4 @@ app.*.map.json
 /android/app/debug
 /android/app/profile
 /android/app/release
+/android/key.properties

+ 6 - 0
README.md

@@ -37,3 +37,9 @@ ios build for pgyer
 ```
 flutter build ipa --export-method ad-hoc --bundle-sksl-path flutter_01.sksl.json
 ```
+
+## 正式打包编译:
+
+```
+flutter build appbundle --release
+```

+ 23 - 4
android/app/build.gradle.kts

@@ -1,11 +1,20 @@
+import java.util.Properties
+
+val localPropertiesFile = rootProject.file("key.properties")
+val localProperties = Properties()
+
+if (localPropertiesFile.exists()) {
+    localPropertiesFile.inputStream().use { inputStream ->
+        localProperties.load(inputStream)
+    }
+}
+
 plugins {
     id("com.android.application")
-    // START: FlutterFire Configuration
-    id("com.google.gms.google-services")
-    // END: FlutterFire Configuration
     id("kotlin-android")
     // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
     id("dev.flutter.flutter-gradle-plugin")
+    id("com.google.gms.google-services")
 }
 
 android {
@@ -36,11 +45,21 @@ android {
         versionName = flutter.versionName
     }
 
+    signingConfigs {
+        create("release") { // 使用 create 方法来定义签名配置
+            storeFile = file(localProperties.getProperty("storeFile"))
+            storePassword = localProperties.getProperty("storePassword")
+            keyAlias = localProperties.getProperty("keyAlias")
+            keyPassword = localProperties.getProperty("keyPassword")
+        }
+    }
+
     buildTypes {
         release {
             // TODO: Add your own signing config for the release build.
             // Signing with the debug keys for now, so `flutter run --release` works.
-            signingConfig = signingConfigs.getByName("debug")
+            // signingConfig = signingConfigs.getByName("debug")
+            signingConfig = signingConfigs.getByName("release")
         }
     }
 }

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


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


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


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


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


BIN
assets/builtin/5.jpeg


BIN
assets/builtin/691550964b99f02d1db7f6ed.jpeg


BIN
assets/builtin/691576da4b99f02d1db80e9c.jpeg


BIN
assets/builtin/691577834b99f02d1db811b3.jpeg


BIN
assets/builtin/691578314b99f02d1db81309.jpeg


BIN
assets/builtin/69157cd64b99f02d1db819a5.jpeg


BIN
assets/builtin/69157ea24b99f02d1db81e14.jpeg


BIN
assets/builtin/691580d84b99f02d1db821b3.jpeg


BIN
assets/builtin/691584ae4b99f02d1db82a1b.jpeg


+ 0 - 0
assets/images/691585964b99f02d1db82b44.jpeg → assets/builtin/691585964b99f02d1db82b44.jpeg


BIN
assets/builtin/6915869d4b99f02d1db82cf2.jpeg


BIN
assets/builtin/69158aab4b99f02d1db83576.jpeg


BIN
assets/builtin/69174ac74b99f02d1db91783.jpeg


BIN
assets/builtin/691b4c0d9b45d37f47dc668d.jpeg


BIN
assets/builtin/691bd6709b45d37f47dc7ad5.jpeg


BIN
assets/builtin/691bd8589b45d37f47dc7c21.jpeg


BIN
assets/builtin/691d397a9b45d37f47dcf7b8.jpeg


BIN
assets/builtin/691d3a809b45d37f47dcf8ca.jpeg


BIN
assets/builtin/691eea1f94c0827052e24617.jpeg


BIN
assets/builtin/691eebd194c0827052e24a13.jpeg


BIN
assets/builtin/692531f83c9826728f73cb73.jpeg


BIN
assets/builtin/6926966dede1f42c42e39a69.jpeg


BIN
assets/builtin/6929d6b7a4a70f6c6369cb5c.jpeg


+ 19 - 1
assets/builtin/collection.json

@@ -5,9 +5,27 @@
       "title": "Roman",
       "width": 2000,
       "height": 3000,
-      "difficulty": 4,
+      "difficulty": 5,
       "thumb": "assets/builtin/691d34ad9b45d37f47dcf31d.jpeg",
       "raw": "assets/builtin/691d34ad9b45d37f47dcf31d.jpeg"
+    },
+    {
+      "_id": "691bd6709b45d37f47dc7ad5",
+      "title": "Vienna",
+      "width": 2000,
+      "height": 3000,
+      "difficulty": 5,
+      "thumb": "assets/builtin/691bd6709b45d37f47dc7ad5.jpeg",
+      "raw": "assets/builtin/691bd6709b45d37f47dc7ad5.jpeg"
+    },
+    {
+      "_id": "691eebd194c0827052e24a13",
+      "title": "Vienna",
+      "width": 2000,
+      "height": 3000,
+      "difficulty": 5,
+      "thumb": "assets/builtin/691eebd194c0827052e24a13.jpeg",
+      "raw": "assets/builtin/691eebd194c0827052e24a13.jpeg"
     }
   ],
   "asset": true,

+ 162 - 6
assets/builtin/latest.json

@@ -1,28 +1,184 @@
 {
   "data": [
     {
-      "_id": "691580d84b99f02d1db821b3",
+      "_id": "6915869d4b99f02d1db82cf2",
       "width": 2000,
       "height": 3000,
       "difficulty": 3,
-      "thumb": "assets/builtin/691580d84b99f02d1db821b3.jpeg",
-      "raw": "assets/builtin/691580d84b99f02d1db821b3.jpeg"
+      "hard": false,
+      "thumb": "assets/builtin/6915869d4b99f02d1db82cf2.jpeg",
+      "raw": "assets/builtin/6915869d4b99f02d1db82cf2.jpeg"
     },
     {
-      "_id": "691578314b99f02d1db81309",
+      "_id": "69174ac74b99f02d1db91783",
+      "width": 2000,
+      "height": 3001,
+      "difficulty": 3,
+      "hard": false,
+      "thumb": "assets/builtin/69174ac74b99f02d1db91783.jpeg",
+      "raw": "assets/builtin/69174ac74b99f02d1db91783.jpeg"
+    },
+    {
+      "_id": "691576da4b99f02d1db80e9c",
+      "width": 2000,
+      "height": 3000,
+      "difficulty": 3,
+      "hard": false,
+      "thumb": "assets/builtin/691576da4b99f02d1db80e9c.jpeg",
+      "raw": "assets/builtin/691576da4b99f02d1db80e9c.jpeg"
+    },
+    {
+      "_id": "692531f83c9826728f73cb73",
+      "width": 2614,
+      "height": 3921,
+      "difficulty": 3,
+      "hard": false,
+      "thumb": "assets/builtin/692531f83c9826728f73cb73.jpeg",
+      "raw": "assets/builtin/692531f83c9826728f73cb73.jpeg"
+    },
+    {
+      "_id": "691b4c0d9b45d37f47dc668d",
+      "width": 2000,
+      "height": 3000,
+      "difficulty": 3,
+      "hard": false,
+      "thumb": "assets/builtin/691b4c0d9b45d37f47dc668d.jpeg",
+      "raw": "assets/builtin/691b4c0d9b45d37f47dc668d.jpeg"
+    },
+    {
+      "_id": "691550964b99f02d1db7f6ed",
+      "width": 2000,
+      "height": 3000,
+      "difficulty": 3,
+      "hard": false,
+      "thumb": "assets/builtin/691550964b99f02d1db7f6ed.jpeg",
+      "raw": "assets/builtin/691550964b99f02d1db7f6ed.jpeg"
+    },
+    {
+      "_id": "691577834b99f02d1db811b3",
+      "width": 2000,
+      "height": 3000,
+      "difficulty": 3,
+      "hard": false,
+      "thumb": "assets/builtin/691577834b99f02d1db811b3.jpeg",
+      "raw": "assets/builtin/691577834b99f02d1db811b3.jpeg"
+    },
+    {
+      "_id": "69157cd64b99f02d1db819a5",
+      "width": 2000,
+      "height": 3000,
+      "difficulty": 3,
+      "hard": false,
+      "thumb": "assets/builtin/69157cd64b99f02d1db819a5.jpeg",
+      "raw": "assets/builtin/69157cd64b99f02d1db819a5.jpeg"
+    },
+    {
+      "_id": "69158aab4b99f02d1db83576",
       "width": 2000,
       "height": 3000,
       "difficulty": 4,
-      "thumb": "assets/builtin/691578314b99f02d1db81309.jpeg",
-      "raw": "assets/builtin/691578314b99f02d1db81309.jpeg"
+      "hard": true,
+      "thumb": "assets/builtin/69158aab4b99f02d1db83576.jpeg",
+      "raw": "assets/builtin/69158aab4b99f02d1db83576.jpeg"
     },
     {
       "_id": "691591cf4b99f02d1db84713",
       "width": 2000,
       "height": 3000,
       "difficulty": 3,
+      "hard": false,
       "thumb": "assets/builtin/691591cf4b99f02d1db84713.jpeg",
       "raw": "assets/builtin/691591cf4b99f02d1db84713.jpeg"
+    },
+    {
+      "_id": "6929d6b7a4a70f6c6369cb5c",
+      "width": 2261,
+      "height": 3392,
+      "difficulty": 3,
+      "hard": false,
+      "thumb": "assets/builtin/6929d6b7a4a70f6c6369cb5c.jpeg",
+      "raw": "assets/builtin/6929d6b7a4a70f6c6369cb5c.jpeg"
+    },
+    {
+      "_id": "691585964b99f02d1db82b44",
+      "width": 2000,
+      "height": 3000,
+      "difficulty": 3,
+      "hard": false,
+      "thumb": "assets/builtin/691585964b99f02d1db82b44.jpeg",
+      "raw": "assets/builtin/691585964b99f02d1db82b44.jpeg"
+    },
+    {
+      "_id": "691d3a809b45d37f47dcf8ca",
+      "width": 2000,
+      "height": 3000,
+      "difficulty": 4,
+      "hard": true,
+      "thumb": "assets/builtin/691d3a809b45d37f47dcf8ca.jpeg",
+      "raw": "assets/builtin/691d3a809b45d37f47dcf8ca.jpeg"
+    },
+    {
+      "_id": "691584ae4b99f02d1db82a1b",
+      "width": 2000,
+      "height": 3000,
+      "difficulty": 3,
+      "hard": false,
+      "thumb": "assets/builtin/691584ae4b99f02d1db82a1b.jpeg",
+      "raw": "assets/builtin/691584ae4b99f02d1db82a1b.jpeg"
+    },
+    {
+      "_id": "691d397a9b45d37f47dcf7b8",
+      "width": 2000,
+      "height": 3000,
+      "difficulty": 3,
+      "hard": false,
+      "thumb": "assets/builtin/691d397a9b45d37f47dcf7b8.jpeg",
+      "raw": "assets/builtin/691d397a9b45d37f47dcf7b8.jpeg"
+    },
+    {
+      "_id": "691bd8589b45d37f47dc7c21",
+      "width": 2000,
+      "height": 3000,
+      "difficulty": 4,
+      "hard": true,
+      "thumb": "assets/builtin/691bd8589b45d37f47dc7c21.jpeg",
+      "raw": "assets/builtin/691bd8589b45d37f47dc7c21.jpeg"
+    },
+    {
+      "_id": "691eea1f94c0827052e24617",
+      "width": 3109,
+      "height": 4664,
+      "difficulty": 4,
+      "hard": false,
+      "thumb": "assets/builtin/691eea1f94c0827052e24617.jpeg",
+      "raw": "assets/builtin/691eea1f94c0827052e24617.jpeg"
+    },
+    {
+      "_id": "6926966dede1f42c42e39a69",
+      "width": 4480,
+      "height": 6720,
+      "difficulty": 4,
+      "hard": false,
+      "thumb": "assets/builtin/6926966dede1f42c42e39a69.jpeg",
+      "raw": "assets/builtin/6926966dede1f42c42e39a69.jpeg"
+    },
+    {
+      "_id": "69157ea24b99f02d1db81e14",
+      "width": 2000,
+      "height": 3000,
+      "difficulty": 5,
+      "hard": true,
+      "thumb": "assets/builtin/69157ea24b99f02d1db81e14.jpeg",
+      "raw": "assets/builtin/69157ea24b99f02d1db81e14.jpeg"
+    },
+    {
+      "_id": "691578314b99f02d1db81309",
+      "width": 2000,
+      "height": 3000,
+      "difficulty": 3,
+      "hard": false,
+      "thumb": "assets/builtin/691578314b99f02d1db81309.jpeg",
+      "raw": "assets/builtin/691578314b99f02d1db81309.jpeg"
     }
   ],
   "asset": true,

BIN
assets/icons/icon.png


BIN
assets/icons/icon_round.png


File diff suppressed because it is too large
+ 1 - 0
assets/images/title.svg


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png


BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png


+ 2 - 5
lib/config/config.dart

@@ -5,12 +5,9 @@ import 'package:flutter/material.dart';
 import 'device.dart';
 
 class Config {
-  static bool get isDebug => true;
+  static bool get isDebug => false;
 
   late Device device;
 
-  Config(
-    BuildContext context,
-    Directory baseDir,
-  ) : device = Device(context, baseDir);
+  Config(BuildContext context, Directory baseDir) : device = Device(context, baseDir);
 }

+ 5 - 6
lib/homepage/home_board.dart

@@ -1,12 +1,11 @@
-import 'dart:typed_data';
+import 'dart:ui' as ui;
 
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
+import 'package:logging/logging.dart';
 import 'package:puzzleweave/config/device.dart';
 import 'package:puzzleweave/models/download.dart';
 import 'package:puzzleweave/models/items.dart';
-import 'package:logging/logging.dart';
-import 'dart:ui' as ui;
 
 final Logger _log = Logger('home_board');
 
@@ -104,9 +103,9 @@ class HomeBoard {
   void switchToNextCollection(ListItem newItem) {
     _log.info('合集完全解锁,切换到新的合集');
 
-    // 先释放原来的资源
-    image?.dispose();
-    image = null;
+    // // 先释放原来的资源
+    // image?.dispose();
+    // image = null;
 
     // 重新加载新合集图
     isReadyNotifier.value = false;

+ 46 - 46
lib/homepage/home_board_play.dart

@@ -125,6 +125,9 @@ class HomeBoardPlayState extends State<HomeBoardPlay> with TickerProviderStateMi
 
           // 动画结束后,通知外部(HomeScreen)
           widget.onCollectionDone?.call();
+
+          // 启动发牌动画
+          _startDealingAnimation();
         }
       });
 
@@ -137,23 +140,34 @@ class HomeBoardPlayState extends State<HomeBoardPlay> with TickerProviderStateMi
         CurvedAnimation(parent: _dealingController, curve: Curves.easeOut) // 使用缓动曲线
           ..addStatusListener((status) {
             if (status == AnimationStatus.completed) {
-              board.status = HomeBoardStatus.playing; // 发牌结束进入正常的绘制状态
-              board.invalidate(); // 确保最终状态绘制正确
+              // 发牌结束,这个时候再来切换到下一个合集
+              switchToNextCollection();
+              setState(() {
+                board.status = HomeBoardStatus.playing; // 发牌结束进入正常的绘制状态
+                board.invalidate(); // 确保最终状态绘制正确
+              });
             }
           });
   }
 
-  _onCollectionDataUpdate(data) async {
+  _onCollectionDataUpdate(colldata) async {
     _log.info('_onCollectionDataUpdate.... ');
-    if (data != null) {
-      collection = data as List<ListItem>;
+    if (colldata != null) {
+      collection = colldata as List<ListItem>;
       if (collection != null && collection!.isNotEmpty) {
+        // 做一个矫正,避免没有正常退出,合集没有切换的情况
+        if ((data.completedWorks.value.length / 25).floor() > data.currentCollectionIndex) {
+          if (currentCollectionItem != null) {
+            _log.info('合集落后于关卡,没有正常切换,矫正!');
+            data.collectionDone(currentCollectionItem!);
+          }
+        }
         board.currentCollectionItem = currentCollectionItem;
       }
       setState(() {});
 
       // 远程数据没有加载到,3秒后重试
-      if (data.length < 5) {
+      if (colldata.length < 5) {
         Future.delayed(Duration(seconds: 3), () => refresh());
       }
     }
@@ -173,40 +187,34 @@ class HomeBoardPlayState extends State<HomeBoardPlay> with TickerProviderStateMi
     await collectionCachedRequest.refresh();
   }
 
+  // board 图片等资源加载完成的回调
   _onBoardReady() {
-    if (board.isReadyNotifier.value == true) {
-      // 首次加载或切换合集时,判断是否需要发牌动画
-      if (board.status == HomeBoardStatus.unlocking) {
-        _log.info('开启新的合集,发牌');
-        setState(() {
-          board.status = HomeBoardStatus.dealing;
-        });
-
-        _startDealingAnimation(); // 如果是 dealing 状态,就启动发牌动画
-        _dealingCount = 0;
-        audio.playSfx(SfxType.card);
-        _dealingPeriodicTimer = Timer.periodic(Duration(milliseconds: 150), (timer) {
-          if (mounted) {
-            _dealingCount++;
-            if (_dealingCount >= (_totalDealingDuration / 150) - 2) {
-              timer.cancel();
-            } else {
-              audio.playSfx(SfxType.card);
-            }
-          }
-        });
-      } else {
-        setState(() {
-          board.status = HomeBoardStatus.playing;
-        });
-      }
+    if (board.status == HomeBoardStatus.loading) {
+      setState(() {
+        board.status = HomeBoardStatus.playing;
+      });
     }
   }
 
   // !!! 改造点 2: 启动发牌动画
   void _startDealingAnimation() {
+    setState(() {
+      board.status = HomeBoardStatus.dealing;
+    });
     _dealingController.duration = Duration(milliseconds: _totalDealingDuration); // 确保duration是正确的
     _dealingController.forward(from: 0.0);
+    _dealingCount = 0;
+    audio.playSfx(SfxType.card);
+    _dealingPeriodicTimer = Timer.periodic(Duration(milliseconds: 150), (timer) {
+      if (mounted) {
+        _dealingCount++;
+        if (_dealingCount >= (_totalDealingDuration / 150) - 2) {
+          timer.cancel();
+        } else {
+          audio.playSfx(SfxType.card);
+        }
+      }
+    });
   }
 
   ListItem? get currentCollectionItem {
@@ -268,12 +276,6 @@ class HomeBoardPlayState extends State<HomeBoardPlay> with TickerProviderStateMi
         board.invalidate();
       });
 
-      // 展示撒花动画
-      audio.playSfx(SfxType.star);
-      confettiLayer.play();
-
-      // 等待confetti动画结束, 然后启动解锁动画
-      await Future.delayed(Duration(milliseconds: 600));
       _startUnlockAnimation();
     }
   }
@@ -353,11 +355,11 @@ class HomeBoardPlayState extends State<HomeBoardPlay> with TickerProviderStateMi
   void startFlipAnimation() {
     _flipController.forward(from: 0.0);
     audio.playSfx(SfxType.flip);
-    // 判断合集是否完成(此时level已经改变,但是合集还没有)
-    if (data.currentLevel != 0 && (data.currentCollectionIndex + 1) * 25 == data.currentLevel) {
-      if (currentCollectionItem != null) {
-        data.collectionDone(currentCollectionItem!);
-      }
+    if (data.currentLevel != 0 && (data.currentCollectionIndex + 1) * 25 == data.currentLevel && currentCollectionItem != null) {
+      data.collectionDone(currentCollectionItem!);
+      // 展示撒花动画
+      audio.playSfx(SfxType.star);
+      confettiLayer.play();
     }
   }
 
@@ -664,15 +666,13 @@ class CanvasPainter extends CustomPainter {
 
   _paintPlaying(Canvas canvas, Size size) {
     _log.info('_paintPlaying');
+
     for (var i = 0; i < board.rows; i++) {
       for (var j = 0; j < board.cols; j++) {
         // 玩过的关卡翻正面显示, 否则显示卡片背面
         final int curIndex = i * board.rows + j;
         bool flipped = level > collectionIndex * board.count + curIndex;
 
-        // for test:
-        // bool flipped = (i == 4 && j == 4) ? false : true;
-
         _drawPiece(canvas, size, i, j, flipped);
       }
     }

+ 129 - 12
lib/homepage/home_screen.dart

@@ -1,5 +1,6 @@
 import 'dart:async';
 import 'dart:io';
+import 'dart:math';
 
 import 'package:advertising_id/advertising_id.dart';
 import 'package:app_tracking_transparency/app_tracking_transparency.dart';
@@ -8,7 +9,11 @@ import 'package:firebase_messaging/firebase_messaging.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
+import 'package:flutter_svg/svg.dart';
 import 'package:fluttertoast/fluttertoast.dart';
+import 'package:logging/logging.dart';
+import 'package:lottie/lottie.dart';
+import 'package:provider/provider.dart';
 import 'package:puzzleweave/ads/applovin_ads_controller.dart';
 import 'package:puzzleweave/audio/jc_audio_controller.dart';
 import 'package:puzzleweave/collection/collection_screen.dart';
@@ -17,17 +22,13 @@ import 'package:puzzleweave/homepage/home_board_play.dart';
 import 'package:puzzleweave/l10n/app_localizations.dart';
 import 'package:puzzleweave/models/cached_request.dart';
 import 'package:puzzleweave/models/data.dart';
+import 'package:puzzleweave/models/download.dart';
 import 'package:puzzleweave/models/items.dart';
 import 'package:puzzleweave/platform/my_method_channel.dart';
 import 'package:puzzleweave/play/board_play.dart';
-import 'package:puzzleweave/settings/settings_dialog.dart';
 import 'package:puzzleweave/settings/settings_screen.dart';
 import 'package:puzzleweave/skin/skin.dart';
 import 'package:puzzleweave/utils/mybutton.dart';
-import 'package:logging/logging.dart';
-import 'package:lottie/lottie.dart';
-import 'package:provider/provider.dart';
-import '../ads/ads_state.dart';
 
 final Logger _log = Logger('home_screen');
 
@@ -38,6 +39,8 @@ class HomeScreen extends StatefulWidget {
   State<StatefulWidget> createState() => _HomeScreen();
 }
 
+const int minimumRemoteLoadCount = 30; // 假设加载到 30 张图才算网络畅通
+
 class _HomeScreen extends State<HomeScreen> with TickerProviderStateMixin {
   late Device device;
   late JcAudioController audio;
@@ -80,7 +83,7 @@ class _HomeScreen extends State<HomeScreen> with TickerProviderStateMixin {
           vsync: this,
         )..addStatusListener((status) {
           if (status == AnimationStatus.completed) {
-            _canvasKey.currentState?.switchToNextCollection();
+            audio.playSfx(SfxType.pop);
           }
         });
 
@@ -108,10 +111,31 @@ class _HomeScreen extends State<HomeScreen> with TickerProviderStateMixin {
       latest = data as List<ListItem>;
       isLoading = false;
       setState(() {});
-      if (data.length >= 20) {
-        // 远程latest列表已加载,说明网络已通,这个时候再来初始化Admod,ATT, UMP这些东西
-        initThird();
+
+      // 1. 检查数据量是否达到最低要求 (>= 30)
+      final bool hasSufficientData = data.length >= minimumRemoteLoadCount;
+
+      // 2. 检查数据是否来自最近一次成功的网络请求
+      final bool isNetworkActive = latestCachedRequest.hasRecentSuccessfulFetch; // !!! 关键检查点
+
+      if (hasSufficientData) {
+        // 如果数据完整,无论是否是缓存数据,都尝试初始化第三方服务(因为主页已经可以显示了)
+        if (!hasInit) {
+          initThird();
+        }
+
+        // !!! 核心修改:只有在数据完整且最近网络请求成功时,才启动预加载
+        if (isNetworkActive) {
+          _log.info('Data sufficient AND Network Active. Starting preload.');
+          _preloadNextImages();
+        } else {
+          // 数据完整,但来自缓存,网络状态未知,3秒后尝试刷新(refresh)
+          _log.info('Data sufficient BUT Network status unknown/inactive. Attempting refresh in 3s.');
+          Future.delayed(Duration(seconds: 3), () => refresh());
+        }
       } else {
+        // 数据不足 (例如,只有内置图),无论是缓存还是远程失败,都需要重试
+        _log.info('Data insufficient (only ${data.length} items). Attempting refresh in 3s.');
         Future.delayed(Duration(seconds: 3), () => refresh());
       }
     }
@@ -170,6 +194,70 @@ class _HomeScreen extends State<HomeScreen> with TickerProviderStateMixin {
     return null;
   }
 
+  /// 预加载未来 N 张图片到磁盘,并最后触发当前关卡下载以最大化内存缓存命中率。
+  void _preloadNextImages() {
+    // 预加载数量 (包括当前关卡在内,共 20 个)
+    const int totalPreloadCount = 20;
+
+    // 1. 确保 latest 数据已加载
+    if (latest == null || latest!.isEmpty || latest!.length < minimumRemoteLoadCount) {
+      _log.info('Preload failed: latest list is empty.');
+      return;
+    }
+
+    // 2. 查找当前未完成的第一张图片的索引 (Index of currentItem)
+    final Set<String> completedIds = data.completedWorks.value.map((work) => work.id).toSet();
+    int startIndex = -1;
+    for (int i = 0; i < latest!.length; i++) {
+      if (!completedIds.contains(latest![i].id)) {
+        startIndex = i;
+        break;
+      }
+    }
+
+    if (startIndex == -1) {
+      _log.info('Preload: All images completed, nothing to preload.');
+      return;
+    }
+
+    // 确定预加载范围 (从当前图片startIndex到 totalPreloadCount 个图片)
+    final int endPreloadIndex = min(startIndex + totalPreloadCount, latest!.length);
+
+    // 3. 准备要加载的列表 (从 startIndex 开始)
+    final List<ListItem> itemsToLoad = latest!.sublist(startIndex, endPreloadIndex);
+
+    if (itemsToLoad.isEmpty) {
+      _log.info('Preload: No items found in the range.');
+      return;
+    }
+
+    // 4. 将当前关卡 (第一个元素) 移动到列表的末尾
+    final ListItem currentItemToLoad = itemsToLoad.removeAt(0);
+    itemsToLoad.add(currentItemToLoad);
+
+    _log.info('Preloading ${itemsToLoad.length} images. Current item: ${currentItemToLoad.id} will be loaded last.');
+
+    // 5. 循环触发 ItemLoader 加载
+    int preloadCount = 0;
+    for (final itemToLoad in itemsToLoad) {
+      // 对远程图片进行预加载
+      // 调用 ItemLoader.load,它会使用 Download 单例进行下载和缓存
+      // 我们不关心返回值或 Future,只是触发下载
+      if (itemToLoad is RemoteItem) {
+        try {
+          // 触发下载。对于非当前关卡,下载器会完成下载并写入磁盘,然后可能释放内存。
+          // 对于当前关卡 (最后一个被调用的),它留在内存中的可能性最大。
+          ItemLoader.load(itemToLoad);
+          preloadCount++;
+        } catch (e) {
+          _log.warning('Failed to load item for preloading: ${itemToLoad.id}, error: $e');
+        }
+      }
+    }
+
+    _log.info('Preload initiated for $preloadCount remote images, current item was last.');
+  }
+
   @override
   Widget build(BuildContext context) {
     if (isLoading) return scrollableDummy;
@@ -216,9 +304,20 @@ class _HomeScreen extends State<HomeScreen> with TickerProviderStateMixin {
             ),
           ),
         ),
-        title: const Text(
-          'Jigsort Solitaire',
-          style: TextStyle(color: Colors.black87, fontWeight: FontWeight.bold, fontSize: 24),
+        // title: const Text(
+        //   'Jigsort Solitaire',
+        //   style: TextStyle(color: Colors.black87, fontWeight: FontWeight.bold, fontSize: 24),
+        // ),
+        // 🚀 改造点:将 Text 标题替换为 SvgPicture
+        title: SvgPicture.asset(
+          'assets/images/title.svg', // 替换为您的 SVG 文件路径
+          height: 32, // 根据您的设计调整高度,确保它在 AppBar 中显示良好
+          // colorFilter: const ColorFilter.mode(Colors.black87, BlendMode.srcIn), // 如果SVG是单色,可以设置颜色
+          placeholderBuilder: (BuildContext context) => const Text(
+            // 占位符,以防SVG加载失败
+            'Jigsort Solitaire',
+            style: TextStyle(color: Colors.black87, fontWeight: FontWeight.bold, fontSize: 24),
+          ),
         ),
         actions: [
           IconButton(
@@ -306,6 +405,24 @@ class _HomeScreen extends State<HomeScreen> with TickerProviderStateMixin {
           final result = await Navigator.push(context, pageRouteBuilder);
           if (result == true) {
             _canvasKey.currentState?.startFlipAnimation();
+            final bool hasSufficientData = latest != null && latest!.length >= minimumRemoteLoadCount;
+            final bool isNetworkActive = latestCachedRequest.hasRecentSuccessfulFetch;
+
+            if (hasSufficientData) {
+              // 1. 数据完整:如果网络活跃,立即顺延预加载。
+              if (isNetworkActive) {
+                _log.info('Game finished, data complete & Network Active. Triggering sequential preloading...');
+                _preloadNextImages();
+              } else {
+                // 2. 数据完整但网络不活跃/状态未知:尝试刷新,让 _onLatestDataUpdate 负责后续处理
+                _log.info('Game finished, data complete but Network inactive. Attempting refresh.');
+                refresh();
+              }
+            } else {
+              // 3. 数据不完整:无论如何都需要刷新,让 _onLatestDataUpdate 重新处理
+              _log.info('Game finished, remote data incomplete. Attempting refresh...');
+              refresh();
+            }
           }
         } else {
           Fluttertoast.showToast(

+ 4 - 7
lib/main.dart

@@ -1,3 +1,4 @@
+import 'dart:developer' as dev;
 import 'dart:io';
 
 import 'package:device_info_plus/device_info_plus.dart';
@@ -6,8 +7,9 @@ import 'package:firebase_crashlytics/firebase_crashlytics.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
-// import 'package:google_fonts/google_fonts.dart';
-import 'package:puzzleweave/ads/applovin_ads_controller.dart';
+import 'package:logging/logging.dart';
+import 'package:path_provider/path_provider.dart';
+import 'package:provider/provider.dart';
 import 'package:puzzleweave/app_lifecycle/app_lifecycle.dart';
 import 'package:puzzleweave/audio/jc_audio_controller.dart';
 import 'package:puzzleweave/firebase/firebase_options.dart';
@@ -19,11 +21,6 @@ import 'package:puzzleweave/persistence/persistence.dart';
 import 'package:puzzleweave/play/board_play.dart';
 import 'package:puzzleweave/remote_config/remote_config.dart';
 import 'package:puzzleweave/settings/settings_controller.dart';
-import 'package:logging/logging.dart';
-import 'dart:developer' as dev;
-
-import 'package:path_provider/path_provider.dart';
-import 'package:provider/provider.dart';
 
 import 'config/config.dart' as cfg;
 import 'config/device.dart';

+ 1 - 1
lib/models/api_helper.dart

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
 
 import '../config/config.dart';
 
-const String cdnHost = 'd2mb6s2cy1zg97.cloudfront.net';
+const String cdnHost = 'dpk2r23lv4s2l.cloudfront.net';
 const String developmentHost = 'color.jccytech.cn';
 const String productionHost = 'app.pcoloring.com';
 const String localAVDHost = '10.0.2.2:6888';

+ 12 - 1
lib/models/cached_request.dart

@@ -13,6 +13,10 @@ typedef TransformFunction = Future<dynamic> Function(dynamic json);
 
 // 混入 WidgetsBindingObserver 以监听应用生命周期
 class CachedRequest with WidgetsBindingObserver {
+  // !!! 新增属性 1: 标记最近一次请求是否通过网络成功完成
+  bool _hasRecentSuccessfulFetch = false;
+  bool get hasRecentSuccessfulFetch => _hasRecentSuccessfulFetch;
+
   static final Map<String, CachedRequest> _cache = {};
 
   final String url;
@@ -95,9 +99,14 @@ class CachedRequest with WidgetsBindingObserver {
     try {
       final response = await http.get(Uri.parse(url));
       if (response.statusCode != 200) {
+        // 如果状态码失败,则标记为失败,并抛出异常
+        _hasRecentSuccessfulFetch = false; // !!! 关键:网络失败
         throw Exception('Invalid status code: ${response.statusCode} when fetching: $url');
       }
-      _log.info('${response.statusCode}, $url');
+
+      // !!! 关键:网络请求成功,标记为成功
+      _hasRecentSuccessfulFetch = true;
+      _log.info('${response.statusCode}, $url, Network Success: true');
 
       final data = jsonDecode(response.body);
       _emit(data);
@@ -106,6 +115,8 @@ class CachedRequest with WidgetsBindingObserver {
     } catch (error) {
       _streamController.addError(error);
       _log.severe('Remote load failed for $url: $error');
+      // 即使在 catch 块中,也再次确认标记为失败(以防万一)
+      _hasRecentSuccessfulFetch = false;
     }
   }
 

+ 36 - 0
lib/models/data.dart

@@ -4,6 +4,7 @@ import 'package:puzzleweave/models/cached_request.dart';
 import 'package:puzzleweave/models/items.dart';
 import 'package:puzzleweave/persistence/persistence.dart';
 import 'package:logging/logging.dart';
+import 'package:puzzleweave/utils/utils.dart';
 
 final Logger _log = Logger('data.dart');
 
@@ -16,6 +17,12 @@ class Data {
   Data({required Persistence persistence}) : _persistence = persistence;
 
   Future<void> loadDataFromPersistence() async {
+    // for test 为了测试合集完成动画
+    // var works = _persistence.completedWorks;
+    // works = works.sublist(0, works.length - 1);
+    // _persistence.completedWorks = works;
+    // _persistence.completedCollections = [];
+
     completedWorks.value = _persistence.completedWorks;
     completedCollections.value = _persistence.completedCollections;
   }
@@ -24,9 +31,38 @@ class Data {
   // !!! 改造点:接受 ListItem 和可选的耗时
   void workDone(ListItem item, {Duration? timeSpent}) {
     final newWork = Work.fromListItem(item, timeSpent: timeSpent);
+
+    // 1. 记录作品完成
     final updatedWorks = [...completedWorks.value, newWork];
     completedWorks.value = updatedWorks;
     _persistence.completedWorks = updatedWorks; // 存储更新后的列表
+
+    // 2. 核心新增:清除已完成作品的缓存
+    _clearCompletedItemCache(item);
+  }
+
+  /// 清除已完成作品的缓存文件。
+  void _clearCompletedItemCache(ListItem item) async {
+    // 只有 RemoteItem 才会有需要清理的下载缓存
+    if (item is RemoteItem) {
+      _log.info('Work done for remote item ${item.id}. Attempting to clear cache at path: ${item.cachePath}');
+
+      try {
+        // 使用 cachePath 定义,并通过 localFile 找到实际的文件路径
+        final file = await localFile(item.cachePath);
+
+        if (await file.exists()) {
+          await file.delete();
+          _log.info('Successfully cleared cache file for ${item.id}');
+        } else {
+          _log.warning('Cache file not found for ${item.id} at path: ${item.cachePath}');
+        }
+      } catch (e) {
+        _log.severe('Failed to clear cache for item ${item.id}, error: $e');
+      }
+    } else {
+      _log.info('Work done for asset item ${item.id}. No network cache to clear.');
+    }
   }
 
   // 获取当前的关卡index (基于已完成作品的数量)

+ 14 - 2
lib/models/download.dart

@@ -25,7 +25,8 @@ class Download {
   final Map<String, DownloadItem> _cache = {};
 
   DownloadItem download(url, cachePath) {
-    _clean();
+    // 移除同步 _clean() 调用,避免干扰正在启动的下载序列
+    // _clean();
     if (_cache[url] != null) {
       _log.info('Cache hit for $url');
       _cache[url]!.touch(); //update last use time.
@@ -41,17 +42,28 @@ class Download {
   _watch(DownloadItem item) async {
     try {
       await item.loadCompleter.future;
+      // !!! 修正点 1: 在任务完成后,异步触发清理
+      // 任务完成后,它占用的内存 Image 和 Data 就可以被清理了
+      Future.microtask(_clean);
     } catch (err) {
+      // 发生错误,立即移除缓存项
       _log.info('Watch download item got error: $err');
       _cache.remove(item.url);
     }
   }
 
   _clean() {
-    final list = _cache.values.toList();
+    // final list = _cache.values.toList();
+    // 1. 筛选出可以被清理的项:必须是已完成加载(已完成下载并写入磁盘)
+    final list = _cache.values
+        .where((item) => item.loadCompleter.isCompleted) // !!! 修正点 2: 仅清理已完成加载的项
+        .toList();
     if (list.length <= maxCachedItems) return;
     _log.info('cleaning...');
+
+    // 2. 按最近使用时间排序(时间越早越应该被清理)
     list.sort((a, b) => a.lastUsed.compareTo(b.lastUsed));
+    // 3. 清理到只剩下 maxCachedItems 个
     while (list.length > maxCachedItems) {
       final item = list.removeAt(0);
       _log.info('clean item: $item');

+ 2 - 4
lib/play/board.dart

@@ -1,15 +1,13 @@
 // board.dart
 
-import 'dart:async';
-import 'dart:math';
-import 'dart:typed_data';
 import 'dart:ui' as ui;
+
 import 'package:flutter/material.dart';
+import 'package:logging/logging.dart';
 import 'package:puzzleweave/config/device.dart';
 import 'package:puzzleweave/play/piece.dart';
 import 'package:puzzleweave/skin/skin.dart';
 import 'package:puzzleweave/utils/utils.dart';
-import 'package:logging/logging.dart';
 import 'package:vector_math/vector_math.dart' as vmath;
 
 final Logger _log = Logger('board.dart');

+ 6 - 8
lib/play/board_play.dart

@@ -1,12 +1,13 @@
 import 'dart:async';
 import 'dart:io';
 import 'dart:math';
-import 'dart:typed_data';
+import 'dart:ui' as ui;
 
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:fluttertoast/fluttertoast.dart';
-import 'package:puzzleweave/audio/audio_controller.dart' hide SfxType;
+import 'package:logging/logging.dart';
+import 'package:provider/provider.dart';
 import 'package:puzzleweave/audio/jc_audio_controller.dart';
 import 'package:puzzleweave/config/device.dart';
 import 'package:puzzleweave/l10n/app_localizations.dart';
@@ -23,14 +24,9 @@ import 'package:puzzleweave/settings/settings_controller.dart';
 import 'package:puzzleweave/settings/settings_dialog.dart';
 import 'package:puzzleweave/skin/skin.dart';
 import 'package:puzzleweave/utils/mybutton.dart';
-import 'package:logging/logging.dart';
-import 'package:provider/provider.dart';
-import 'dart:ui' as ui;
 import 'package:vector_math/vector_math.dart' as vmath;
 import 'package:vibration/vibration.dart';
 
-import '../ads/ads_state.dart';
-
 final Logger _log = Logger('board_play.dart');
 
 // 移动类型 (不再需要,但保留枚举以防止其他文件引用报错)
@@ -55,7 +51,7 @@ class BoardPlay extends StatefulWidget {
     return _BoardPlayState();
   }
 
-  static PageRouteBuilder buildRoute(ListItem item, {difficult = 1, count = 6}) {
+  static PageRouteBuilder buildRoute(ListItem item) {
     return PageRouteBuilder(
       pageBuilder: (context, animation, secondaryAnimation) {
         return BoardPlay(item: item);
@@ -552,11 +548,13 @@ class _BoardPlayState extends State<BoardPlay> with TickerProviderStateMixin {
   _onSuccess() {
     _log.info('success! 游戏完成!');
     data.workDone(widget.item);
+
     board!.success();
     audio.playSfx(SfxType.success);
     confettiLayer.play();
 
     _successAnimationController.forward(from: 0.0);
+
     setState(() {});
   }
 

BIN
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png


BIN
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png


BIN
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png


BIN
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png


BIN
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png


BIN
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png


BIN
macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png


+ 40 - 0
pubspec.lock

@@ -443,6 +443,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "4.0.1"
+  flutter_svg:
+    dependency: "direct main"
+    description:
+      name: flutter_svg
+      sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.2.3"
   flutter_test:
     dependency: "direct dev"
     description: flutter
@@ -654,6 +662,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.9.1"
+  path_parsing:
+    dependency: transitive
+    description:
+      name: path_parsing
+      sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.0"
   path_provider:
     dependency: "direct main"
     description:
@@ -1019,6 +1035,30 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "4.5.2"
+  vector_graphics:
+    dependency: transitive
+    description:
+      name: vector_graphics
+      sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.19"
+  vector_graphics_codec:
+    dependency: transitive
+    description:
+      name: vector_graphics_codec
+      sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.13"
+  vector_graphics_compiler:
+    dependency: transitive
+    description:
+      name: vector_graphics_compiler
+      sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.19"
   vector_math:
     dependency: "direct main"
     description:

+ 1 - 0
pubspec.yaml

@@ -73,6 +73,7 @@ dependencies:
   launch_review_latest: ^1.0.0
   flutter_launcher_icons: ^0.14.4
   flutter_native_splash: ^2.4.7
+  flutter_svg: ^2.2.3
 
 dev_dependencies:
   flutter_test:

BIN
web/favicon.png


BIN
web/icons/Icon-192.png


BIN
web/icons/Icon-512.png


BIN
web/icons/Icon-maskable-192.png


BIN
web/icons/Icon-maskable-512.png


+ 3 - 0
web/index.html

@@ -32,6 +32,8 @@
     
   
   <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
+  
+  
   <style id="splash-screen-style">
     html {
       height: 100%
@@ -105,6 +107,7 @@
       <img class="center" aria-hidden="true" src="splash/img/light-1x.png" alt="">
   </picture>
   
+  
     <script src="flutter_bootstrap.js" async=""></script>
   
 

BIN
windows/runner/resources/app_icon.ico


Some files were not shown because too many files changed in this diff