Compare commits

...

19 Commits

Author SHA1 Message Date
豌杂 197eae3079 ios 打包 2025-12-19 17:49:04 +08:00
豌杂 8fd0303427 添加Mac环境打包脚本 2025-12-19 16:44:27 +08:00
豌杂 122dfd6542 修复苹果登陆不能输入非数字,优化升级插件 2025-12-19 16:29:55 +08:00
豌杂 d125a2b470 处理IOS密码不能输入非数字的内容 2025-12-18 13:57:19 +08:00
DESKTOP-I3JPKHK\wy 7f3961c8ea 1 2025-10-10 15:18:26 +08:00
DESKTOP-I3JPKHK\wy 2545e4e314 windows 更新 2025-10-10 14:46:06 +08:00
豌杂 1a157ffc6f IOS版本号升级 2025-09-30 11:18:48 +08:00
DESKTOP-I3JPKHK\wy 4161a9fcc8 优化顶部打分bar 和 number 打分栏的rebuild 性能优化 2025-09-30 11:11:51 +08:00
DESKTOP-I3JPKHK\wy 2ac88000cc 移除高频 print 日志 2025-09-29 17:30:47 +08:00
DESKTOP-I3JPKHK\wy c7afafc3aa feat(homework_review): 优化批注绘制与坐标换算,提升交互流畅度
- 批注绘制改为 Path 聚合一次性绘制
- 移除高频 print,开启 CustomPaint isComplex/willChange
- 无变换时跳过逆矩阵
- 回滚不必要的 RepaintBoundary/低过滤
- 调整 Path 判空判断写法
2025-09-29 17:22:54 +08:00
DESKTOP-I3JPKHK\wy 1fc54d7526 补充提交 2025-09-29 13:39:37 +08:00
DESKTOP-I3JPKHK\wy e96429fe05 11111 2025-09-29 09:29:28 +08:00
DESKTOP-I3JPKHK\wy f050263ddf 终于解决了这个切换试题放大的内容乱跑的问题(更新视图zoomController位置重置时机应在key重新后,不该在前,被覆盖) 2025-09-28 18:07:50 +08:00
DESKTOP-I3JPKHK\wy bac6a7410a 版本更新 1.0.6+7 2025-09-23 16:31:24 +08:00
DESKTOP-I3JPKHK\wy 10025b49ee 修复部分代码 2025-09-23 16:16:59 +08:00
DESKTOP-I3JPKHK\wy c6409b095c 修复BUG和使用更新APP插件 2025-09-22 16:44:53 +08:00
DESKTOP-I3JPKHK\wy f638ef23de 补充提交,修复留白区域笔迹问题 2025-09-15 15:31:46 +08:00
DESKTOP-I3JPKHK\wy 8fd6c57874 Merge branch 'mcy_new' 2025-09-12 14:00:16 +08:00
wangyang 5f4c9237c1 Merge pull request 'mcy_new' (#1) from mcy_new into master
Reviewed-on: #1
2025-08-28 10:10:44 +08:00
48 changed files with 1642 additions and 2483 deletions

View File

@ -5,9 +5,11 @@
*.swp *.swp
.DS_Store .DS_Store
.atom/ .atom/
.build/
.buildlog/ .buildlog/
.history .history
.svn/ .svn/
.swiftpm/
migrate_working_dir/ migrate_working_dir/
# IntelliJ related # IntelliJ related

View File

@ -35,6 +35,7 @@ android {
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8
coreLibraryDesugaringEnabled true
} }
kotlinOptions { kotlinOptions {
@ -84,8 +85,14 @@ android {
shrinkResources false // shrinkResources false //
} }
} }
} }
flutter { flutter {
source = "../.." source = "../.."
} }
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' //
}

View File

@ -40,7 +40,7 @@
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2"/> android:value="2"/>
<!-- Provider --> <!-- Provider -->
<provider <!-- <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileProvider" android:authorities="${applicationId}.fileProvider"
android:exported="false" android:exported="false"
@ -48,6 +48,16 @@
<meta-data <meta-data
android:name="android.support.FILE_PROVIDER_PATHS" android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/> android:resource="@xml/file_paths"/>
</provider> -->
<!-- ✅ FileProvider 配置 - 这是必需的! -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider> </provider>
</application> </application>
<!-- 访问电话状态 --> <!-- 访问电话状态 -->
@ -77,25 +87,21 @@
<!-- 屏幕常亮权限 --> <!-- 屏幕常亮权限 -->
<uses-permission <uses-permission
android:name="android.permission.WAKE_LOCK"/> android:name="android.permission.WAKE_LOCK"/>
<uses-permission <!-- <uses-permission
android:name="android.permission.CAMERA"/> android:name="android.permission.CAMERA"/> -->
<!-- Permissions options for the `access notification policy` group --> <!-- Permissions options for the `access notification policy` group -->
<uses-permission <uses-permission
android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/> android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>
<!-- Permissions options for the `notification` group --> <!-- Permissions options for the `notification` group -->
<uses-permission <uses-permission
android:name="android.permission.POST_NOTIFICATIONS"/> android:name="android.permission.POST_NOTIFICATIONS"/>
<!-- Required to query activities that can process text, see: <!-- 注释掉文本处理权限,避免不必要的剪切板访问 -->
https://developer.android.com/training/package-visibility and <!-- <queries>
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent> <intent>
<action <action
android:name="android.intent.action.PROCESS_TEXT"/> android:name="android.intent.action.PROCESS_TEXT"/>
<data <data
android:mimeType="text/plain"/> android:mimeType="text/plain"/>
</intent> </intent>
</queries> </queries> -->
</manifest> </manifest>

View File

@ -1,5 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<paths> <paths xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 外部存储 -->
<external-path name="external_files" path="." />
<!-- 应用内部存储 -->
<files-path name="files" path="." />
<!-- 缓存目录 -->
<cache-path name="cache" path="." />
<!-- 外部缓存 -->
<external-cache-path name="external_cache" path="." />
<!-- 下载目录 -->
<external-path name="downloads" path="Download/" />
<!-- 如果你已有其他配置,请保留它们 -->
<external-path path="Android/data/com.yuanxuan.making_school_asignment_app/" name="files_root" /> <external-path path="Android/data/com.yuanxuan.making_school_asignment_app/" name="files_root" />
<external-path path="." name="external_storage_root" /> <external-path path="." name="external_storage_root" />
</paths> </paths>

View File

@ -30,8 +30,8 @@
<!-- 屏幕常亮权限 --> <!-- 屏幕常亮权限 -->
<uses-permission <uses-permission
android:name="android.permission.WAKE_LOCK"/> android:name="android.permission.WAKE_LOCK"/>
<uses-permission <!-- <uses-permission
android:name="android.permission.CAMERA"/> android:name="android.permission.CAMERA"/> -->
<!-- Permissions options for the `access notification policy` group --> <!-- Permissions options for the `access notification policy` group -->
<uses-permission <uses-permission
android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/> android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>

View File

@ -29,7 +29,7 @@
type="application/octet-stream" /> type="application/octet-stream" />
</item> --> </item> -->
<item> <item>
<title>Version 1.0.3+4</title> <title>Version 1.0.7+8</title>
#发行说明-读取html方式(2选1) #发行说明-读取html方式(2选1)
<!-- <sparkle:releaseNotesLink> <!-- <sparkle:releaseNotesLink>
https://your_domain/your_path/release_notes.html https://your_domain/your_path/release_notes.html
@ -39,16 +39,15 @@
<![CDATA[ <![CDATA[
<ul> <ul>
<li>1、修复已知BUG</li> <li>1、修复已知BUG</li>
<li>2、优化操作布局</li> <li>2、添加缩放布局和批阅书写</li>
</ul> </ul>
]]> ]]>
</description> </description>
<pubDate>Fri, 14 Mar 2025 12:00:00 +0800</pubDate> <pubDate>Tue, 30 Sep 2025 14:09:13 +0800</pubDate>
#你更新程序的地址 #你更新程序的地址
<enclosure url="https://dpc-job-oss.23544.com/infra-app/making_school_asignment_app/1.0.3/3/making_school_asignment_app-1.0.3+4-windows-setup.exe" <enclosure url="https://dpc-job-oss.23544.com/infra-app/making_school_asignment_app/1.0.7/3/making_school_asignment_app-1.0.7+8-windows-setup.exe"
sparkle:dsaSignature="MEYCIQCuU0BodcdWrF+WoJrWRpY8P1pfK+dKkvrl3ZJ5KxnDdAIhAIWqp7VBNO9IaKFj2ypQ+s7DWurBUSaf6MTvexMmuvX+" sparkle:dsaSignature="MEUCIQCwWC4fKrn/dfdH6uzX4Sssv7G6XVgvwjQLklrTM6+cBQIgZDY4INHcwuxrsIREuPQeTvQL4j0mvF9qPQfppjRSgsg=" length="0"
length="0" sparkle:version="1.0.7+8"
sparkle:version="1.0.3+4"
sparkle:os="windows" sparkle:os="windows"
type="application/octet-stream" /> type="application/octet-stream" />
</item> </item>

View File

@ -0,0 +1,159 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
# Ensure UTF-8 locale for macOS terminals
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
# Resolve project directory (script location)
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
echo "Project dir: $PROJECT_DIR"
# To save credentials in macOS Keychain for non-interactive uploads, run:
# security add-generic-password -s "TransporterCredentials" -w "you@example.com:app-specific-password" -a transporter -U
# The script will read that item and expect the stored password string in the form: "APPLE_ID:APPLE_PASSWORD"
force_delete() {
local target="$1"
local max_retries=3
local retry_delay=1
if [ ! -e "$PROJECT_DIR/$target" ]; then
return 0
fi
for i in $(seq 1 $max_retries); do
echo "Attempting to delete $target (try $i of $max_retries)"
rm -rf "$PROJECT_DIR/$target" 2>/dev/null || true
if [ ! -e "$PROJECT_DIR/$target" ]; then
echo "Successfully deleted $target"
return 0
fi
if [ "$i" -lt "$max_retries" ]; then
sleep $retry_delay
retry_delay=$((retry_delay * 2))
fi
done
echo "Warning: Could not completely delete $target, some files may be in use"
return 1
}
cd "$PROJECT_DIR"
echo "Running flutter clean..."
if ! flutter clean; then
echo "flutter clean failed"
exit 1
fi
echo "Removing residual files"
force_delete build || true
force_delete .dart_tool || true
force_delete pubspec.lock || true
echo "Running flutter pub get..."
if ! flutter pub get; then
echo "flutter pub get failed"
exit 1
fi
echo "Running build_runner (if present)..."
if ! flutter packages pub run build_runner build --delete-conflicting-outputs; then
echo "build_runner failed or not present — continuing"
fi
echo "Building iOS IPA (verbose)..."
if ! flutter build ipa --release --verbose; then
echo "IPA build failed"
exit 1
fi
# Try to find the generated .ipa (searching under build/)
IPA_PATH="$(find "$PROJECT_DIR/build" -type f -name '*.ipa' -print -quit || true)"
if [ -z "$IPA_PATH" ]; then
echo "No .ipa found in build output"
echo "Build finished but no IPA to upload. Exiting."
exit 0
fi
echo "Found IPA: $IPA_PATH"
# Upload via Transporter (if installed). Prefer environment vars APPLE_ID and APPLE_PASSWORD (app-specific password).
ITMS_TRANSPORTER="/Applications/Transporter.app/Contents/itms/bin/iTMSTransporter"
if [ -x "$ITMS_TRANSPORTER" ]; then
echo "Using Transporter at $ITMS_TRANSPORTER"
# Read credentials from env, then try macOS Keychain, otherwise prompt
if [ -z "${APPLE_ID:-}" ] || [ -z "${APPLE_PASSWORD:-}" ]; then
# Try to read combined credentials from Keychain (service: TransporterCredentials)
if command -v security >/dev/null 2>&1; then
KC_PAIR="$(security find-generic-password -s "TransporterCredentials" -w 2>/dev/null || true)"
if [ -n "$KC_PAIR" ]; then
# Expect stored value in form: appleid:app-specific-password
APPLE_ID="${KC_PAIR%%:*}"
APPLE_PASSWORD="${KC_PAIR#*:}"
if [ -n "${APPLE_ID}" ] && [ -n "${APPLE_PASSWORD}" ]; then
echo "Credentials loaded from Keychain (service: TransporterCredentials)."
else
# clear if parsing failed
APPLE_ID=""
APPLE_PASSWORD=""
fi
fi
fi
if [ -z "${APPLE_ID:-}" ] || [ -z "${APPLE_PASSWORD:-}" ]; then
echo "Provide App Store Connect credentials for upload. You can set APPLE_ID and APPLE_PASSWORD (app-specific password) in the environment or save a keychain item named 'TransporterCredentials' to avoid prompts."
read -p "Apple ID (email): " APPLE_ID
read -s -p "App-specific password (will not echo): " APPLE_PASSWORD
echo
fi
fi
echo "Uploading $IPA_PATH to App Store Connect (this may take a while)..."
# Try transporter with a small retry loop
UPLOAD_EXIT=0
MAX_UPLOAD_TRIES=2
for try in $(seq 1 $MAX_UPLOAD_TRIES); do
echo "Transporter upload attempt $try of $MAX_UPLOAD_TRIES..."
if "$ITMS_TRANSPORTER" -m upload -u "$APPLE_ID" -p "$APPLE_PASSWORD" -f "$IPA_PATH"; then
echo "Upload succeeded"
UPLOAD_EXIT=0
break
else
UPLOAD_EXIT=$?
echo "Transporter upload attempt $try failed (exit $UPLOAD_EXIT)"
# small delay before retry
sleep $((try * 2))
fi
done
if [ $UPLOAD_EXIT -ne 0 ]; then
echo "Transporter upload failed after $MAX_UPLOAD_TRIES attempts (exit $UPLOAD_EXIT)."
echo "Opening Transporter app so you can upload the IPA manually..."
# Try to open Transporter GUI with the IPA as argument (may import the file)
if command -v open >/dev/null 2>&1; then
if open -a Transporter "$IPA_PATH" 2>/dev/null; then
echo "Transporter app opened. Please complete the upload there."
else
echo "Failed to open Transporter with the IPA. Opening Transporter app without file..."
open -a Transporter || true
echo "Transporter opened. Please drag the IPA ($IPA_PATH) into the app to upload."
fi
else
echo "Cannot open Transporter GUI: 'open' command not found. Please upload IPA manually: $IPA_PATH"
fi
# Do not exit with error here so the script can finish and user can manually complete upload
fi
else
echo "Transporter not found at $ITMS_TRANSPORTER. Please install Transporter app from the Mac App Store or upload the IPA manually."
echo "IPA path: $IPA_PATH"
fi
echo "All steps completed successfully!"
echo "IPA: $IPA_PATH"
exit 0

View File

@ -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>12.0</string> <string>13.0</string>
</dict> </dict>
</plist> </plist>

View File

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project # Uncomment this line to define a global platform for your project
# platform :ios, '12.0' # platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency. # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true' ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@ -477,9 +477,10 @@
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 = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
@ -495,7 +496,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 7; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = Z778GC45N8; DEVELOPMENT_TEAM = Z778GC45N8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@ -503,7 +504,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 4; MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
PRODUCT_BUNDLE_IDENTIFIER = "com.yuanxuan.makingS--buneng--choolAsignmentApp"; PRODUCT_BUNDLE_IDENTIFIER = "com.yuanxuan.makingS--buneng--choolAsignmentApp";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -524,7 +525,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.yuanxuan.makingSchoolAsignmentApp.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.yuanxuan.makingSchoolAsignmentApp.RunnerTests;
@ -542,7 +543,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = Z778GC45N8; DEVELOPMENT_TEAM = Z778GC45N8;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
@ -559,7 +560,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.yuanxuan.makingSchoolAsignmentApp.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.yuanxuan.makingSchoolAsignmentApp.RunnerTests;
@ -619,10 +620,11 @@
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 = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
@ -671,9 +673,10 @@
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 = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule; SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_OPTIMIZATION_LEVEL = "-O";
@ -689,7 +692,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 7; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = Z778GC45N8; DEVELOPMENT_TEAM = Z778GC45N8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@ -697,7 +700,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 4; MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
PRODUCT_BUNDLE_IDENTIFIER = com.yuanxuan.makingSchoolAsignmentApp; PRODUCT_BUNDLE_IDENTIFIER = com.yuanxuan.makingSchoolAsignmentApp;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
@ -719,7 +722,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements;
CURRENT_PROJECT_VERSION = 7; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = Z778GC45N8; DEVELOPMENT_TEAM = Z778GC45N8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@ -727,7 +730,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 4; MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
PRODUCT_BUNDLE_IDENTIFIER = com.yuanxuan.makingSchoolAsignmentApp; PRODUCT_BUNDLE_IDENTIFIER = com.yuanxuan.makingSchoolAsignmentApp;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";

View File

@ -26,6 +26,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
@ -54,11 +55,13 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES" debugDocumentVersioning = "YES"
debugServiceExtension = "internal" debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES"> allowLocationSimulation = "YES">
<BuildableProductRunnable <BuildableProductRunnable
runnableDebuggingMode = "0"> runnableDebuggingMode = "0">

View File

@ -1,7 +1,7 @@
import Flutter import Flutter
import UIKit import UIKit
@UIApplicationMain @main
@objc class AppDelegate: FlutterAppDelegate { @objc class AppDelegate: FlutterAppDelegate {
override func application( override func application(
_ application: UIApplication, _ application: UIApplication,

View File

@ -7,7 +7,8 @@ class AppStateController extends GetxController with WidgetsBindingObserver {
final appLifecycleState = AppLifecycleState.resumed.obs; final appLifecycleState = AppLifecycleState.resumed.obs;
// 便 // 便
bool get isAppInForeground => appLifecycleState.value == AppLifecycleState.resumed; bool get isAppInForeground =>
appLifecycleState.value == AppLifecycleState.resumed;
@override @override
void onInit() { void onInit() {

View File

@ -0,0 +1,77 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:making_school_asignment_app/common/job/app_version.dart';
import 'package:making_school_asignment_app/common/mixins/request_tool_mixin.dart';
import 'package:yx_app_upgrade_flutter/yx_app_upgrade_flutter.dart';
class UpgradeLogic extends GetxService with RequestToolMixin {
Timer? _timer;
@override
void onReady() {
///
Future.delayed(const Duration(seconds: 10)).then(
(value) => checkUpdate(Get.context!),
);
///
_timer?.cancel();
_timer = Timer.periodic(
const Duration(seconds: 40),
(_) => checkUpdate(Get.context!),
);
super.onReady();
}
@override
void onClose() {
_timer?.cancel();
super.onClose();
}
void checkUpdate(BuildContext context) {
UpgradeAuxiliaryUtils.instance.initiateVersionCheck(
context,
future: (int upType) async {
// upType: 1 Android, 2 iOS
try {
AppVersion? result = await getClient().getLastAppVersion(
'making_school_asignment_app',
upType,
);
if (result != null) return _convertToAppUpgradeVersion(result);
} catch (e, stack) {
debugPrint('fetchLatestUpgradeVersion error: $e\n$stack');
}
return null;
},
);
}
/// UpdateappResult AppUpgradeVersion
AppUpgradeVersion? _convertToAppUpgradeVersion(AppVersion? model) {
if (model == null) return null;
// KB
final filePath =
(model.appFileUrl?.isNotEmpty ?? false) ? model.appFileUrl : null;
return AppUpgradeVersion(
versionName: model.version,
// versionBuildNumber: model.version,
isForce: false,
updateContent: model.description ?? '修复BUG优化体验',
downloadUrl: filePath,
appStoreUrl: filePath,
// apkSize: apkSizeBytes,
supportedMethods: [
AppUpgradeMethod.market,
AppUpgradeMethod.inApp,
AppUpgradeMethod.browser,
],
);
}
}

View File

@ -1,159 +0,0 @@
/*
* @Descripttion: APK
* @version: DownloadApk
* @Author: wy
* @Date: 2020-07-30 15:54:40
* @LastEditors: wangyang 1147192855@qq.com
* @LastEditTime: 2022-08-02 15:12:21
*/
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:making_school_asignment_app/common/utils/utils.dart';
import 'package:path_provider/path_provider.dart';
import 'package:app_installer/app_installer.dart';
import 'package:url_launcher/url_launcher.dart';
import 'UpgradePermission.dart';
import 'model/UpdateAppEvent.dart';
import 'upgradeLogic.dart';
class DownloadApk {
///
static Future<File?> _downloadAndroid(
context, UpdateAppEvent event, UpgradeLogic logic) async {
///
Directory? storageDir = await getExternalStorageDirectory();
final storagePath = storageDir?.path ?? '/';
String version = event.version.replaceAll(".", "-");
File file = new File('$storagePath/${event.appName}v$version.apk');
if (await file.exists()) await file.delete();
if (!file.existsSync()) file.createSync();
try {
///
Response response = await Dio().get(
event.link,
onReceiveProgress: (num received, num total) {
showDownloadProgress(context, received, total, logic);
},
options: Options(
responseType: ResponseType.bytes,
followRedirects: false,
),
);
file.writeAsBytesSync(response.data);
return file;
} catch (e) {
print(e);
// toPrint(val: e);
}
return null;
}
// apk
static Future<bool> installApk(
context, UpdateAppEvent event, UpgradeLogic logic) async {
try {
logic.loadingApk.value = true;
File? _apkFile = await _downloadAndroid(context, event, logic);
if (_apkFile == null) return false;
String _apkFilePath = _apkFile.path;
if (_apkFilePath.isEmpty) {
debugPrint('make sure the apk file is set');
return false;
}
await showDialog<bool>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("未知应用安装权限提示",
style: TextStyle(
fontWeight: FontWeight.bold,
)),
content: const Text("请注意:更新时若出现需要同意“安装未知应用权限”,请同意!!!"),
actions: [
MaterialButton(
color: Theme.of(context).primaryColor,
child: const Text("我已知晓",
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold)),
onPressed: () => Navigator.of(context).pop(),
),
],
);
},
);
await AppInstaller.installApk(_apkFilePath); // APK
// 2
Utils.getInstance().setTimeOut(1, () async {
try {
//
// await SystemNavigator.pop(); // 退APP
var options = ['应用市场更新APP', '浏览器下载并安装APP'];
var uri =
Uri.parse('market://details?id=${event.packageName}'); // URI
if (!await canLaunchUrl(uri))
options.removeAt(0); //
String? option = await UpgradePermission.showCustomModalBottomSheet(
context, options);
if (option == '应用市场更新APP') await launchUrl(uri);
if (option == '浏览器下载并安装APP') await launchUrl(Uri.parse(event.link));
} catch (_) {}
});
print('安装执行完成了..............0.0');
} catch (e) {
print('安装进入报错....');
print(e);
} finally {
logic.loadingApk.value = false;
}
return false;
}
///
static void showDownloadProgress(
context, num received, num total, UpgradeLogic logic) {
if (total != -1) {
double progress = double.parse((received / total).toStringAsFixed(2));
// debugPrint('下载进度$progress');
logic.downloadRatio.value = progress;
}
}
}
class RestartWidget extends StatefulWidget {
final Widget child;
const RestartWidget({super.key, required this.child});
static restartApp(BuildContext context) {
final _RestartWidgetState? state =
context.findAncestorStateOfType<_RestartWidgetState>();
state?.restartApp();
}
@override
State<RestartWidget> createState() => _RestartWidgetState();
}
class _RestartWidgetState extends State<RestartWidget> {
Key key = UniqueKey();
void restartApp() {
setState(() {
key = UniqueKey();
});
}
@override
Widget build(BuildContext context) {
return KeyedSubtree(
key: key,
child: widget.child,
);
}
}

View File

@ -1,223 +0,0 @@
/*
* @Author: wangyang 1147192855@qq.com
* @Date:
* @LastEditors: wangyang 1147192855@qq.com
* @LastEditTime: 2022-09-29 10:11:54
* @FilePath: \marking_app\lib\utils\app_upgrade\UpdateDialog.dart
* @Description: ,`customMade`, koroFileHeader查看配置 : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
/*
* @Descripttion:
* @version:
* @Author: wy
* @Date: 2020-07-30 14:03:28
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-01-12 15:08:43
*/
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
import 'package:get/get.dart';
import 'package:making_school_asignment_app/common/utils/app_upgrade/DownloadApk.dart';
import 'package:making_school_asignment_app/common/utils/app_upgrade/UpgradePermission.dart';
import 'package:making_school_asignment_app/common/utils/app_upgrade/upgradeLogic.dart';
import 'package:making_school_asignment_app/common/utils/utils.dart';
import 'package:making_school_asignment_app/page/global_widget/my_text.dart';
import 'package:percent_indicator/linear_percent_indicator.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';
import '../anti_shake_throttling.dart';
import 'model/UpdateAppEvent.dart';
class UpdateDialog extends Dialog {
final UpdateAppEvent updateAppEvent;
final String deviceInfo;
const UpdateDialog({super.key, required this.updateAppEvent, required this.deviceInfo});
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: isPad() ? ScreenUtil().screenWidth * 0.62 : 319.w,
height: 440.h,
padding: EdgeInsets.symmetric(vertical: 8.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(18.sp),
image: const DecorationImage(
alignment: Alignment.topCenter,
image: AssetImage("assets/images/upgrade_dialog_bgc.png"),
fit: BoxFit.fitWidth,
),
),
child: Column(
children: <Widget>[
Expanded(
child: ListView(
physics: const BouncingScrollPhysics(),
padding: EdgeInsets.fromLTRB(16.w, 0, 16.w, 0),
children: [
Container(
alignment: Alignment.center,
margin: EdgeInsets.only(top:isPad()? 136.h : 128.h, bottom: 10.h),
child: quickText(
updateAppEvent.title,
size: 18.sp,
fontWeight: FontWeight.w600,
color: const Color.fromRGBO(58, 90, 159, 1),
),
),
HtmlWidget(
updateAppEvent.description,
customStylesBuilder: (element) {
return {'color': '#666666', 'font-weight': 'normal', 'text-decoration': 'none'};
},
onLoadingBuilder: (context, element, loadingProgress) => const CircularProgressIndicator(),
renderMode: RenderMode.column,
textStyle: TextStyle(fontSize: 14.sp, color: Colors.black87),
)
],
),
),
DownloadProgress(),
DownloadButton(updateAppEvent, deviceInfo: deviceInfo),
],
),
),
);
}
//
static showUpdateDialog(BuildContext context, UpdateAppEvent updateAppEvent) {
return showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: UpdateDialog(updateAppEvent: updateAppEvent, deviceInfo: updateAppEvent.deviceInfo),
);
},
);
}
}
//
class DownloadProgress extends StatelessWidget {
DownloadProgress({super.key});
final logic = Get.find<UpgradeLogic>();
@override
Widget build(BuildContext context) {
return Obx(() {
var str = (logic.downloadRatio.value * 100).toString().split('.')[0];
if (logic.downloadRatio.value <= 0) return Container();
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
LinearPercentIndicator(
alignment: MainAxisAlignment.center,
width: isPad() ? ScreenUtil().screenWidth * 0.55 : 245.w,
animation: false,
lineHeight: 20.0.h,
percent: logic.downloadRatio.value,
center: quickText(
"$str%",
size: 14.sp,
align: TextAlign.center,
color: Colors.white,
),
linearStrokeCap: LinearStrokeCap.roundAll,
progressColor: Theme.of(context).primaryColor,
),
Container(
height: 6.h,
),
quickText('更新中...', size: 12.sp, fontWeight: FontWeight.w500, color: const Color.fromRGBO(90, 90, 90, 1))
],
);
});
}
}
//
class DownloadButton extends StatelessWidget {
final UpdateAppEvent updateAppEvent;
final String deviceInfo;
DownloadButton(this.updateAppEvent, {required this.deviceInfo, super.key});
final logic = Get.find<UpgradeLogic>();
//
Future<bool> toLaunch(UpdateAppEvent data) async {
var uri = Uri.parse('market://details?id=${updateAppEvent.packageName}');
if (await canLaunchUrl(uri)) {
//
return await launchUrl(uri);
}
//
uri = Uri.parse(data.link);
if (await canLaunchUrl(uri)) return await launchUrl(uri);
return false;
}
@override
Widget build(BuildContext context) {
return Obx(() {
final count = logic.downloadRatio.value;
if (count > 0) return Container();
var primaryColor = Theme.of(context).primaryColor;
if (updateAppEvent.equipment == Equipment.windows) {
return SizedBox(
child: quickText('若没有弹出更新弹框可关闭APP重新打开执行更新...', size: 16.sp),
);
}
return Container(
height: 38.h,
width: isPad() ? ScreenUtil().screenWidth * 0.45 : 245.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(42.h)),
gradient: LinearGradient(colors: [primaryColor, primaryColor.withOpacity(0.7)]),
),
child: MaterialButton(
onPressed: () => easyThrottle('DownloadButton_App_Upgrade', duration: const Duration(milliseconds: 1000), () async {
if (deviceInfo == "android" && updateAppEvent.equipment == Equipment.android) {
//
bool flag = await UpgradePermission(updateAppEvent.deviceInfo).checkPermission(context, updateAppEvent);
if (flag) {
flag = await DownloadApk.installApk(context, updateAppEvent, logic);
if (!flag) {
print('执行到了重置更新按钮的地方....');
logic.downloadRatio.value = 0.0; //
} else {
print('更新成功重新打开APP..............');
RestartWidget.restartApp(context); // APP
}
return;
}
// await FlutterClipboard.copy(updateAppEvent.link);
// setTimeOut(1000, () => ToastUtils.showInfo('下载链接已经复制到设备,可前往浏览器下载安装'));
} else if (deviceInfo == "ios" && updateAppEvent.equipment == Equipment.ios) {
try {
print(updateAppEvent.link);
await launchUrlString(updateAppEvent.link);
} catch (e) {
print('进来更新报错$e');
}
}
// else if (deviceInfo == 'windows' && updateAppEvent.equipment == Equipment.windows) {
// await autoUpdater.setFeedURL(updateAppEvent.link);
// await autoUpdater.checkForUpdates();
// await autoUpdater.setScheduledCheckInterval(0);
// }
}),
child: quickText(!logic.loadingApk.value ? '立即体验' : '正在下载...', size: 16.sp, color: Colors.white, fontWeight: FontWeight.w500),
),
);
});
}
}

View File

@ -1,143 +0,0 @@
/*
* @Descripttion:
* @version: UpgradePermission
* @Author: wy
* @Date: 2020-07-30 15:41:39
* @LastEditors: wangyang 1147192855@qq.com
* @LastEditTime: 2022-08-01 14:08:57
*/
import 'package:app_installer/app_installer.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:url_launcher/url_launcher.dart';
import 'model/UpdateAppEvent.dart';
class UpgradePermission {
final String _flatform;
const UpgradePermission(this._flatform);
///
/// noExecutions
Future<bool> checkPermission(BuildContext context, UpdateAppEvent updateAppEvent, [int? noExecutions]) async {
noExecutions ??= 1;
if (_flatform != 'android') return true; //
var status = await Permission.storage.request();
if (status.isGranted) return true;
if (status.isDenied) {
//
await showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("权限提示"),
content: const Text("无法获取存储权限,请同意获取设备存储权限"),
actions: [
MaterialButton(
color: Theme.of(context).primaryColor,
child: const Text("同意", style: TextStyle(color: Colors.white)),
onPressed: () => Navigator.of(context).pop(),
),
],
);
},
);
if (noExecutions < 2) return checkPermission(context, updateAppEvent, ++noExecutions);
// 2
}
//
bool? res = await showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("权限提示"),
content: const Text("储存权限被永久拒绝,并且不再提示。请前往设置页面同意储存权限"),
actions: [
MaterialButton(
color: Colors.green.shade900,
child: const Text("其它方式更新", style: TextStyle(color: Colors.white)),
onPressed: () => Navigator.of(context).pop(false),
),
MaterialButton(
color: Theme.of(context).primaryColor,
child: const Text("前往设置", style: TextStyle(color: Colors.white)),
onPressed: () => Navigator.of(context).pop(true),
),
],
);
},
);
if (res == null || !res) {
//
// await SystemNavigator.pop(); // 退APP
var options = ['应用市场更新APP', '浏览器下载并安装APP'];
var uri = Uri.parse('market://details?id=${updateAppEvent.packageName}'); // URI
// if (!await canLaunchUrl(uri)) options.removeAt(0); //
String? option = await showCustomModalBottomSheet(context, options);
if (option == '应用市场更新APP') {
if (await canLaunchUrl(uri)) {
await launchUrl(uri);
} else {
await AppInstaller.goStore(updateAppEvent.packageName, 'iOSAppId');
}
}
if (option == '浏览器下载并安装APP') await launchUrl(Uri.parse(updateAppEvent.link));
} else
await openAppSettings();
return false;
}
//
static Future<String?> showCustomModalBottomSheet(context, List<String> options) async {
return showModalBottomSheet<String>(
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (BuildContext context) {
return Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(20.0),
topRight: const Radius.circular(20.0),
),
),
height: MediaQuery.of(context).size.height / 4.0,
child: Column(children: [
SizedBox(
height: 50,
child: Stack(
textDirection: TextDirection.rtl,
children: [
Center(child: Text('选择其它方式更新APP', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0))),
IconButton(icon: Icon(Icons.close), onPressed: () => Navigator.of(context).pop()),
],
),
),
Divider(height: 1.0),
Expanded(
child: ListView.builder(
itemCount: options.length,
itemBuilder: (BuildContext context, int index) {
var name = options[index];
return ListTile(
title: Text(name),
trailing: Icon(name.contains('浏览器') ? Icons.browser_updated_outlined : Icons.local_grocery_store_outlined),
onTap: () => Navigator.of(context).pop(name),
);
},
),
),
]),
);
},
);
}
}

View File

@ -1,83 +0,0 @@
/*
* @Descripttion:
* @version:
* @Author: wy
* @Date: 2020-07-30 14:10:44
* @LastEditors: wangyang 1147192855@qq.com
* @LastEditTime: 2022-09-28 18:20:11
*/
class UpdateAppEvent {
final String version;
final String title;
final String description;
final String link;
final bool upgrade;
final String deviceInfo;
final String appName;
final String packageName;
final Equipment equipment;
num? v;
num? lv;
UpdateAppEvent({
required this.version,
required this.title,
required this.description,
required this.link,
required this.upgrade,
required this.deviceInfo,
required this.appName,
required this.packageName,
this.v,
this.lv,
int type = 0,
}) : equipment = Equipment.values[type];
factory UpdateAppEvent.fromJson(Map json, String localVersion, String deviceInfo, String appName, String packageName,
{String keyStr = "version", String typeName = "packageNameType"}) {
String version = json[keyStr]; //
String? descriptionStr = json["description"];
String newDescription = '系统有更新,请立即下载';
bool upgrade = false;
num? v;
num? lv;
//
if (version != '') {
v = num.parse(version.replaceAll(".", ""));
lv = num.parse(localVersion.replaceAll(".", ""));
// if (lv < v) upgrade = true;
//线线
if (lv != v) upgrade = true;
}
//
if (descriptionStr != null && descriptionStr.isNotEmpty) {
newDescription = descriptionStr;
}
return UpdateAppEvent(
version: version, //
title: json["title"] ?? "发现新版本", // title
description: newDescription, //
link: json["downloadPath"]??'', //
upgrade: upgrade,
deviceInfo: deviceInfo,
appName: appName,
packageName: packageName,
type: json[typeName] ?? 0,
v: v,
lv: lv,
);
}
@override
String toString() {
return "UpdateAppEvent { version: $version, title: $title, description: $description, link: $link}";
}
}
//
enum Equipment { other, android, ios, windows }

View File

@ -1,156 +0,0 @@
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:making_school_asignment_app/common/job/app_version.dart';
import 'package:making_school_asignment_app/common/mixins/request_tool_mixin.dart';
import 'package:making_school_asignment_app/common/store/user_store.dart';
import 'package:making_school_asignment_app/common/utils/permission_describe_util.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:permission_handler/permission_handler.dart';
import 'UpdateDialog.dart';
import 'model/UpdateAppEvent.dart';
class UpgradeLogic extends GetxController with RequestToolMixin {
Rx<bool> showUpgrade = false.obs; //
Rx<bool> loadingApk = false.obs; //
Rx<double> downloadRatio = 0.0.obs; //
// @override
// void onInit() {
// super.onInit();
// }
Future<UpdateAppEvent?> getUpdateAppEvent() async {
//
String deviceInfo;
int deviceType;
if (Platform.isAndroid) {
deviceInfo = "android";
deviceType = 1;
} else if (Platform.isIOS) {
deviceInfo = "ios";
deviceType = 2;
} else if (Platform.isWindows) {
deviceInfo = "windows";
deviceType = 3;
} else {
return null;
}
AppVersion? result = await getClient().getLastAppVersion('making_school_asignment_app', deviceType);
if (result == null) return null;
//
PackageInfo packageInfo = await PackageInfo.fromPlatform();
//
String localVersion = packageInfo.version;
String appName = packageInfo.appName; //
String packageName = packageInfo.packageName; //
// String buildNumber = packageInfo.buildNumber; //
Map json = {
'downloadPath': Platform.isWindows ? '' : result.appFileUrl,
'version': result.version,
'systemType': deviceType,
'description': result.description ?? 'APP新版本更新',
};
return UpdateAppEvent.fromJson(json, localVersion, deviceInfo, appName, packageName, typeName: 'systemType');
}
Future<void> getAppUpgrade(BuildContext context) async {
if (!const bool.fromEnvironment('dart.vm.product')) return;
try {
showUpgrade.value = true;
final names = [
UserStore.to.userDetailInfo.value?.name,
UserStore.to.userDetailInfo.value?.account,
].whereType<String>().where((name) => name.isNotEmpty).toList();
if (names.contains('AppleTester')) return;
UpdateAppEvent? updateAppEvent = await getUpdateAppEvent();
if (updateAppEvent != null && updateAppEvent.upgrade) {
if (Platform.isAndroid) {
DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo = await deviceInfoPlugin.androidInfo;
Permission storagePermission;
if (androidInfo.version.sdkInt >= 33) {
storagePermission = Permission.manageExternalStorage;
} else {
storagePermission = Permission.storage;
}
await PermissionDescribeUtil.instance.toLaunchPermissionRequest(
Get.context ?? context,
title: '储存权限请求',
describe: "为了提供更好的服务需要获取到存储权限用于保存APP升级文件APK进行升级",
permissions: [storagePermission],
);
}
await UpdateDialog.showUpdateDialog(
Get.context ?? context,
updateAppEvent,
);
}
/**
//
String deviceInfo;
int deviceType;
if (Platform.isAndroid) {
deviceInfo = "android";
deviceType = 1;
} else if (Platform.isIOS) {
deviceInfo = "ios";
deviceType = 2;
} else if (Platform.isWindows) {
deviceInfo = "windows";
deviceType = 3;
} else {
return false;
}
if (result != null) {
//
PackageInfo packageInfo = await PackageInfo.fromPlatform();
//
String localVersion = packageInfo.version;
String appName = packageInfo.appName; //
String packageName = packageInfo.packageName; //
// String buildNumber = packageInfo.buildNumber; //
Map json = {'downloadPath': Platform.isWindows ? '' : result.appFileUrl, 'version': result.version, 'systemType': deviceType, 'description': result.description ?? 'APP新版本更新'};
UpdateAppEvent updateAppEvent = UpdateAppEvent.fromJson(json, localVersion, deviceInfo, appName, packageName, typeName: 'systemType');
if (updateAppEvent.upgrade) {
if (Platform.isAndroid) {
DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo = await deviceInfoPlugin.androidInfo;
Permission storagePermission;
if (androidInfo.version.sdkInt >= 33) {
storagePermission = Permission.manageExternalStorage;
} else {
storagePermission = Permission.storage;
}
await PermissionDescribeUtil.instance.toLaunchPermissionRequest(
Get.context ?? context,
title: '储存权限请求',
describe: "为了提供更好的服务需要获取到存储权限用于保存APP升级文件APK进行升级",
permissions: [storagePermission],
);
}
await UpdateDialog.showUpdateDialog(
Get.context ?? context,
updateAppEvent,
);
}
}
*/
} finally {
showUpgrade.value = false;
}
}
}

View File

@ -16,12 +16,13 @@ import 'package:making_school_asignment_app/common/utils/utils.dart';
import 'package:making_school_asignment_app/routes/app_pages.dart'; import 'package:making_school_asignment_app/routes/app_pages.dart';
import 'common/config/request_config.dart'; import 'common/config/request_config.dart';
import 'common/utils/app_upgrade/upgradeLogic.dart'; import 'common/controllers/upgrade_logic.dart';
void main() async { void main() async {
// Get // Get
Get.testMode = true; Get.testMode = true;
FlutterNativeSplash.preserve(widgetsBinding: WidgetsFlutterBinding.ensureInitialized()); FlutterNativeSplash.preserve(
widgetsBinding: WidgetsFlutterBinding.ensureInitialized());
/// ///
await Get.putAsync<StorageService>(() => StorageService.init()); await Get.putAsync<StorageService>(() => StorageService.init());
@ -31,7 +32,8 @@ void main() async {
// Windows // Windows
if (Platform.isWindows) { if (Platform.isWindows) {
String feedURL = '${RequestConfig.imgUrl}infra-app/making_school_asignment_app/3/appcast.xml'; String feedURL =
'${RequestConfig.imgUrl}infra-app/making_school_asignment_app/3/appcast.xml';
await autoUpdater.setFeedURL(feedURL); await autoUpdater.setFeedURL(feedURL);
await autoUpdater.checkForUpdates(); await autoUpdater.checkForUpdates();
await autoUpdater.setScheduledCheckInterval(3600); await autoUpdater.setScheduledCheckInterval(3600);
@ -41,11 +43,14 @@ void main() async {
statusBarColor: Colors.transparent, // statusBarColor: Colors.transparent, //
statusBarIconBrightness: Brightness.light // dark: light statusBarIconBrightness: Brightness.light // dark: light
)); ));
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky, overlays: [SystemUiOverlay.top]); // SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky,
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); // overlays: [SystemUiOverlay.top]); //
await SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); //
runApp(const MyApp()); runApp(const MyApp());
Future.delayed(const Duration(seconds: 3), () => FlutterNativeSplash.remove()); Future.delayed(
const Duration(seconds: 3), () => FlutterNativeSplash.remove());
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
@ -127,9 +132,11 @@ class MyApp extends StatelessWidget {
builder: (context, child) { builder: (context, child) {
return MediaQuery( return MediaQuery(
//Setting font does not change with system font size //Setting font does not change with system font size
data: MediaQuery.of(context).copyWith(textScaler: const TextScaler.linear(1.0)), data: MediaQuery.of(context)
.copyWith(textScaler: const TextScaler.linear(1.0)),
child: Scaffold( child: Scaffold(
body: GestureDetector(onTap: () => Utils.hideKeyboard(), child: child), body: GestureDetector(
onTap: () => Utils.hideKeyboard(), child: child),
), ),
); );
}, },

View File

@ -9,7 +9,6 @@ import 'package:making_school_asignment_app/common/job/user_info.dart';
import 'package:making_school_asignment_app/common/job/user_info_detail.dart'; import 'package:making_school_asignment_app/common/job/user_info_detail.dart';
import 'package:making_school_asignment_app/common/mixins/request_tool_mixin.dart'; import 'package:making_school_asignment_app/common/mixins/request_tool_mixin.dart';
import 'package:making_school_asignment_app/common/store/user_store.dart'; import 'package:making_school_asignment_app/common/store/user_store.dart';
import 'package:making_school_asignment_app/common/utils/app_upgrade/upgradeLogic.dart';
import 'package:making_school_asignment_app/common/utils/storage.dart'; import 'package:making_school_asignment_app/common/utils/storage.dart';
import 'package:making_school_asignment_app/common/utils/toast_utils.dart'; import 'package:making_school_asignment_app/common/utils/toast_utils.dart';
import 'package:making_school_asignment_app/page/home_page/children/my_info.dart'; import 'package:making_school_asignment_app/page/home_page/children/my_info.dart';
@ -24,11 +23,8 @@ class StartPage extends StatefulWidget {
} }
class _StartPageState extends State<StartPage> with RequestToolMixin { class _StartPageState extends State<StartPage> with RequestToolMixin {
Timer? _timer;
Timer? _timerPermission;
DateTime? lastPopTime; DateTime? lastPopTime;
final _pageController = Get.find<PageIndexController>(); final _pageController = Get.find<PageIndexController>();
final _upgradeLogic = Get.find<UpgradeLogic>();
late final List<Widget> _bodyList; late final List<Widget> _bodyList;
@ -38,26 +34,12 @@ class _StartPageState extends State<StartPage> with RequestToolMixin {
// const WorkPage(), // const WorkPage(),
_bodyList = [const HomePage(), const MyInfo()]; _bodyList = [const HomePage(), const MyInfo()];
// APP
WidgetsBinding.instance.addPostFrameCallback((e) {
/// 40APP
if (!_upgradeLogic.showUpgrade.value && UserStore.to.userDetailInfo.value != null)
_upgradeLogic.getAppUpgrade(context);
});
_timer?.cancel();
_timer = Timer.periodic(const Duration(seconds: 40), (e) {
if (Get.currentRoute == Routes.login) return; // APP
if (!_upgradeLogic.showUpgrade.value) _upgradeLogic.getAppUpgrade(context);
});
String? token = UserStore.to.token; String? token = UserStore.to.token;
UserInfo? userInfo = UserStore.to.userInfo.value; UserInfo? userInfo = UserStore.to.userInfo.value;
UserInfoDetail? userInfoDetail = UserStore.to.userDetailInfo.value; UserInfoDetail? userInfoDetail = UserStore.to.userDetailInfo.value;
if ((token?.isNotEmpty ?? false) && userInfo != null) { if ((token?.isNotEmpty ?? false) && userInfo != null) {
// Future.delayed(const Duration(milliseconds: 200)).then((e) {
// Get.toNamed(Routes.home);
// });
// //
if (userInfoDetail == null) UserStore.to.updateUserInfo(); if (userInfoDetail == null) UserStore.to.updateUserInfo();
} else { } else {
@ -72,8 +54,6 @@ class _StartPageState extends State<StartPage> with RequestToolMixin {
@override @override
void dispose() { void dispose() {
Get.delete<PageIndexController>(); Get.delete<PageIndexController>();
_timer?.cancel();
_timerPermission?.cancel();
super.dispose(); super.dispose();
} }
@ -141,7 +121,9 @@ class _StartPageState extends State<StartPage> with RequestToolMixin {
}), }),
), ),
onPopInvokedWithResult: (bool didPop, dynamic result) async { onPopInvokedWithResult: (bool didPop, dynamic result) async {
if (lastPopTime == null || DateTime.now().difference(lastPopTime!) > const Duration(seconds: 1)) { if (lastPopTime == null ||
DateTime.now().difference(lastPopTime!) >
const Duration(seconds: 1)) {
lastPopTime = DateTime.now(); lastPopTime = DateTime.now();
ToastUtils.getFluttertoast( ToastUtils.getFluttertoast(
context: context, context: context,
@ -177,6 +159,23 @@ class PageIndexController extends GetxController with RequestToolMixin {
_pageIndexState = PageIndexState(pageController: PageController()); _pageIndexState = PageIndexState(pageController: PageController());
super.onInit(); super.onInit();
} }
/// TODO 使
// @override
// void onReady() {
// AppUpgradeSimple.instance.checkUpdate(
// context: Get.context!,
// url: 'https://dpc-teacher-api.23544.com/api/infra/AppVersion/Get',
// params: {
// 'appName': 'making_school_asignment_app',
// 'ftuType': 1,
// },
// showNoUpdateToast: true,
// autoDownload: false,
// autoInstall: true,
// );
// super.onReady();
// }
} }
class StartPageIndexBinding extends Bindings { class StartPageIndexBinding extends Bindings {

View File

@ -8,6 +8,7 @@ import 'package:making_school_asignment_app/common/job/annotated_class.dart';
import 'package:making_school_asignment_app/common/utils/utils.dart'; import 'package:making_school_asignment_app/common/utils/utils.dart';
import 'package:making_school_asignment_app/page/global_widget/ReturnToHomepage.dart'; import 'package:making_school_asignment_app/page/global_widget/ReturnToHomepage.dart';
import 'package:making_school_asignment_app/page/home_page/children/annotate_class/widget/annotate_item.dart'; import 'package:making_school_asignment_app/page/home_page/children/annotate_class/widget/annotate_item.dart';
import 'annotate_class_logic.dart'; import 'annotate_class_logic.dart';
import 'widget/completed_annotate_item.dart'; import 'widget/completed_annotate_item.dart';
@ -27,23 +28,21 @@ class _AnnotateClassPageState extends State<AnnotateClassPage> {
String homeworkId = state.homeworkId.value; String homeworkId = state.homeworkId.value;
return AnnotatedRegion( return AnnotatedRegion(
value: const SystemUiOverlayStyle( value: const SystemUiOverlayStyle(
systemNavigationBarColor:Colors.transparent, systemNavigationBarColor: Colors.transparent,
systemNavigationBarDividerColor: null, systemNavigationBarDividerColor: null,
statusBarColor: Colors.transparent, statusBarColor: Colors.transparent,
systemNavigationBarIconBrightness: Brightness.light, systemNavigationBarIconBrightness: Brightness.light,
statusBarIconBrightness: Brightness.light, statusBarIconBrightness: Brightness.light,
statusBarBrightness: Brightness.light, statusBarBrightness: Brightness.light,
), ),
child: OrientationBuilder( child: OrientationBuilder(builder: (BuildContext context, Orientation orientation) {
builder: (BuildContext context, Orientation orientation) {
return Scaffold( return Scaffold(
backgroundColor: const Color.fromRGBO(245, 245, 245, 1), backgroundColor: const Color.fromRGBO(245, 245, 245, 1),
appBar: AppBar( appBar: AppBar(
backgroundColor: Colors.white, backgroundColor: Colors.white,
elevation: 0.3,
title: Obx(() { title: Obx(() {
return Text(state.name.value, return Text(state.name.value, style: TextStyle(fontSize: 14.sp, color: const Color(0xFF333333)));
style:
TextStyle(fontSize: 14.sp, color: const Color(0xFF333333)));
}), }),
centerTitle: true, centerTitle: true,
leading: IconButton( leading: IconButton(
@ -71,8 +70,7 @@ class _AnnotateClassPageState extends State<AnnotateClassPage> {
child: state.completed.value child: state.completed.value
? Utils.isPad() ? Utils.isPad()
? GridView( ? GridView(
gridDelegate: gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, //widget crossAxisCount: 2, //widget
mainAxisSpacing: 10.h, mainAxisSpacing: 10.h,
crossAxisSpacing: 6.w, crossAxisSpacing: 6.w,

View File

@ -94,7 +94,7 @@ class _AnnotateItemState extends State<AnnotateItem> {
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return ShowStudentList( return ShowStudentList(
title: '${widget.item.className ?? ''} ${submitted ? '已提交' : '未提交'}作业学生', title: '${widget.item.className} ${submitted ? '已提交' : '未提交'}作业学生',
studentList: students, studentList: students,
homeworkId: widget.homeworkId, homeworkId: widget.homeworkId,
subject: widget.logic.state.subject, subject: widget.logic.state.subject,
@ -133,7 +133,7 @@ class _AnnotateItemState extends State<AnnotateItem> {
}, },
child: Text( child: Text(
'已交:${widget.item.commitStudentCount}', '已交:${widget.item.commitStudentCount}',
style: TextStyle(fontSize: widget.font - 2.sp, color: const Color(0xFF4CC793)), style: TextStyle(fontSize: widget.font - 2.sp, color: Theme.of(context).primaryColor),
), ),
), ),
SizedBox( SizedBox(
@ -256,12 +256,7 @@ class _AnnotateItemState extends State<AnnotateItem> {
linearGradient: LinearGradient( linearGradient: LinearGradient(
tileMode: TileMode.mirror, tileMode: TileMode.mirror,
stops: const [0.0, 1.0], stops: const [0.0, 1.0],
colors: widget.item.annotateRate / 100 != 1 colors: [Theme.of(context).primaryColor.withOpacity(0.1), Theme.of(context).primaryColor],
? [Theme.of(context).primaryColor.withOpacity(0.1), Theme.of(context).primaryColor]
: [
const Color.fromRGBO(144, 224, 190, 1).withOpacity(0.1),
const Color.fromRGBO(144, 224, 190, 1),
],
), ),
// linearStrokeCap: LinearStrokeCap.butt, // linearStrokeCap: LinearStrokeCap.butt,
// progressColor: Theme.of(context).primaryColor, // progressColor: Theme.of(context).primaryColor,
@ -271,21 +266,21 @@ class _AnnotateItemState extends State<AnnotateItem> {
), ),
ProgressBar( ProgressBar(
title: '客观题正确率:', title: '客观题正确率:',
color: const Color(0xFFADDCA5), color: Theme.of(context).primaryColor,
percent: widget.item.kgtCorrectRate / 100, percent: widget.item.kgtCorrectRate / 100,
marginEdg: EdgeInsets.zero, marginEdg: EdgeInsets.zero,
padingEdg: EdgeInsets.only(top: 8.h, left: 14.r, right: 14.r), padingEdg: EdgeInsets.only(top: 8.h, left: 14.r, right: 14.r),
fontSize: widget.font - 2.sp), fontSize: widget.font - 2.sp),
ProgressBar( ProgressBar(
title: '主观题正确率:', title: '主观题正确率:',
color: const Color(0xFFADDCA5), color: Theme.of(context).primaryColor,
percent: widget.item.zgtCorrectRate / 100, percent: widget.item.zgtCorrectRate / 100,
padingEdg: EdgeInsets.symmetric(horizontal: 10.r), padingEdg: EdgeInsets.symmetric(horizontal: 10.r),
marginEdg: EdgeInsets.only(top: 8.h), marginEdg: EdgeInsets.only(top: 8.h),
fontSize: widget.font - 2.sp), fontSize: widget.font - 2.sp),
ProgressBar( ProgressBar(
title: '总正确率:', title: '总正确率:',
color: const Color(0xFFADDCA5), color: Theme.of(context).primaryColor,
percent: widget.item.correctRate / 100, percent: widget.item.correctRate / 100,
padingEdg: EdgeInsets.symmetric(horizontal: 10.r), padingEdg: EdgeInsets.symmetric(horizontal: 10.r),
marginEdg: EdgeInsets.only(top: 8.h), marginEdg: EdgeInsets.only(top: 8.h),

View File

@ -12,7 +12,7 @@ import 'package:making_school_asignment_app/routes/app_pages.dart';
import 'class_student_logic.dart'; import 'class_student_logic.dart';
class ClassStudentPage extends StatefulWidget { class ClassStudentPage extends StatefulWidget {
const ClassStudentPage({Key? key}) : super(key: key); const ClassStudentPage({super.key});
@override @override
State<ClassStudentPage> createState() => _ClassStudentPageState(); State<ClassStudentPage> createState() => _ClassStudentPageState();
@ -24,8 +24,7 @@ class _ClassStudentPageState extends State<ClassStudentPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return OrientationBuilder( return OrientationBuilder(builder: (BuildContext context, Orientation orientation) {
builder: (BuildContext context, Orientation orientation){
return Scaffold( return Scaffold(
backgroundColor: const Color.fromRGBO(245, 245, 245, 1), backgroundColor: const Color.fromRGBO(245, 245, 245, 1),
appBar: AppBar( appBar: AppBar(
@ -60,110 +59,85 @@ class _ClassStudentPageState extends State<ClassStudentPage> {
? Utils.isPad() ? Utils.isPad()
? GridView( ? GridView(
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, crossAxisCount: 2,
mainAxisSpacing: 0.r, mainAxisSpacing: 0.r,
crossAxisSpacing: 0.r, crossAxisSpacing: 0.r,
childAspectRatio: 556 / 90, childAspectRatio: 556 / 90,
), ),
children: children: List.generate(state.studentList.length, (index) {
List.generate(state.studentList.length, (index) {
StudentItem item = state.studentList[index]; StudentItem item = state.studentList[index];
return InkWell( return InkWell(
onTap: () { onTap: () {
// RouterManager.router.navigateTo(context, // RouterManager.router.navigateTo(context,
// '${RouterManager.jobPersonalDetailPath}?studentId=${item.studentId}&studentName=${Uri.encodeComponent(item.studentName)}'); // '${RouterManager.jobPersonalDetailPath}?studentId=${item.studentId}&studentName=${Uri.encodeComponent(item.studentName)}');
Get.toNamed(Routes.studentWorkDetailPage, Get.toNamed(Routes.studentWorkDetailPage, arguments: {
arguments: {
'studentName': item.name, 'studentName': item.name,
'studentId': item.id, 'studentId': item.id,
'subject':state.subject, 'subject': state.subject,
}); });
}, },
child: Container( child: Container(
padding: EdgeInsets.symmetric(horizontal: 10.r), padding: EdgeInsets.symmetric(horizontal: 10.r),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: borderRadius: BorderRadius.all(Radius.circular(0.r)),
BorderRadius.all(Radius.circular(0.r)),
color: Colors.transparent, color: Colors.transparent,
border: Border(left: BorderSide(width: border: Border(
(index + 1)%2 == 0? 1.r:0,color: const Color(0xFFA5A5A5)),bottom: BorderSide(width: 1.r,color: const Color(0xFFA5A5A5))) left: BorderSide(
), width: (index + 1) % 2 == 0 ? 1.r : 0, color: const Color(0xFFA5A5A5)),
bottom: BorderSide(width: 1.r, color: const Color(0xFFA5A5A5)))),
child: Row( child: Row(
mainAxisAlignment: mainAxisAlignment: MainAxisAlignment.spaceBetween,
MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
item.name, item.name,
style: TextStyle( style: TextStyle(fontSize: 12.sp, color: Theme.of(context).primaryColor),
fontSize: 12.sp,
color: Theme.of(context).primaryColor),
)), )),
state.page == 'answerTrajectory' state.page == 'answerTrajectory'
? Container( ? Container(
height: 20.r, height: 20.r,
width: 70.r, width: 70.r,
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all( border: Border.all(width: 1.r, color: const Color(0xFFB2DA93)),
width: 1.r, borderRadius: BorderRadius.all(Radius.circular(20.r)),
color: const Color(0xFFB2DA93)),
borderRadius: BorderRadius.all(
Radius.circular(20.r)),
), ),
child: Center( child: Center(
child: Text('详情', child: Text('详情',
style: TextStyle( style: TextStyle(fontSize: 10.r, color: const Color(0xFFB2DA93))),
fontSize: 10.r,
color: Color(0xFFB2DA93))),
)) ))
: state.page == 'history' : state.page == 'history'
? Container( ? Container(
height: 20.r, height: 20.r,
width: 70.r, width: 70.r,
decoration: BoxDecoration( decoration: BoxDecoration(
color: color: Theme.of(context).primaryColor,
Theme.of(context).primaryColor, borderRadius: BorderRadius.all(Radius.circular(20.r))),
borderRadius:
BorderRadius.all(
Radius.circular(
20.r))),
child: Center( child: Center(
child: Text( child: Text(
'历史作业', '历史作业',
style: TextStyle( style: TextStyle(fontSize: 10.r, color: Colors.white),
fontSize: 10.r,
color: Colors.white),
)), )),
) )
: item.priorityAnnotate : item.priorityAnnotate
? InkWell( ? InkWell(
onTap: () { onTap: () {
logic.setJobReadLevel( logic.setJobReadLevel(item.id, false);
item.id, false); EasyLoading.show(status: 'loading...');
EasyLoading.show(
status: 'loading...');
}, },
child: Container( child: Container(
height: 20.r, height: 20.r,
width: 80.r, width: 80.r,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: borderRadius: BorderRadius.all(Radius.circular(4.r)),
BorderRadius.all( color: const Color(0xFFEBE4FF),
Radius.circular(
4.r)),
color: const Color(
0xFFEBE4FF),
), ),
child: Row( child: Row(
crossAxisAlignment: crossAxisAlignment: CrossAxisAlignment.start,
CrossAxisAlignment
.start,
children: [ children: [
Padding( Padding(
padding: padding: EdgeInsets.only(left: 3.r),
EdgeInsets.only(
left: 3.r),
child: Image.asset( child: Image.asset(
'assets/images/youx_icon_active.png', 'assets/images/youx_icon_active.png',
width: 14.r, width: 14.r,
@ -171,15 +145,11 @@ class _ClassStudentPageState extends State<ClassStudentPage> {
), ),
), ),
Padding( Padding(
padding: padding: EdgeInsets.only(top: 2.r, left: 4.r),
EdgeInsets.only(
top: 2.r,
left: 4.r),
child: Text( child: Text(
'优先批阅', '优先批阅',
style: TextStyle( style: TextStyle(
fontSize: 10.sp, fontSize: 10.sp, color: Theme.of(context).primaryColor),
color: Theme.of(context).primaryColor),
), ),
), ),
], ],
@ -188,31 +158,21 @@ class _ClassStudentPageState extends State<ClassStudentPage> {
) )
: InkWell( : InkWell(
onTap: () { onTap: () {
logic.setJobReadLevel( logic.setJobReadLevel(item.id, true);
item.id, true); EasyLoading.show(status: 'loading...');
EasyLoading.show(
status: 'loading...');
}, },
child: Container( child: Container(
height: 20.r, height: 20.r,
width: 80.r, width: 80.r,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: borderRadius: BorderRadius.all(Radius.circular(4.r)),
BorderRadius.all( color: const Color(0xFFE1E1E1),
Radius.circular(
4.r)),
color: const Color(
0xFFE1E1E1),
), ),
child: Row( child: Row(
crossAxisAlignment: crossAxisAlignment: CrossAxisAlignment.start,
CrossAxisAlignment
.start,
children: [ children: [
Padding( Padding(
padding: padding: EdgeInsets.only(left: 3.r),
EdgeInsets.only(
left: 3.r),
child: Image.asset( child: Image.asset(
'assets/images/youx_icon_default.png', 'assets/images/youx_icon_default.png',
width: 14.r, width: 14.r,
@ -220,16 +180,11 @@ class _ClassStudentPageState extends State<ClassStudentPage> {
), ),
), ),
Padding( Padding(
padding: padding: EdgeInsets.only(top: 2.r, left: 4.r),
EdgeInsets.only(
top: 2.r,
left: 4.r),
child: Text( child: Text(
'优先批阅', '优先批阅',
style: TextStyle( style: TextStyle(
fontSize: 10.sp, fontSize: 10.sp, color: const Color(0xFF8A9691)),
color: const Color(
0xFF8A9691)),
), ),
), ),
], ],
@ -245,101 +200,79 @@ class _ClassStudentPageState extends State<ClassStudentPage> {
: Padding( : Padding(
padding: EdgeInsets.symmetric(vertical: 14.r, horizontal: 14.r), padding: EdgeInsets.symmetric(vertical: 14.r, horizontal: 14.r),
child: ListView.builder( child: ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: state.studentList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
StudentItem item = state.studentList[index]; StudentItem item = state.studentList[index];
return InkWell( return InkWell(
onTap: () { onTap: () {
// RouterManager.router.navigateTo(context, // RouterManager.router.navigateTo(context,
// '${RouterManager.jobPersonalDetailPath}?studentId=${item.studentId}&studentName=${Uri.encodeComponent(item.studentName)}'); // '${RouterManager.jobPersonalDetailPath}?studentId=${item.studentId}&studentName=${Uri.encodeComponent(item.studentName)}');
Get.toNamed(Routes.studentWorkDetailPage, Get.toNamed(Routes.studentWorkDetailPage, arguments: {
arguments: {
'studentName': item.name, 'studentName': item.name,
'studentId': item.id, 'studentId': item.id,
'subject':state.subject, 'subject': state.subject,
}); });
}, },
child: Container( child: Container(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(vertical: 20.r, horizontal: 15.r),
vertical: 20.r, horizontal: 15.r),
margin: EdgeInsets.only(bottom: 15.r), margin: EdgeInsets.only(bottom: 15.r),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: borderRadius: BorderRadius.all(Radius.circular(10.r)),
BorderRadius.all(Radius.circular(10.r)),
color: Colors.white, color: Colors.white,
), ),
child: Row( child: Row(
mainAxisAlignment: mainAxisAlignment: MainAxisAlignment.spaceBetween,
MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
item.name, item.name,
style: TextStyle( style: TextStyle(fontSize: 12.sp, color: const Color(0xFF4CC793)),
fontSize: 12.sp,
color: const Color(0xFF4CC793)),
)), )),
state.page == 'answerTrajectory' state.page == 'answerTrajectory'
? Container( ? Container(
height: 24.r, height: 24.r,
width: 72.r, width: 72.r,
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all( border: Border.all(width: 1.r, color: const Color(0xFFB2DA93)),
width: 1.r, borderRadius: BorderRadius.all(Radius.circular(20.r)),
color: const Color(0xFFB2DA93)),
borderRadius: BorderRadius.all(
Radius.circular(20.r)),
), ),
child: Center( child: Center(
child: Text('详情', child: Text('详情',
style: TextStyle( style: TextStyle(fontSize: 10.r, color: const Color(0xFFB2DA93))),
fontSize: 10.r,
color: Color(0xFFB2DA93))),
)) ))
: state.page == 'history' : state.page == 'history'
? Container( ? Container(
height: 24.r, height: 24.r,
width: 82.r, width: 82.r,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Color(0xFF4CC793), color: Theme.of(context).primaryColor,
borderRadius: borderRadius: BorderRadius.all(Radius.circular(20.r))),
BorderRadius.all(
Radius.circular(
20.r))),
child: Center( child: Center(
child: Text( child: Text(
'历史作业', '历史作业',
style: TextStyle( style: TextStyle(fontSize: 10.r, color: Colors.white),
fontSize: 10.r,
color: Colors.white),
)), )),
) )
: item.priorityAnnotate : item.priorityAnnotate
? InkWell( ? InkWell(
onTap: () { onTap: () {
logic.setJobReadLevel( logic.setJobReadLevel(item.id, false);
item.id, false); EasyLoading.show(status: 'loading...');
EasyLoading.show(
status: 'loading...');
}, },
child: Container( child: Container(
height: 24.r, height: 24.r,
width: 82.r, width: 82.r,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: borderRadius: BorderRadius.all(Radius.circular(4.r)),
BorderRadius.all( color: const Color(0xFFB7FFE0),
Radius.circular(
4.r)),
color: Color(0xFFB7FFE0),
), ),
child: Row( child: Row(
crossAxisAlignment: crossAxisAlignment: CrossAxisAlignment.start,
CrossAxisAlignment
.start,
children: [ children: [
Padding( Padding(
padding: padding: EdgeInsets.only(left: 3.r),
EdgeInsets.only(
left: 3.r),
child: Image.asset( child: Image.asset(
'assets/images/youx_icon_active.png', 'assets/images/youx_icon_active.png',
width: 14.r, width: 14.r,
@ -347,16 +280,11 @@ class _ClassStudentPageState extends State<ClassStudentPage> {
), ),
), ),
Padding( Padding(
padding: padding: EdgeInsets.only(top: 5.r, left: 4.r),
EdgeInsets.only(
top: 5.r,
left: 4.r),
child: Text( child: Text(
'优先批阅', '优先批阅',
style: TextStyle( style: TextStyle(
fontSize: 10.sp, fontSize: 10.sp, color: const Color(0xFF4CC793)),
color: const Color(
0xFF4CC793)),
), ),
), ),
], ],
@ -365,31 +293,21 @@ class _ClassStudentPageState extends State<ClassStudentPage> {
) )
: InkWell( : InkWell(
onTap: () { onTap: () {
logic.setJobReadLevel( logic.setJobReadLevel(item.id, true);
item.id, true); EasyLoading.show(status: 'loading...');
EasyLoading.show(
status: 'loading...');
}, },
child: Container( child: Container(
height: 24.r, height: 24.r,
width: 82.r, width: 82.r,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: borderRadius: BorderRadius.all(Radius.circular(4.r)),
BorderRadius.all( color: const Color(0xFFE1E1E1),
Radius.circular(
4.r)),
color: const Color(
0xFFE1E1E1),
), ),
child: Row( child: Row(
crossAxisAlignment: crossAxisAlignment: CrossAxisAlignment.start,
CrossAxisAlignment
.start,
children: [ children: [
Padding( Padding(
padding: padding: EdgeInsets.only(left: 3.r),
EdgeInsets.only(
left: 3.r),
child: Image.asset( child: Image.asset(
'assets/images/youx_icon_default.png', 'assets/images/youx_icon_default.png',
width: 14.r, width: 14.r,
@ -397,16 +315,11 @@ class _ClassStudentPageState extends State<ClassStudentPage> {
), ),
), ),
Padding( Padding(
padding: padding: EdgeInsets.only(top: 5.r, left: 4.r),
EdgeInsets.only(
top: 5.r,
left: 4.r),
child: Text( child: Text(
'优先批阅', '优先批阅',
style: TextStyle( style: TextStyle(
fontSize: 10.sp, fontSize: 10.sp, color: const Color(0xFF8A9691)),
color: const Color(
0xFF8A9691)),
), ),
), ),
], ],
@ -418,15 +331,13 @@ class _ClassStudentPageState extends State<ClassStudentPage> {
), ),
); );
}, },
itemCount: state.studentList.length,
), ),
) )
: const MyEmptyWidget(), : const MyEmptyWidget(),
); );
}), }),
); );
} });
);
} }
@override @override

View File

@ -29,9 +29,14 @@ class _FavStudentPageState extends State<FavStudentPage> {
return AlertDialog( return AlertDialog(
insetPadding: EdgeInsets.all(25.r), insetPadding: EdgeInsets.all(25.r),
content: FavoriteStudentDialog( content: FavoriteStudentDialog(
item: item, group: groups, deleteFav: logic.getDelete, confirmDialog: confirmDialog), item: item,
group: groups,
deleteFav: logic.getDelete,
confirmDialog: confirmDialog,
),
contentPadding: const EdgeInsets.all(0), contentPadding: const EdgeInsets.all(0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.r)))); shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.r))),
);
}, },
); );
} }

View File

@ -1,8 +1,6 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get/get_rx/get_rx.dart';
import 'package:making_school_asignment_app/common/config/request_config.dart'; import 'package:making_school_asignment_app/common/config/request_config.dart';
import 'package:making_school_asignment_app/common/job/annotated_class.dart'; import 'package:making_school_asignment_app/common/job/annotated_class.dart';
import 'package:making_school_asignment_app/page/global_widget/MyEmptyWidget.dart'; import 'package:making_school_asignment_app/page/global_widget/MyEmptyWidget.dart';
@ -15,12 +13,8 @@ class FavoriteStudentDialog extends StatefulWidget {
final Function deleteFav; final Function deleteFav;
final Future<bool> Function() confirmDialog; final Future<bool> Function() confirmDialog;
const FavoriteStudentDialog({Key? key, const FavoriteStudentDialog(
required this.item, {super.key, required this.item, required this.group, required this.deleteFav, required this.confirmDialog});
required this.group,
required this.deleteFav,
required this.confirmDialog})
: super(key: key);
@override @override
State<FavoriteStudentDialog> createState() => _FavoriteStudentDialogState(); State<FavoriteStudentDialog> createState() => _FavoriteStudentDialogState();
@ -30,7 +24,7 @@ class _FavoriteStudentDialogState extends State<FavoriteStudentDialog> {
RxInt defaultIndex = 0.obs; RxInt defaultIndex = 0.obs;
RxList<HomeworkFavs> imageList = RxList(); RxList<HomeworkFavs> imageList = RxList();
late PageController pageController; late PageController pageController;
late Rx<HomeworkFavs> currentStudent = Rx(HomeworkFavs('','','',-1,-1,'','',-1,'',-1,'-1','','')); late Rx<HomeworkFavs> currentStudent = Rx(HomeworkFavs('', '', '', -1, -1, '', '', -1, '', -1, '-1', '', ''));
@override @override
void initState() { void initState() {
@ -43,8 +37,7 @@ class _FavoriteStudentDialogState extends State<FavoriteStudentDialog> {
} }
} }
imageList.value = list; imageList.value = list;
defaultIndex.value = defaultIndex.value = list.indexWhere((element) => element.id == widget.item.id);
list.indexWhere((element) => element.id == widget.item.id);
pageController = PageController(initialPage: defaultIndex.value); pageController = PageController(initialPage: defaultIndex.value);
} }
@ -58,14 +51,8 @@ class _FavoriteStudentDialogState extends State<FavoriteStudentDialog> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
width: MediaQuery width: MediaQuery.of(context).size.width,
.of(context) height: MediaQuery.of(context).size.height,
.size
.width,
height: MediaQuery
.of(context)
.size
.height,
padding: EdgeInsets.symmetric(vertical: 10.r, horizontal: 14.r), padding: EdgeInsets.symmetric(vertical: 10.r, horizontal: 14.r),
child: Column( child: Column(
children: [ children: [
@ -74,16 +61,17 @@ class _FavoriteStudentDialogState extends State<FavoriteStudentDialog> {
children: [ children: [
Obx(() { Obx(() {
return Text( return Text(
'${currentStudent.value.className} ${currentStudent.value '${currentStudent.value.className} ${currentStudent.value.studentName}',
.studentName}',
style: TextStyle(fontSize: 12.sp, color: Theme.of(context).primaryColor), style: TextStyle(fontSize: 12.sp, color: Theme.of(context).primaryColor),
); );
}), }),
Expanded(child: Container()), Expanded(child: Container()),
Text( Obx(() {
return Text(
'${currentStudent.value.questionNo}', '${currentStudent.value.questionNo}',
style: TextStyle(fontSize: 12.sp, color: Color(0xFF868686)), style: TextStyle(fontSize: 12.sp, color: const Color(0xFF868686)),
), );
}),
InkWell( InkWell(
onTap: () async { onTap: () async {
bool confim = await widget.confirmDialog(); bool confim = await widget.confirmDialog();
@ -94,8 +82,7 @@ class _FavoriteStudentDialogState extends State<FavoriteStudentDialog> {
if (defaultIndex.value < imageList.length) { if (defaultIndex.value < imageList.length) {
currentStudent.value = imageList[defaultIndex.value]; currentStudent.value = imageList[defaultIndex.value];
} else { } else {
currentStudent.value = currentStudent.value = imageList[defaultIndex.value - 1];
imageList[defaultIndex.value - 1];
defaultIndex = defaultIndex - 1; defaultIndex = defaultIndex - 1;
} }
} else { } else {
@ -133,10 +120,7 @@ class _FavoriteStudentDialogState extends State<FavoriteStudentDialog> {
? Expanded( ? Expanded(
child: Container( child: Container(
color: Colors.white, color: Colors.white,
width: MediaQuery width: MediaQuery.of(context).size.width,
.of(context)
.size
.width,
child: PhotoViewGallery.builder( child: PhotoViewGallery.builder(
scrollPhysics: const BouncingScrollPhysics(), scrollPhysics: const BouncingScrollPhysics(),
builder: (BuildContext context, int index) { builder: (BuildContext context, int index) {
@ -157,16 +141,13 @@ class _FavoriteStudentDialogState extends State<FavoriteStudentDialog> {
), ),
) )
: Padding( : Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(top: MediaQuery.of(context).size.height / 2 - 200.r),
top: MediaQuery
.of(context)
.size
.height / 2 - 200.r),
child: const MyEmptyWidget(), child: const MyEmptyWidget(),
); );
}), }),
Obx(() { Obx(() {
return imageList.isNotEmpty?Padding( return imageList.isNotEmpty
? Padding(
padding: EdgeInsets.symmetric(vertical: 15.r), padding: EdgeInsets.symmetric(vertical: 15.r),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -179,27 +160,18 @@ class _FavoriteStudentDialogState extends State<FavoriteStudentDialog> {
} }
}, },
child: Container( child: Container(
width: width: (MediaQuery.of(context).size.width - 78.r) / 2 - 10.r,
(MediaQuery
.of(context)
.size
.width - 78.r) / 2 - 10.r,
height: 28.r, height: 28.r,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(6.r)), borderRadius: BorderRadius.all(Radius.circular(6.r)),
border: Border.all( border: Border.all(width: 1.r, color: const Color(0xFFCACACA), style: BorderStyle.solid),
width: 1.r,
color: Color(0xFFCACACA),
style: BorderStyle.solid),
), ),
child: Center( child: Center(
child: Text( child: Text(
'上一页', '上一页',
style: TextStyle( style: TextStyle(
fontSize: 10.r, fontSize: 10.r,
color: defaultIndex == 0 color: defaultIndex == 0 ? const Color(0xFFCACACA) : const Color(0xFF505E6E)),
? Color(0xFFCACACA)
: Color(0xFF505E6E)),
)), )),
), ),
), ),
@ -211,18 +183,11 @@ class _FavoriteStudentDialogState extends State<FavoriteStudentDialog> {
} }
}, },
child: Container( child: Container(
width: width: (MediaQuery.of(context).size.width - 78.r) / 2 - 10.r,
(MediaQuery
.of(context)
.size
.width - 78.r) / 2 - 10.r,
height: 28.r, height: 28.r,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(6.r)), borderRadius: BorderRadius.all(Radius.circular(6.r)),
border: Border.all( border: Border.all(width: 1.r, color: const Color(0xFFCACACA), style: BorderStyle.solid),
width: 1.r,
color: Color(0xFFCACACA),
style: BorderStyle.solid),
), ),
child: Center( child: Center(
child: Text( child: Text(
@ -230,14 +195,15 @@ class _FavoriteStudentDialogState extends State<FavoriteStudentDialog> {
style: TextStyle( style: TextStyle(
fontSize: 10.r, fontSize: 10.r,
color: defaultIndex == imageList.length - 1 color: defaultIndex == imageList.length - 1
? Color(0xFFCACACA) ? const Color(0xFFCACACA)
: Color(0xFF505E6E)), : const Color(0xFF505E6E)),
)), )),
), ),
), ),
], ],
), ),
):Container(); )
: Container();
}) })
], ],
), ),

View File

@ -6,7 +6,6 @@ import 'package:get/get.dart';
import 'package:making_school_asignment_app/common/job/marking_models/do_paper_bus.dart'; import 'package:making_school_asignment_app/common/job/marking_models/do_paper_bus.dart';
import 'package:making_school_asignment_app/common/mixins/event_bus_mixin.dart'; import 'package:making_school_asignment_app/common/mixins/event_bus_mixin.dart';
import 'package:making_school_asignment_app/common/utils/anti_shake_throttling.dart'; import 'package:making_school_asignment_app/common/utils/anti_shake_throttling.dart';
import 'package:making_school_asignment_app/common/utils/utils.dart';
import '../configuration_files/index.dart'; import '../configuration_files/index.dart';
@ -33,7 +32,7 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch>
lowerBound: 0, lowerBound: 0,
upperBound: 1, upperBound: 1,
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
)..addListener(toUp); );
_opControllisten = _logicControl.opControl.listen((e) { _opControllisten = _logicControl.opControl.listen((e) {
if (e) { if (e) {
@ -48,37 +47,16 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch>
@override @override
void dispose() { void dispose() {
_animationController _animationController.dispose();
..removeListener(toUp)
..dispose();
_opControllisten?.cancel(); _opControllisten?.cancel();
eventCancel(); eventCancel();
super.dispose(); super.dispose();
} }
void toUp() => toUpState(setState, () {}, mounted);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SafeArea( final Widget barContent = Row(
left: false,
right: false,
top: false,
child: Container(
width: double.infinity,
height: _animationController.value * 70.h,
decoration: BoxDecoration(
color: const Color.fromRGBO(83, 83, 83, 1),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, -2),
),
],
),
child: Row(
children: [ children: [
Expanded( Expanded(
flex: 7, flex: 7,
@ -93,8 +71,8 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch>
child: _buildActionButton( child: _buildActionButton(
'全对', '全对',
Icons.check_circle_outline, Icons.check_circle_outline,
() => easyThrottle('homework_bottom_operation_bar_scoring_related', () => easyThrottle(
() => _homeworkLogic.allPairs(context)), 'homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.allPairs(context)),
isEnabled: !_homeworkLogic.state.submitLoading.value, isEnabled: !_homeworkLogic.state.submitLoading.value,
isPrimary: true, isPrimary: true,
), ),
@ -210,8 +188,33 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch>
), ),
), ),
], ],
);
return SafeArea(
left: false,
right: false,
top: false,
child: AnimatedBuilder(
animation: _animationController,
builder: (context, _) {
return Container(
width: double.infinity,
height: _animationController.value * 70.h,
decoration: BoxDecoration(
color: const Color.fromRGBO(83, 83, 83, 1),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, -2),
), ),
)); ],
),
child: barContent,
);
},
),
);
} }
Widget _buildActionButton( Widget _buildActionButton(

View File

@ -15,8 +15,42 @@ import 'original_manuscript_handwriting/answer_handwriting_view.dart';
part 'dropdown_switch_students_type.g.dart'; part 'dropdown_switch_students_type.g.dart';
/// //
class _Constants {
static const double containerHeight = 30.0;
static const double borderRadius = 4.0;
static const double iconSize = 12.0;
static const double textSize = 14.0;
static const double smallTextSize = 12.0;
static const double tinyTextSize = 4.0;
//
static const Color shadowColor = Color.fromRGBO(46, 91, 255, 0.2);
static const Color backgroundColor = Color.fromRGBO(244, 244, 244, 1);
static const Color textColor = Color.fromRGBO(79, 79, 79, 1);
static const Color iconColor = Color.fromRGBO(104, 103, 103, 1);
static const Color priorityIconDisabledColor = Color.fromRGBO(164, 164, 164, 1);
//
static const double paddingSmall = 2.0;
static const double paddingMedium = 8.0;
static const double paddingLarge = 12.0;
static const double paddingXL = 13.0;
//
static const double shadowBlurRadius = 14.0;
static const double shadowSpreadRadius = 0.5;
static const Offset shadowOffset = Offset(2.0, 2.0);
}
///
/// ///
///
/// -
/// -
/// -
/// -
/// -
class DropdownSwitchStudentsType extends StatelessWidget { class DropdownSwitchStudentsType extends StatelessWidget {
const DropdownSwitchStudentsType({super.key}); const DropdownSwitchStudentsType({super.key});
@ -24,19 +58,20 @@ class DropdownSwitchStudentsType extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final logic = Get.find<HomeworkReviewLogic>(); final logic = Get.find<HomeworkReviewLogic>();
final sateData = logic.state.data; final stateData = logic.state.data;
return Container( return Container(
height: 30.h, height: _Constants.containerHeight.h,
padding: EdgeInsets.only(bottom: 2.r, left: 12.r, right: 12.r), padding: EdgeInsets.only(
bottom: _Constants.paddingSmall.r, left: _Constants.paddingLarge.r, right: _Constants.paddingLarge.r),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: const Color.fromRGBO(46, 91, 255, 0.2), color: _Constants.shadowColor,
offset: Offset(2.w, 2.h), //y轴偏移量 offset: Offset(_Constants.shadowOffset.dx.w, _Constants.shadowOffset.dy.h),
blurRadius: 14, // blurRadius: _Constants.shadowBlurRadius,
spreadRadius: 0.5, // spreadRadius: _Constants.shadowSpreadRadius,
) )
], ],
), ),
@ -44,38 +79,152 @@ class DropdownSwitchStudentsType extends StatelessWidget {
children: [ children: [
Expanded( Expanded(
flex: 2, flex: 2,
child: Container( child: _buildPageDropdown(logic, stateData),
),
SizedBox(width: _Constants.paddingMedium.w),
Expanded(
flex: 3,
child: _buildStudentDropdown(context, logic, stateData),
),
SizedBox(width: _Constants.paddingMedium.w),
const Expanded(
flex: 6,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
$ContinueToReview(), //
$HistoryHomework(), //
$StudentHandwriting(), //
],
),
),
],
),
);
}
///
Widget _buildPageDropdown(HomeworkReviewLogic logic, Rx<DoPaperDetailsResult?> stateData) {
return Container(
padding: EdgeInsets.only(left: 10.w), padding: EdgeInsets.only(left: 10.w),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color.fromRGBO(244, 244, 244, 1), color: _Constants.backgroundColor,
borderRadius: BorderRadius.circular(4.r), borderRadius: BorderRadius.circular(_Constants.borderRadius.r),
), ),
child: Obx(() { child: Obx(() {
final data = stateData.value;
return DropdownButton( return DropdownButton(
isExpanded: true, isExpanded: true,
underline: Container(), underline: Container(),
padding: EdgeInsets.only(right: 4.w), padding: EdgeInsets.only(right: 4.w),
icon: const Icon(Icons.keyboard_arrow_down_rounded), icon: const Icon(Icons.keyboard_arrow_down_rounded),
value: sateData.value?.templateId, value: data?.templateId,
hint: const Text('请选择作业页码'), // hint: const Text('请选择作业页码'),
items: sateData.value?.templateIdKeys?.map((e) { items: data?.templateIdKeys?.map((e) {
return DropdownMenuItem( return DropdownMenuItem(
value: e, value: e,
child: quickText('${sateData.value!.templateIdKeyMap![e]}', child: quickText(
color: const Color.fromRGBO(79, 79, 79, 1), size: 14.sp), '${data.templateIdKeyMap![e]}',
color: _Constants.textColor,
size: _Constants.textSize.sp,
),
); );
}).toList(), }).toList(),
onChanged: (value) { onChanged: (value) => _handlePageChange(logic, value),
);
}),
);
}
///
Widget _buildStudentDropdown(BuildContext context, HomeworkReviewLogic logic, Rx<DoPaperDetailsResult?> stateData) {
return Container(
padding: EdgeInsets.only(left: _Constants.paddingXL.w),
decoration: BoxDecoration(
color: _Constants.backgroundColor,
borderRadius: BorderRadius.circular(_Constants.borderRadius.r),
),
child: Obx(() {
final data = stateData.value;
return DropdownButton(
padding: EdgeInsets.only(right: 4.w),
icon: const Icon(Icons.keyboard_arrow_down_rounded),
value: data?.studentId,
underline: Container(),
isExpanded: true,
items: data?.students.map((student) {
return DropdownMenuItem(
value: student.id,
child: _buildStudentItem(context, student),
);
}).toList(),
hint: const Text('请选择学生'),
onChanged: (value) => _handleStudentChange(logic, value),
);
}),
);
}
///
Widget _buildStudentItem(BuildContext context, PaperStudents student) {
return Stack(
alignment: const FractionalOffset(0, 0.5),
children: [
Container(
padding: EdgeInsets.only(left: 16.w),
child: quickText(
student.name,
size: _Constants.textSize.sp,
color: _Constants.textColor,
),
),
Stack(
alignment: const FractionalOffset(0.52, 0.26),
children: [
Icon(
const IconData(0xe63d, fontFamily: "AlibabaIcon"),
size: _Constants.iconSize.sp,
color: student.isPriority ? Theme.of(context).primaryColor : _Constants.priorityIconDisabledColor,
),
quickText('优先', size: _Constants.tinyTextSize.sp, color: Colors.white),
],
),
],
);
}
///
void _handlePageChange(HomeworkReviewLogic logic, dynamic value) {
if (logic.state.param.value.templateId == value) return; if (logic.state.param.value.templateId == value) return;
var templateIds = logic.state.data.value?.templateIds;
if (value != null && templateIds != null) { final templateIds = logic.state.data.value?.templateIds;
if (value == null || templateIds == null) return;
final templateIdKeyMap = logic.state.data.value?.templateIdKeyMap; final templateIdKeyMap = logic.state.data.value?.templateIdKeyMap;
final answeredAlready = templateIds[value.toString()];
var answeredAlready = templateIds[value.toString()];
if (answeredAlready != null && !answeredAlready) { if (answeredAlready != null && !answeredAlready) {
final currentStudentId = sateData.value?.studentId; _showUnansweredPageToast(logic.state.data.value, value, templateIdKeyMap);
final students = sateData.value?.students ?? []; return;
}
logic.state.param.value.templateId = value;
logic.state.param.value = DoPaperDetailsParam.fromJson(logic.state.param.value.toJson());
}
///
void _handleStudentChange(HomeworkReviewLogic logic, dynamic value) {
if (logic.state.param.value.studentId == value) return;
logic.state.param.value.studentId = value;
logic.state.param.value = DoPaperDetailsParam.fromJson(logic.state.param.value.toJson());
}
///
void _showUnansweredPageToast(DoPaperDetailsResult? data, dynamic value, Map<dynamic, dynamic>? templateIdKeyMap) {
final currentStudentId = data?.studentId;
final students = data?.students ?? [];
// //
PaperStudents? currentStudent; PaperStudents? currentStudent;
@ -91,249 +240,186 @@ class DropdownSwitchStudentsType extends StatelessWidget {
final questionNumber = templateIdKeyMap?[value] ?? '当前选择页'; final questionNumber = templateIdKeyMap?[value] ?? '当前选择页';
ToastUtils.showInfo("$studentName第$questionNumber页,未作答 无需批阅"); ToastUtils.showInfo("$studentName第$questionNumber页,未作答 无需批阅");
return;
}
}
logic.state.param.value.templateId = value;
logic.state.param.value = DoPaperDetailsParam.fromJson(logic.state.param.value.toJson());
// _useSwitchStudentAndType.currentTab.value = _useSwitchStudentAndType.tabs.value.firstWhere((element) => element.pageIndex == value);
},
);
}),
),
),
SizedBox(width: 8.w),
Expanded(
flex: 3,
child: Stack(
children: [
Container(
padding: EdgeInsets.only(left: 13.w),
decoration: BoxDecoration(
color: const Color.fromRGBO(244, 244, 244, 1),
borderRadius: BorderRadius.circular(4.r),
),
child: Obx(() {
return DropdownButton(
padding: EdgeInsets.only(right: 4.w),
icon: const Icon(Icons.keyboard_arrow_down_rounded),
value: sateData.value?.studentId,
underline: Container(),
isExpanded: true,
items: sateData.value?.students.map((e) {
return DropdownMenuItem(
value: e.id,
child: Stack(
alignment: const FractionalOffset(0, 0.62),
children: [
Container(
padding: sateData.value?.studentId != e.id && e.isPriority
? EdgeInsets.only(left: 14.w)
: null,
child: quickText(
e.name,
size: 14.sp,
color: const Color.fromRGBO(79, 79, 79, 1),
),
),
if (e.isPriority && sateData.value?.studentId != e.id)
Stack(
alignment: const FractionalOffset(0.52, 0.24),
children: [
Icon(
const IconData(0xe63d, fontFamily: "AlibabaIcon"),
size: 12.sp,
color: e.isPriority
? Theme.of(context).primaryColor
: const Color.fromRGBO(164, 164, 164, 1),
),
quickText('优先', size: 4.sp, color: Colors.white),
],
),
],
),
);
}).toList(),
hint: const Text('请选择学生'), //
onChanged: (value) {
if (logic.state.param.value.studentId == value) return;
logic.state.param.value.studentId = value;
logic.state.param.value = DoPaperDetailsParam.fromJson(logic.state.param.value.toJson());
},
);
}),
),
Positioned(
left: 2.w,
child: Stack(
alignment: const FractionalOffset(0.52, 0.24),
children: [
Obx(() {
return Icon(
const IconData(0xe63d, fontFamily: "AlibabaIcon"),
size: 12.sp,
color: sateData.value?.priority ?? false
? Theme.of(context).primaryColor
: const Color.fromRGBO(164, 164, 164, 1),
);
}),
quickText('优先', size: 4.sp, color: Colors.white),
],
),
),
],
),
),
// const Expanded(flex: 1, child: SizedBox()),
SizedBox(width: 8.w),
const Expanded(
flex: 5,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
$ContinueToReview(), //
$HistoryHomework(), //
$StudentHandwriting(), //
],
),
),
],
),
);
} }
} }
@swidget @swidget
Widget $studentHandwriting(BuildContext context) { Widget $studentHandwriting(BuildContext context) {
final logic = Get.find<HomeworkReviewLogic>(); final logic = Get.find<HomeworkReviewLogic>();
final sateData = Get.find<HomeworkReviewLogic>().state.data; final stateData = logic.state.data;
return InkWell( return InkWell(
onTap: () => easyThrottle( onTap: () => easyThrottle(
'SHOW_ANSWER_HANDWRITING', 'SHOW_ANSWER_HANDWRITING',
() async { () async {
var homeworkId = logic.state.param.value.homeworkId; final data = stateData.value;
var studentId = sateData.value?.studentId; final studentId = data?.studentId;
if (studentId == null) return; if (studentId == null) return;
var templateIdKeyMap = sateData.value?.templateIdKeyMap;
final homeworkId = logic.state.param.value.homeworkId;
final templateId = data?.templateId;
final templateIdKeyMap = data?.templateIdKeyMap;
int? pageNum; int? pageNum;
var templateId = sateData.value?.templateId;
if (templateIdKeyMap != null && templateId != null) { if (templateIdKeyMap != null && templateId != null) {
pageNum = templateIdKeyMap[templateId]; pageNum = templateIdKeyMap[templateId];
} }
await showAnswerHandwriting(context,
homeworkId: homeworkId, studentId: studentId, templateId: templateId, pageNum: pageNum); await showAnswerHandwriting(
context,
homeworkId: homeworkId,
studentId: studentId,
templateId: templateId,
pageNum: pageNum,
);
ToastUtils.dismiss(); ToastUtils.dismiss();
}, },
), ),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Icon(Icons.edit_outlined, size: 12.sp, color: Theme.of(context).primaryColor.withOpacity(0.8)), Icon(
Icons.edit_outlined,
size: _Constants.iconSize.sp,
color: Theme.of(context).primaryColor.withOpacity(0.8),
),
SizedBox(width: 1.w), SizedBox(width: 1.w),
quickText( quickText(
'学生笔迹', '学生笔迹',
size: 12.sp, size: _Constants.smallTextSize.sp,
decoration: TextDecoration.underline, decoration: TextDecoration.underline,
color: Theme.of(context).primaryColor.withOpacity(0.9), color: Theme.of(context).primaryColor.withOpacity(0.9),
), ),
], ],
)); ),
);
} }
@swidget @swidget
Widget $continueToReview(BuildContext context, {bool isFloatingAction = false}) { Widget $continueToReview(BuildContext context, {bool isFloatingAction = false}) {
final logic = Get.find<HomeworkReviewLogic>(); final logic = Get.find<HomeworkReviewLogic>();
final sateData = Get.find<HomeworkReviewLogic>().state.data; final stateData = logic.state.data;
return Obx(() { return Obx(() {
var data = sateData.value; final data = stateData.value;
var param = logic.state.param.value;
int? submitCount = data?.submitCount; // //
int? annotatedCount = data?.annotatedCount; // if (!_shouldShowContinueReview(data)) {
// || (submitCount == annotatedCount || (param.templateId == null && param.studentId == null)) return const SizedBox();
// if (data == null || (data.needAnnotate ? true : data.totalUnAnnotateCount <= 0) ) return const SizedBox(); }
if (data == null ||
(data.needAnnotate void onPressed() => easyThrottle(
? (data.continuePage == null ? true : data.continuePage!.templateId == data.templateId)
: data.totalUnAnnotateCount <= 0)) return const SizedBox();
callFun() => easyThrottle(
'DO_PAPERS_JOB_CONTINUE_TO_REVIEW', 'DO_PAPERS_JOB_CONTINUE_TO_REVIEW',
() { () {
var param = logic.state.param.value; final param = logic.state.param.value;
param.templateId = null; param.templateId = null;
param.studentId = null; param.studentId = null;
logic.state.param.value = DoPaperDetailsParam.fromJson(param.toJson()); logic.state.param.value = DoPaperDetailsParam.fromJson(param.toJson());
}, },
); );
if (isFloatingAction) { if (isFloatingAction) {
return FloatingActionButton( return FloatingActionButton(
elevation: 8, elevation: 8,
tooltip: "继续批阅", tooltip: "继续批阅",
backgroundColor: Colors.white, backgroundColor: Colors.white,
onPressed: callFun, onPressed: onPressed,
child: Icon(Icons.flip_camera_android_outlined, size: 20.sp, color: Theme.of(context).primaryColor), child: Icon(
Icons.flip_camera_android_outlined,
size: 20.sp,
color: Theme.of(context).primaryColor,
),
); );
} }
return SizedBox(
child: InkWell( return InkWell(
onTap: callFun, onTap: onPressed,
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Icon(Icons.flip_camera_android_outlined, size: 12.sp, color: Theme.of(context).primaryColor.withOpacity(0.8)), Icon(
Icons.flip_camera_android_outlined,
size: _Constants.iconSize.sp,
color: Theme.of(context).primaryColor.withOpacity(0.8),
),
SizedBox(width: 1.w), SizedBox(width: 1.w),
quickText( quickText(
'继续批阅', '继续批阅',
size: 12.sp, size: _Constants.smallTextSize.sp,
decoration: TextDecoration.underline, decoration: TextDecoration.underline,
color: Theme.of(context).primaryColor.withOpacity(0.9), color: Theme.of(context).primaryColor.withOpacity(0.9),
), ),
SizedBox(width: 2.w), SizedBox(width: 2.w),
], ],
), ),
)); );
}); });
} }
// ///
bool _shouldShowContinueReview(DoPaperDetailsResult? data) {
if (data == null) return false;
if (data.needAnnotate) {
return data.continuePage == null || data.continuePage!.templateId != data.templateId;
} else {
return data.totalUnAnnotateCount > 0;
}
}
///
@swidget @swidget
Widget $historyHomework(BuildContext context) { Widget $historyHomework(BuildContext context) {
final sateData = Get.find<HomeworkReviewLogic>().state.data; final logic = Get.find<HomeworkReviewLogic>();
return SizedBox( final stateData = logic.state.data;
child: InkWell(
onTap: () => easyThrottle('DO_PAPERS_JOB_HISTORICAL_HOMEWORK', () { return InkWell(
int? studentId = sateData.value?.studentId; onTap: () => easyThrottle(
if (kDebugMode) print(studentId); 'DO_PAPERS_JOB_HISTORICAL_HOMEWORK',
if (studentId == null || (sateData.value?.students.isEmpty ?? true)) return; () => _navigateToStudentHistory(logic, stateData.value),
PaperStudents? currentStudent; ),
try {
currentStudent = sateData.value!.students.firstWhere((e) => e.id == studentId);
} catch (e) {
return;
}
var theState = Get.find<HomeworkReviewLogic>().state;
Get.toNamed(Routes.studentWorkDetailPage, arguments: {
'studentId': studentId,
'studentName': currentStudent.name,
'subject': theState.param.value.subject
});
}),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Icon(Icons.location_history, size: 12.sp, color: const Color.fromRGBO(104, 103, 103, 1)), Icon(
Icons.location_history,
size: _Constants.iconSize.sp,
color: _Constants.iconColor,
),
SizedBox(width: 1.w), SizedBox(width: 1.w),
quickText( quickText(
'历史作业', '历史作业',
size: 12.sp, size: _Constants.smallTextSize.sp,
decoration: TextDecoration.underline, decoration: TextDecoration.underline,
color: Theme.of(context).primaryColor.withOpacity(0.8), color: Theme.of(context).primaryColor.withOpacity(0.8),
), ),
], ],
), ),
),
); );
} }
///
void _navigateToStudentHistory(HomeworkReviewLogic logic, DoPaperDetailsResult? data) {
final studentId = data?.studentId;
final students = data?.students ?? [];
if (kDebugMode) print('Student ID: $studentId');
if (studentId == null || students.isEmpty) return;
PaperStudents? currentStudent;
try {
currentStudent = students.firstWhere((e) => e.id == studentId);
} catch (e) {
if (kDebugMode) print('Student not found: $e');
return;
}
Get.toNamed(Routes.studentWorkDetailPage, arguments: {
'studentId': studentId,
'studentName': currentStudent.name,
'subject': logic.state.param.value.subject,
});
}

View File

@ -106,7 +106,8 @@ Widget $questionNumberScrollView({
/// ///
// if (sateData.panQuestView == false) sateData.slide.value = scrollControllerNum.offset; // if (sateData.panQuestView == false) sateData.slide.value = scrollControllerNum.offset;
final currentMatrix = controller.zoomController.value; final currentMatrix = controller.zoomController?.value;
if (currentMatrix == null) return;
// //
final newMatrix = Matrix4.copy(currentMatrix) final newMatrix = Matrix4.copy(currentMatrix)
@ -116,7 +117,7 @@ Widget $questionNumberScrollView({
currentMatrix.getTranslation().z, // Z currentMatrix.getTranslation().z, // Z
)); ));
controller.zoomController.value = newMatrix; controller.zoomController?.value = newMatrix;
}); });
var listenVal = sateData.slide.listen((e) { var listenVal = sateData.slide.listen((e) {
if (sateData.panQuestView != null && sateData.panQuestView == true && e != scrollControllerNum.offset) { if (sateData.panQuestView != null && sateData.panQuestView == true && e != scrollControllerNum.offset) {
@ -132,30 +133,24 @@ Widget $questionNumberScrollView({
// var actualImgHeight = useImageInfo.value?.actualImgHeight ?? 0; // // var actualImgHeight = useImageInfo.value?.actualImgHeight ?? 0; //
print("图片高度:${usePiddingTop.value}"); // / I/O
return SingleChildScrollView( return ListView.builder(
controller: scrollControllerNum, controller: scrollControllerNum,
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
padding: EdgeInsets.only(top: usePiddingTop.value > 0 ? usePiddingTop.value : 0), padding: EdgeInsets.only(top: usePiddingTop.value > 0 ? usePiddingTop.value : 0),
scrollDirection: Axis.vertical, // itemCount: studentQuestions.length,
child: SizedBox( itemBuilder: (context, index) {
height: (actualImgHeight.value ?? 0) * useZoom.value, final e = studentQuestions[index];
child: Column( return $ScoringQuestionsView(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: studentQuestions
.map((e) => $ScoringQuestionsView(
key: Key('${sateData.data.value?.templateId}_${sateData.data.value?.studentId}_${e.questionNo}'), key: Key('${sateData.data.value?.templateId}_${sateData.data.value?.studentId}_${e.questionNo}'),
sateData: sateData, sateData: sateData,
item: e, item: e,
logic: controller, logic: controller,
scaleRatio: sateZoomData.zoomFile.value!.scaleRatio, scaleRatio: sateZoomData.zoomFile.value!.scaleRatio,
initScale: useZoom.value, initScale: useZoom.value,
)) );
.toList(), },
),
),
); );
} }
@ -182,7 +177,6 @@ Widget $scoringQuestionsView(
useEffect(() { useEffect(() {
/// ///
studentScoreListener() { studentScoreListener() {
print(item.toJson());
item.studentScore = studentScore.value; item.studentScore = studentScore.value;
try { try {
var theVal = sateData.studentQuestions.value?.firstWhere((e) => e.questionNo == item.questionNo); var theVal = sateData.studentQuestions.value?.firstWhere((e) => e.questionNo == item.questionNo);

View File

@ -1,6 +1,4 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:flutter/cupertino.dart' hide TransformationController; import 'package:flutter/cupertino.dart' hide TransformationController;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -17,6 +15,7 @@ import 'package:making_school_asignment_app/common/utils/cached_network_img.dart
import 'package:making_school_asignment_app/common/utils/toast_utils.dart'; import 'package:making_school_asignment_app/common/utils/toast_utils.dart';
import 'package:making_school_asignment_app/page/global_widget/my_text.dart'; import 'package:making_school_asignment_app/page/global_widget/my_text.dart';
import 'package:making_school_asignment_app/page/home_page/children/homework_review/configuration_files/index.dart'; import 'package:making_school_asignment_app/page/home_page/children/homework_review/configuration_files/index.dart';
import 'package:vector_math/vector_math_64.dart' show Vector3, Matrix4;
import 'package:zoom_widget/zoom_widget.dart'; import 'package:zoom_widget/zoom_widget.dart';
import '../configuration_files/zoom_logic.dart'; import '../configuration_files/zoom_logic.dart';
@ -47,10 +46,10 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
if (zoomFileModel == null) { if (zoomFileModel == null) {
/// ///
return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) { return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
WidgetsBinding.instance.addPostFrameCallback((_) => zoomState.zoomFile.value = ZoomFileModel( WidgetsBinding.instance.addPostFrameCallback(
viewWidth: constraints.maxWidth, (_) => zoomState.zoomFile.value =
viewHeight: constraints.maxHeight, ZoomFileModel(viewWidth: constraints.maxWidth, viewHeight: constraints.maxHeight),
)); );
return const SizedBox(); return const SizedBox();
}); });
} }
@ -299,25 +298,11 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
return -1; return -1;
} }
int _activePointers = 0;
Timer? timer;
// //
static const int timeoutDuration = 300; static const int timeoutDuration = 300;
/// ///
void toTimer(ValueNotifier<List<dynamic>> vnHandWritings) { /// build useRef
timer?.cancel();
timer = Timer(const Duration(milliseconds: timeoutDuration), () {
if (_activePointers > 2) {
_activePointers = 0;
if (vnHandWritings.value.isNotEmpty && vnHandWritings.value.last != null) {
vnHandWritings.value.add(null); // 线
sateData.handwritings = vnHandWritings.value; //
}
}
});
}
/// ///
Offset? getLastDrop(List<dynamic> vals, int index) { Offset? getLastDrop(List<dynamic> vals, int index) {
@ -338,12 +323,40 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// HookWidget 使
final activePointers = useRef<int>(0);
final timerRef = useRef<Timer?>(null);
void startTimerForHandwriting(ValueNotifier<List<dynamic>> vnHandWritings) {
timerRef.value?.cancel();
timerRef.value = Timer(const Duration(milliseconds: timeoutDuration), () {
if (activePointers.value > 2) {
activePointers.value = 0;
if (vnHandWritings.value.isNotEmpty && vnHandWritings.value.last != null) {
vnHandWritings.value.add(null); // 线
sateData.handwritings = vnHandWritings.value; //
}
}
});
}
final theMaxHeight = useState<double>(maxHeight); final theMaxHeight = useState<double>(maxHeight);
useValueChanged<double, void>(maxHeight, (oldValue, __) => theMaxHeight.value = maxHeight); useValueChanged<double, void>(maxHeight, (oldValue, __) => theMaxHeight.value = maxHeight);
var zoomKey = useState<GlobalKey>(GlobalKey()); var zoomKey = useState<GlobalKey>(GlobalKey());
useValueChanged<int?, void>(zoomState.zoomFile.value?.templateId, (old, __) { useValueChanged<int?, void>(zoomState.zoomFile.value?.templateId, (old, __) {
// Zoom组件
WidgetsBinding.instance.addPostFrameCallback((_) {
zoomKey.value = GlobalKey(); zoomKey.value = GlobalKey();
//
WidgetsBinding.instance.addPostFrameCallback((_) {
final double offsetY = (zoomState.zoomFile.value?.imageHeightOffsetStart ?? 0).toDouble();
if (offsetY > 0) {
logic.zoomController?.value = Matrix4.identity()..setTranslationRaw(0, offsetY, 0);
}
});
});
}); });
var vnHandWritings = useValueNotifier<List<dynamic>>([]); var vnHandWritings = useValueNotifier<List<dynamic>>([]);
@ -477,7 +490,8 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
}); });
return () { return () {
_activePointers = 0; activePointers.value = 0;
timerRef.value?.cancel();
eventCancel(); eventCancel();
}; };
}, []); }, []);
@ -491,46 +505,37 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
// //
// //
_activePointers = _activePointers + 1; activePointers.value = activePointers.value + 1;
/// ///
toTimer(vnHandWritings); startTimerForHandwriting(vnHandWritings);
sateData.panQuestView = true; sateData.panQuestView = true;
}, },
onPointerUp: (PointerUpEvent details) { onPointerUp: (PointerUpEvent details) {
// //
// activePointers--; // activePointers--;
// globalPosition = null; // globalPosition = null;
if (_activePointers > 0) { if (activePointers.value > 0) {
_activePointers = _activePointers - 1; activePointers.value = activePointers.value - 1;
} }
print("---进入onPointerUp $_activePointers"); //
timer?.cancel(); timerRef.value?.cancel();
if (!annotationState.pen.value) return; if (!annotationState.pen.value) return;
vnHandWritings.value.add(null); // 线 vnHandWritings.value.add(null); // 线
sateData.handwritings = vnHandWritings.value; // sateData.handwritings = vnHandWritings.value; //
}, },
onPointerMove: (PointerMoveEvent event) { onPointerMove: (PointerMoveEvent event) {
print("进入onPointerMove $_activePointers"); //
if (_activePointers != 1) return; if (activePointers.value != 1) return;
toTimer(vnHandWritings); startTimerForHandwriting(vnHandWritings);
if (!annotationState.pen.value) return; if (!annotationState.pen.value) return;
Offset localPosition = event.localPosition; // Offset localPosition = event.localPosition; //
var zoomFile = zoomState.zoomFile.value!;
// var imageHeightOffsetStart = zoomFile.imageHeightOffsetStart??0;
var imageHeightOffsetStart = zoomState.zoomFile.value!.getZoomFileOffsetStart(zoomState.initScale.value ?? 1);
// print("位置:$localPosition; 图片所在位置:$imageHeightOffsetStart");
if (imageHeightOffsetStart == 0) return;
var dy = localPosition.dy; // 使
// print(zoomFile.getZoomFileHeightOffsetEnd(zoomState.initScale.value ?? 1)); final Matrix4 matrix = logic.zoomController?.value ?? Matrix4.identity(); //
if (dy < imageHeightOffsetStart || dy > zoomFile.getZoomFileHeightOffsetEnd(zoomState.initScale.value ?? 1)) { final double theScale = matrix.getMaxScaleOnAxis();
return; //
}
var theScale = zoomState.initScale.value ?? 1;
// if (theScale != 1) { // if (theScale != 1) {
// print("PPPPPPPPPPPPPPPPPPPPPPPP ${(zoomFile.imageHeightOffsetStart ?? 0)}"); // print("PPPPPPPPPPPPPPPPPPPPPPPP ${(zoomFile.imageHeightOffsetStart ?? 0)}");
// localPosition = Offset(localPosition.dx, localPosition.dy + (zoomFile.imageHeightOffsetStart ?? 0)); // localPosition = Offset(localPosition.dx, localPosition.dy + (zoomFile.imageHeightOffsetStart ?? 0));
@ -549,26 +554,51 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
// } // }
// (dy / theScale) - (max(0, imageHeightOffsetStart) / theScale) + ((sateData.zoomOffset?.dy.abs() ?? 0) / theScale), // (dy / theScale) - (max(0, imageHeightOffsetStart) / theScale) + ((sateData.zoomOffset?.dy.abs() ?? 0) / theScale),
var leftOffset = zoomFile.getZoomFileOffsetStartWidth(theScale); // ->
var topOffset = max(0, imageHeightOffsetStart); // final inv = Matrix4.inverted(matrix);
// final v = inv.transform3(Vector3(localPosition.dx, localPosition.dy, 0));
// localPosition = Offset(v.x, v.y);
// 使// /// -> /
final translation = logic.zoomController.value.getTranslation(); /// Matrix4 4x4
final panDx = translation.x; /// storage[12] x storage[13] y storage[14] z
final panDy = translation.y; /// Matrix4 index = col * 4 + row 4 3 121314
/// theScale == 1.0 && storage[12] == 0.0 && storage[13] == 0.0 /
final double tx = matrix.storage[12];
final double ty = matrix.storage[13];
if (theScale == 1.0 && tx == 0.0 && ty == 0.0) {
// identity
} else {
final inv = Matrix4.inverted(matrix);
final v = inv.transform3(Vector3(localPosition.dx, localPosition.dy, 0));
localPosition = Offset(v.x, v.y);
}
// // 0..maxWidth0..actualHeight
final correctedDx = (localPosition.dx - leftOffset - panDx) / theScale; if (localPosition.dy < 0 || localPosition.dy > actualHeight) return;
final correctedDy = (dy - topOffset - panDy) / theScale; if (localPosition.dx < 0 || localPosition.dx > maxWidth) return;
localPosition = Offset(correctedDx, correctedDy);
/// ///
if (Platform.isAndroid) { /// -
/// -
var lastDrop = getLastDrop(vnHandWritings.value, vnHandWritings.value.length - 1); var lastDrop = getLastDrop(vnHandWritings.value, vnHandWritings.value.length - 1);
if (lastDrop != null && if (lastDrop != null) {
((lastDrop.dx - localPosition.dx).abs() > 65 || (lastDrop.dy - localPosition.dy).abs() > 65)) { const double baseJump = 65.0;
/// X点和上一个x点相差 10 final double jumpThreshold = baseJump / theScale;
return; if ((lastDrop.dx - localPosition.dx).abs() > jumpThreshold ||
(lastDrop.dy - localPosition.dy).abs() > jumpThreshold) {
return; // /
}
}
//
if (lastDrop != null) {
const double baseDist = 1.6; //
final double minDist = baseDist / theScale;
final double dx = localPosition.dx - lastDrop.dx;
final double dy = localPosition.dy - lastDrop.dy;
if ((dx * dx + dy * dy) < (minDist * minDist)) {
return; //
} }
} }
// print("最终位置 $localPosition"); // print("最终位置 $localPosition");
@ -576,9 +606,7 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
sateData.handwritings = vnHandWritings.value; sateData.handwritings = vnHandWritings.value;
}, },
child: Obx(() { child: Obx(() {
var isPen = annotationState.pen.value;
var showZgtAnnotate = sateData.data.value?.showZgtAnnotate; var showZgtAnnotate = sateData.data.value?.showZgtAnnotate;
return Container( return Container(
height: double.infinity, height: double.infinity,
width: double.infinity, width: double.infinity,
@ -586,22 +614,7 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
child: IgnorePointer( child: IgnorePointer(
// Zoom // Zoom
ignoring: !annotationState.gestureMove.value, ignoring: !annotationState.gestureMove.value,
child: child: Zoom(
// ZoomView(
// key: zoomKey.value,
// viewWidth: maxWidth,
// viewHeight: maxHeight,
// imageDisplayHeight: actualHeight,
// url: sateData.data.value!.zgtAnswer,
// onScale: logic.zoomLogic.onScaleUpdate,
// // onTrans: (offset) {
// // print("偏移位置:$offset");
// // },
// onContentOffset: logic.zoomLogic.onPanUpPosition,
// ),
Obx(
() => Zoom(
key: zoomKey.value, key: zoomKey.value,
// initTotalZoomOut: true, // // initTotalZoomOut: true, //
zoomSensibility: 0.05, zoomSensibility: 0.05,
@ -611,13 +624,13 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
maxZoomHeight: actualHeight, maxZoomHeight: actualHeight,
canvasColor: Colors.transparent, canvasColor: Colors.transparent,
doubleTapScaleChange: 1, doubleTapScaleChange: 1,
// initPosition: initPosition.value, initScale: 1,
// initScale: logic.zoomLogic.zoomState.initScale.value ?? 1,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
onScaleUpdate: (double scale, double zoom) => logic.zoomLogic.onScaleUpdate(zoom), // onScaleUpdate: (double scale, double zoom) => logic.zoomLogic.onScaleUpdate(zoom),
onPositionUpdate: logic.zoomLogic.onPanUpPosition, // onPositionUpdate: logic.zoomLogic.onPanUpPosition,
transformationController: logic.zoomController, transformationController: logic.zoomController,
child: Stack( child: Obx(() {
return Stack(
children: [ children: [
$TheCachedNetworkImage( $TheCachedNetworkImage(
imgWidth: maxWidth, imgWidth: maxWidth,
@ -627,7 +640,8 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
RepaintBoundary( RepaintBoundary(
key: logic.pictureOverviewKey, key: logic.pictureOverviewKey,
child: CustomPaint( child: CustomPaint(
// isComplex: true, isComplex: true,
willChange: true,
size: Size(maxWidth, actualHeight), size: Size(maxWidth, actualHeight),
foregroundPainter: DrawingPainter(ctrl: vnHandWritings), foregroundPainter: DrawingPainter(ctrl: vnHandWritings),
// child: $TheCachedNetworkImage( // child: $TheCachedNetworkImage(
@ -644,9 +658,10 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
), ),
), ),
], ],
);
}),
), ),
), ),
)),
); );
}), }),
); );
@ -659,19 +674,40 @@ class DrawingPainter extends CustomPainter {
final Paint paintBrush = Paint() final Paint paintBrush = Paint()
..color = Colors.red ..color = Colors.red
..strokeCap = StrokeCap.round ..strokeCap = StrokeCap.round
..strokeWidth = 0.7.sp; ..strokeJoin = StrokeJoin.round
..isAntiAlias = true
..strokeWidth = 0.7.sp
..style = PaintingStyle.stroke;
DrawingPainter({required this.ctrl}) : super(repaint: ctrl); DrawingPainter({required this.ctrl}) : super(repaint: ctrl);
@override @override
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {
var points = ctrl.value; final List<dynamic> points = ctrl.value;
var pointsLength = points.length; if (points.isEmpty) return;
for (int i = 0; i < pointsLength; i++) {
Offset? offsetData = points[i]; Path path = Path();
Offset? nextOffsetData = pointsLength - 1 == i ? null : points[i + 1]; Offset? previous;
if (offsetData != null && nextOffsetData != null) { for (int i = 0; i < points.length; i++) {
canvas.drawLine(offsetData, nextOffsetData, paintBrush); final Offset? current = points[i] as Offset?;
if (current == null) {
//
if (path.computeMetrics().isNotEmpty) {
canvas.drawPath(path, paintBrush);
} }
path = Path();
previous = null;
continue;
}
if (previous == null) {
path.moveTo(current.dx, current.dy);
} else {
path.lineTo(current.dx, current.dy);
}
previous = current;
}
//
if (path.computeMetrics().isNotEmpty) {
canvas.drawPath(path, paintBrush);
} }
} }

View File

@ -83,7 +83,7 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
StreamSubscription<TestQuestionsImageInfo?>? imageScaleZoomStream; StreamSubscription<TestQuestionsImageInfo?>? imageScaleZoomStream;
late final zoomWidget.TransformationController zoomController; zoomWidget.TransformationController? zoomController;
@override @override
void onInit() { void onInit() {
@ -105,15 +105,6 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
// //
_dataListen = state.data.listen((e) { _dataListen = state.data.listen((e) {
if (e == null) return; if (e == null) return;
var zoomState = zoomLogic.zoomState;
final currentTemplateId = zoomState.zoomFile.value?.templateId; // ID
if (currentTemplateId != null && currentTemplateId != e.templateId) {
// zoom zoom文件
zoomState.initScale.value = null;
zoomState.zoomFile.value!.clearZoomFile(e.templateId);
zoomState.zoomFile.update((_) {}); //
}
if (state.favorite.value != e.isFav) state.favorite.value = !state.favorite.value; if (state.favorite.value != e.isFav) state.favorite.value = !state.favorite.value;
}); });
@ -147,7 +138,7 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
@override @override
void onClose() { void onClose() {
zoomController.dispose(); zoomController?.dispose();
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.top]); // // SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.top]); //
eventCancel(); eventCancel();
_dataListen.cancel(); _dataListen.cancel();
@ -173,7 +164,7 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
state.studentQuestions.value = data.studentQuestions; // state.studentQuestions.value = data.studentQuestions; //
state.data.value = data; state.data.value = data;
} catch (e) { } catch (e) {
print('获取数据报错了:$e'); // 使
state.getDataError.value = true; state.getDataError.value = true;
ToastUtils.showError('获取试题数据失败,请检查网络连接后重试'); ToastUtils.showError('获取试题数据失败,请检查网络连接后重试');
} finally { } finally {
@ -256,8 +247,7 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
return imgKey; return imgKey;
} catch (e) { } catch (e) {
print('图片上传失败'); //
print(e.toString());
ToastUtils.getFluttertoast(msg: '图片上传失败', context: context); ToastUtils.getFluttertoast(msg: '图片上传失败', context: context);
} finally { } finally {
// ToastUtils.dismiss(); // ToastUtils.dismiss();
@ -340,7 +330,7 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
ToastUtils.showSuccess("批阅已提交"); ToastUtils.showSuccess("批阅已提交");
}); });
} catch (e) { } catch (e) {
print("批阅提交报错 $e"); //
ToastUtils.showError('提交失败,请检查网络连接后重试'); ToastUtils.showError('提交失败,请检查网络连接后重试');
} finally { } finally {
state.submitLoading.value = false; state.submitLoading.value = false;

View File

@ -17,47 +17,43 @@ class ZoomLogic extends GetxController {
); );
var oldTemplateId; var oldTemplateId;
late StreamSubscription _streamHomework; StreamSubscription? _streamHomework;
late StreamSubscription _streamZoomState; StreamSubscription? _streamZoomState;
StreamSubscription<double?>? initScaleStream; StreamSubscription<double?>? initScaleStream;
Map<int, ZoomFileModel> imageSizeMap = {};
HomeworkReviewLogic get homeworkReviewLogic => Get.find<HomeworkReviewLogic>();
@override @override
void onInit() { void onInit() {
oldTemplateId = zoomState.zoomFile.value?.templateId; oldTemplateId = zoomState.zoomFile.value?.templateId;
/// ///
_streamZoomState = zoomState.zoomFile.listen((e) { _streamZoomState = zoomState.zoomFile.listen((e) => refreshZoomFile(e));
var templateId = e?.templateId;
if (templateId == null) return;
var homeworkData = Get.find<HomeworkReviewLogic>().state.data.value;
var zgtAnswer = homeworkData?.zgtAnswer;
if (zgtAnswer == null) return;
if (oldTemplateId == templateId) return;
// getNetworkImageDimensions(zgtAnswer);
//
oldTemplateId = templateId;
CachedNetworkImageProvider(zgtAnswer).getImageSize().then((s) {
//
if (s == null) return;
var oldVal = zoomState.zoomFile.value!;
oldVal.fileWidth = s.width;
oldVal.fileHeight = s.height;
zoomState.zoomFile.value = ZoomFileModel.fromJson(oldVal.toJson());
});
});
WidgetsBinding.instance.addPostFrameCallback((e) { WidgetsBinding.instance.addPostFrameCallback((e) {
/// ///
_streamHomework = Get.find<HomeworkReviewLogic>().state.data.listen((e) { _streamHomework = homeworkReviewLogic.state.data.listen((e) {
print("HOMEWORKSTATE 变化了"); var zoomFile = zoomState.zoomFile.value;
if (e == null || zoomState.zoomFile.value == null) return; if (e == null || zoomFile == null) return;
zoomState.zoomFile.value!.templateId = e.templateId;
print("666666 ${e.templateId}"); if (zoomFile.templateId != e.templateId) {
zoomState.zoomFile.value = ZoomFileModel.fromJson(zoomState.zoomFile.value!.toJson()); // zoom zoom文件
zoomState.initScale.value = null;
zoomFile = ZoomFileModel(
templateId: e.templateId,
viewWidth: zoomFile.viewWidth,
viewHeight: zoomFile.viewHeight,
);
///
_resetZoom();
}
///
refreshZoomFile(zoomFile);
}); });
}); });
@ -69,15 +65,53 @@ class ZoomLogic extends GetxController {
@override @override
void onClose() { void onClose() {
_streamHomework.cancel(); _streamHomework?.cancel();
_streamZoomState.cancel(); _streamZoomState?.cancel();
super.onClose(); super.onClose();
} }
void refreshZoomFile(ZoomFileModel? e) {
var templateId = e?.templateId;
if (templateId == null) return;
var homeworkData = homeworkReviewLogic.state.data.value;
var zgtAnswer = homeworkData?.zgtAnswer;
if (zgtAnswer == null || oldTemplateId == templateId) return;
// getNetworkImageDimensions(zgtAnswer);
oldTemplateId = templateId;
final fileSize = imageSizeMap[templateId];
// I/O
if (fileSize != null) {
zoomState.zoomFile.value = fileSize;
return;
}
///
CachedNetworkImageProvider(zgtAnswer).getImageSize().then((s) {
if (s == null) return;
//
var oldZoomFile = zoomState.zoomFile.value!;
///
var newZoomFile = ZoomFileModel(
templateId: templateId,
viewWidth: oldZoomFile.viewWidth,
viewHeight: oldZoomFile.viewHeight,
fileWidth: s.width,
fileHeight: s.height,
);
/// zoom文件
imageSizeMap[templateId] = newZoomFile;
zoomState.zoomFile.value = newZoomFile;
});
}
// ==> // ==>
void onScaleUpdate(double zoom) async { void onScaleUpdate(double zoom) async {
print("缩放比例:$zoom");
/// ///
zoomState.initScale.value = zoom; zoomState.initScale.value = zoom;
// anti_shake_throttling.debounce(() => zoomState.initScale.value = zoom, const Duration(milliseconds: 100))(); // anti_shake_throttling.debounce(() => zoomState.initScale.value = zoom, const Duration(milliseconds: 100))();
@ -86,12 +120,20 @@ class ZoomLogic extends GetxController {
// ==> // ==>
void onPanUpPosition(Offset val) async { void onPanUpPosition(Offset val) async {
// //
var state = Get.find<HomeworkReviewLogic>().state; var state = homeworkReviewLogic.state;
print('**************** 正在移动位置 YYY:${val.dy}');
print('**************** 正在移动位置 XXX:${val.dx}');
state.zoomOffset = val; state.zoomOffset = val;
state.slide.value = val.dy.abs().toInt().toDouble(); state.slide.value = val.dy.abs().toInt().toDouble();
} }
//
void _resetZoom() {
//
zoomState.initScale.value = 1.0;
//
homeworkReviewLogic.state.zoomOffset = Offset.zero;
homeworkReviewLogic.state.slide.value = 0.0;
}
} }
class HomeworkReviewZoomBinding extends Bindings { class HomeworkReviewZoomBinding extends Bindings {
@ -143,9 +185,9 @@ class ZoomFileModel extends Object {
this.imageHeightOffsetend, this.imageHeightOffsetend,
this.pixelRatio, this.pixelRatio,
}) { }) {
///
// . // .
if (fileHeight == null || fileWidth == null) return; if (fileHeight != null && fileWidth != null) {
scaleRatio = viewWidth / fileWidth!; scaleRatio = viewWidth / fileWidth!;
actualWidth = fileWidth! * scaleRatio; actualWidth = fileWidth! * scaleRatio;
actualHeight = fileHeight! * scaleRatio; actualHeight = fileHeight! * scaleRatio;
@ -156,6 +198,7 @@ class ZoomFileModel extends Object {
imageHeightOffsetStart = remainingHeight! / 2; imageHeightOffsetStart = remainingHeight! / 2;
imageHeightOffsetend = imageHeightOffsetStart! + actualHeight!; imageHeightOffsetend = imageHeightOffsetStart! + actualHeight!;
} }
}
factory ZoomFileModel.fromJson(Map<String, dynamic> srcJson) => _$ZoomFileModelFromJson(srcJson); factory ZoomFileModel.fromJson(Map<String, dynamic> srcJson) => _$ZoomFileModelFromJson(srcJson);

View File

@ -6,7 +6,8 @@ class PersonalDetailTopBar extends StatefulWidget {
final ValueChanged<int>? onTap; final ValueChanged<int>? onTap;
final String customTimeStr; final String customTimeStr;
final bool? hasAll; final bool? hasAll;
const PersonalDetailTopBar({Key? key,required this.controller, this.onTap, required this.customTimeStr,this.hasAll=false}) : super(key: key); const PersonalDetailTopBar(
{super.key, required this.controller, this.onTap, required this.customTimeStr, this.hasAll = false});
@override @override
State<PersonalDetailTopBar> createState() => _PersonalDetailTopBarState(); State<PersonalDetailTopBar> createState() => _PersonalDetailTopBarState();
@ -14,7 +15,7 @@ class PersonalDetailTopBar extends StatefulWidget {
class _PersonalDetailTopBarState extends State<PersonalDetailTopBar> { class _PersonalDetailTopBarState extends State<PersonalDetailTopBar> {
@override @override
void initState(){ void initState() {
super.initState(); super.initState();
} }
@ -22,9 +23,7 @@ class _PersonalDetailTopBarState extends State<PersonalDetailTopBar> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
decoration: BoxDecoration( decoration: BoxDecoration(border: Border(bottom: BorderSide(width: 1.r, color: const Color(0xFFCCCCCC)))),
border: Border(bottom: BorderSide(width: 1.r,color: Color(0xFFCCCCCC)))
),
child: TabBar( child: TabBar(
controller: widget.controller, controller: widget.controller,
dividerHeight: 0, dividerHeight: 0,
@ -33,26 +32,22 @@ class _PersonalDetailTopBarState extends State<PersonalDetailTopBar> {
labelStyle: TextStyle( labelStyle: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: const Color(0xFF4CC793), color: Theme.of(context).primaryColor,
), ),
isScrollable: true, isScrollable: true,
labelColor: const Color(0xFF4CC793), labelColor: Theme.of(context).primaryColor,
unselectedLabelColor: const Color(0xFF505E6E), unselectedLabelColor: const Color(0xFF505E6E),
padding: EdgeInsets.symmetric(horizontal: 14.r), padding: EdgeInsets.symmetric(horizontal: 14.r),
// indicatorSize: TabBarIndicatorSize.label, // // indicatorSize: TabBarIndicatorSize.label, //
onTap: widget.onTap, onTap: widget.onTap,
indicator: UnderlineTabIndicator( indicator: UnderlineTabIndicator(
borderSide: BorderSide( borderSide: BorderSide(width: 2.r, color: Theme.of(context).primaryColor),
width: 2.r,
color:const Color(0xFF4CC793)
),
), ),
tabs: <Widget>[ tabs: <Widget>[
const Tab(text: '近一周'), const Tab(text: '近一周'),
const Tab(text: '近一月'), const Tab(text: '近一月'),
Tab(text: widget.customTimeStr), Tab(text: widget.customTimeStr),
if(widget.hasAll == true) if (widget.hasAll == true) const Tab(text: '全部'),
const Tab(text: '全部'),
], ],
), ),
); );

View File

@ -21,7 +21,16 @@ class AgreementPage extends StatelessWidget {
body: ListView( body: ListView(
padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 8.h), padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 8.h),
children: [ children: [
HtmlWidget(agreement.richText, textStyle: const TextStyle(color: Colors.black)), HtmlWidget(
agreement.richText,
textStyle: const TextStyle(color: Colors.black),
// 访
renderMode: RenderMode.column,
//
onTapUrl: (url) => false,
//
customStylesBuilder: (element) => <String, String>{},
),
], ],
), ),
); );

View File

@ -1,4 +1,3 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart';
@ -148,6 +147,7 @@ class _RegisterState extends State<Register> with RequestToolMixin {
maxLines: 1, maxLines: 1,
maxLength: 20, maxLength: 20,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
enableInteractiveSelection: false,
onEditingComplete: () { onEditingComplete: () {
FocusScope.of(context).requestFocus(_pwdFocus); FocusScope.of(context).requestFocus(_pwdFocus);
}, },
@ -169,6 +169,7 @@ class _RegisterState extends State<Register> with RequestToolMixin {
maxLines: 1, maxLines: 1,
obscureText: _isShowPwd, // obscureText: _isShowPwd, //
textInputAction: TextInputAction.go, textInputAction: TextInputAction.go,
enableInteractiveSelection: false,
onSubmitted: (_) => toRegister(), onSubmitted: (_) => toRegister(),
style: TextStyle( style: TextStyle(
color: const Color.fromRGBO(80, 87, 103, 1), color: const Color.fromRGBO(80, 87, 103, 1),
@ -249,7 +250,8 @@ class _RegisterState extends State<Register> with RequestToolMixin {
), ),
InkWell( InkWell(
onTap: () { onTap: () {
Get.toNamed(Routes.agreementPage, arguments: {"type": AGREEMENT_KEY.USER_AGREEMENT.name}); Get.toNamed(Routes.agreementPage,
arguments: {"type": AGREEMENT_KEY.USER_AGREEMENT.name});
}, },
child: quickText( child: quickText(
'《用户协议》', '《用户协议》',
@ -259,12 +261,13 @@ class _RegisterState extends State<Register> with RequestToolMixin {
), ),
InkWell( InkWell(
onTap: () { onTap: () {
Get.toNamed(Routes.agreementPage, arguments: {"type": AGREEMENT_KEY.PRIVACY_GREEMENT.name}); Get.toNamed(Routes.agreementPage,
arguments: {"type": AGREEMENT_KEY.PRIVACY_GREEMENT.name});
}, },
child: quickText( child: quickText(
'《隐私协议》', '《隐私协议》',
size: 12.sp, size: 12.sp,
color:Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
), ),
), ),
], ],

View File

@ -4,8 +4,7 @@ import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:making_school_asignment_app/common/mixins/request_tool_mixin.dart'; import 'package:making_school_asignment_app/common/mixins/request_tool_mixin.dart';
import 'package:making_school_asignment_app/common/store/user_store.dart'; import 'package:making_school_asignment_app/common/store/user_store.dart';
import 'package:making_school_asignment_app/common/utils/app_upgrade/model/UpdateAppEvent.dart'; import 'package:making_school_asignment_app/common/controllers/upgrade_logic.dart';
import 'package:making_school_asignment_app/common/utils/app_upgrade/upgradeLogic.dart';
import 'package:making_school_asignment_app/common/utils/storage.dart'; import 'package:making_school_asignment_app/common/utils/storage.dart';
import 'package:making_school_asignment_app/common/utils/toast_utils.dart'; import 'package:making_school_asignment_app/common/utils/toast_utils.dart';
import 'package:making_school_asignment_app/common/utils/utils.dart'; import 'package:making_school_asignment_app/common/utils/utils.dart';
@ -67,9 +66,11 @@ class LoginLogic extends GetxController with RequestToolMixin {
void toMsg(msg, [bool error = true]) { void toMsg(msg, [bool error = true]) {
if (error) { if (error) {
WidgetsBinding.instance.addPostFrameCallback((_) => ToastUtils.showError(msg)); WidgetsBinding.instance
.addPostFrameCallback((_) => ToastUtils.showError(msg));
} else { } else {
WidgetsBinding.instance.addPostFrameCallback((_) => ToastUtils.showInfo(msg)); WidgetsBinding.instance
.addPostFrameCallback((_) => ToastUtils.showInfo(msg));
} }
state.canLogin.value = true; state.canLogin.value = true;
} }
@ -87,15 +88,6 @@ class LoginLogic extends GetxController with RequestToolMixin {
EasyLoading.show(status: 'loading...'); EasyLoading.show(status: 'loading...');
try { try {
/// APP
if (userName != 'AppleTester' && const bool.fromEnvironment('dart.vm.product')) {
UpdateAppEvent? updateAppEvent = await upgradeLogic.getUpdateAppEvent();
var upgrade = updateAppEvent?.upgrade ?? false;
if (upgrade) {
upgradeLogic.getAppUpgrade(Get.context ?? context);
return toMsg('请在升级APP后再次登陆', false);
}
}
await getClient().toLogin(userName, userPwd); await getClient().toLogin(userName, userPwd);
String? nameidentifier = UserStore.to.userInfo.value?.nameidentifier; String? nameidentifier = UserStore.to.userInfo.value?.nameidentifier;
if (nameidentifier == null) { if (nameidentifier == null) {

View File

@ -1,10 +1,10 @@
import 'package:get/get.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:making_school_asignment_app/common/const_text.dart'; import 'package:making_school_asignment_app/common/const_text.dart';
import 'package:making_school_asignment_app/common/utils/anti_shake_throttling.dart'; import 'package:making_school_asignment_app/common/utils/anti_shake_throttling.dart';
import 'package:making_school_asignment_app/common/utils/app_upgrade/upgradeLogic.dart'; import 'package:making_school_asignment_app/common/controllers/upgrade_logic.dart';
import 'package:making_school_asignment_app/common/utils/utils.dart'; import 'package:making_school_asignment_app/common/utils/utils.dart';
import 'package:making_school_asignment_app/page/global_widget/my_text.dart'; import 'package:making_school_asignment_app/page/global_widget/my_text.dart';
import 'package:making_school_asignment_app/routes/app_pages.dart'; import 'package:making_school_asignment_app/routes/app_pages.dart';
@ -33,6 +33,7 @@ class _LoginPageState extends State<LoginPage> {
WidgetsBinding.instance.addPostFrameCallback((_) async { WidgetsBinding.instance.addPostFrameCallback((_) async {
await sysProtocol(context); await sysProtocol(context);
/// 便 /// 便
// await Future.delayed(Duration.zero, () => upgradeLogic.getAppUpgrade(context)); // await Future.delayed(Duration.zero, () => upgradeLogic.getAppUpgrade(context));
}); });
@ -80,15 +81,19 @@ class _LoginPageState extends State<LoginPage> {
child: SizedBox( child: SizedBox(
height: 77.w, height: 77.w,
width: 77.w, width: 77.w,
child: Image.asset('assets/images/login_logo_icon.png', fit: BoxFit.cover), child: Image.asset('assets/images/login_logo_icon.png',
fit: BoxFit.cover),
), ),
), ),
Container( Container(
margin: EdgeInsets.only(top: 90.r), margin: EdgeInsets.only(top: 90.r),
padding: EdgeInsets.only(top: 50.h, bottom: 16.h, left: 40.w, right: 40.w), padding: EdgeInsets.only(
top: 50.h, bottom: 16.h, left: 40.w, right: 40.w),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.only(topLeft: Radius.circular(30.r), topRight: Radius.circular(30.r)), borderRadius: BorderRadius.only(
topLeft: Radius.circular(30.r),
topRight: Radius.circular(30.r)),
/*boxShadow: const [ /*boxShadow: const [
BoxShadow( BoxShadow(
color: Color.fromRGBO(46, 91, 255, 0.1), color: Color.fromRGBO(46, 91, 255, 0.1),
@ -105,13 +110,16 @@ class _LoginPageState extends State<LoginPage> {
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.transparent, color: Colors.transparent,
border: Border.all(width: 1.w, color: const Color(0xFF434343)), border: Border.all(
borderRadius: BorderRadius.all(Radius.circular(17.w)), width: 1.w, color: const Color(0xFF434343)),
borderRadius:
BorderRadius.all(Radius.circular(17.w)),
), ),
child: TextField( child: TextField(
controller: state.userNameController, controller: state.userNameController,
/* maxLines: 1, /* maxLines: 1,
maxLength: 20,*/ maxLength: 20,*/
enableInteractiveSelection: false,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
onEditingComplete: () { onEditingComplete: () {
Get.focusScope?.nextFocus(); Get.focusScope?.nextFocus();
@ -149,30 +157,35 @@ class _LoginPageState extends State<LoginPage> {
), ),
), ),
), ),
SizedBox( SizedBox(height: 20.r),
height: 20.r,
),
Obx(() { Obx(() {
return Container( return Container(
padding: EdgeInsets.symmetric(horizontal: 15.w), padding: EdgeInsets.symmetric(horizontal: 15.w),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.transparent, color: Colors.transparent,
border: Border.all(width: 1.w, color: const Color(0xFF434343)), border: Border.all(
borderRadius: BorderRadius.all(Radius.circular(17.w)), width: 1.w, color: const Color(0xFF434343)),
borderRadius:
BorderRadius.all(Radius.circular(17.w)),
), ),
child: TextField( child: TextField(
focusNode: state.pwdFocus, focusNode: state.pwdFocus,
controller: state.passwordController, controller: state.passwordController,
keyboardType: TextInputType.number, // keyboardType: TextInputType.number,
maxLines: 1, maxLines: 1,
obscureText: state.isShowPwd.value, obscureText: state.isShowPwd.value,
// //
// textInputAction: state.isShowPwd.value?TextInputAction.go:TextInputAction.next, // textInputAction: state.isShowPwd.value?TextInputAction.go:TextInputAction.next,
textInputAction: TextInputAction.send, textInputAction: TextInputAction.send,
onSubmitted: (_) => easyThrottle('LOGIN_EASYTHROTTLE', () async{ enableInteractiveSelection: false,
onSubmitted: (_) =>
easyThrottle('LOGIN_EASYTHROTTLE', () async {
Utils.hideKeyboard(); Utils.hideKeyboard();
await Future.delayed(const Duration(milliseconds: 300)); await Future.delayed(
WidgetsBinding.instance.addPostFrameCallback((_) => logic.toLogin(context,upgradeLogic)); const Duration(milliseconds: 300));
WidgetsBinding.instance.addPostFrameCallback(
(_) =>
logic.toLogin(context, upgradeLogic));
}), }),
style: TextStyle( style: TextStyle(
color: const Color(0xFF434343), color: const Color(0xFF434343),
@ -198,12 +211,15 @@ class _LoginPageState extends State<LoginPage> {
), ),
suffixIcon: InkWell( suffixIcon: InkWell(
onTap: () { onTap: () {
state.isShowPwd.value = !state.isShowPwd.value; state.isShowPwd.value =
!state.isShowPwd.value;
}, },
child: Padding( child: Padding(
padding: EdgeInsets.only(right: 5.r), padding: EdgeInsets.only(right: 5.r),
child: Image.asset( child: Image.asset(
state.isShowPwd.value ? 'assets/images/eye_default.png' : 'assets/images/eye_active.png', state.isShowPwd.value
? 'assets/images/eye_default.png'
: 'assets/images/eye_active.png',
width: 15.r, width: 15.r,
height: 15.r, height: 15.r,
), ),
@ -239,23 +255,35 @@ class _LoginPageState extends State<LoginPage> {
child: Checkbox( child: Checkbox(
// activeColor: Colors.transparent, // // activeColor: Colors.transparent, //
activeColor: Theme.of(context).primaryColor, activeColor:
Theme.of(context).primaryColor,
// checkColor: Colors.white, // checkColor: Colors.white,
value: state.keepPwd.value, value: state.keepPwd.value,
onChanged: (value) { onChanged: (value) {
// Get.focusScope?.nextFocus(); // Get.focusScope?.nextFocus();
FocusScope.of(context).requestFocus(state.pwdFocus); FocusScope.of(context)
FocusScope.of(context).requestFocus(state.theFocus); .requestFocus(state.pwdFocus);
state.keepPwd.value = value ?? false; FocusScope.of(context)
.requestFocus(state.theFocus);
state.keepPwd.value =
value ?? false;
}, },
side: WidgetStateBorderSide.resolveWith( side: WidgetStateBorderSide
.resolveWith(
(Set<WidgetState> states) { (Set<WidgetState> states) {
if (states.contains(WidgetState.selected)) { if (states.contains(
WidgetState.selected)) {
// //
return BorderSide(width: 1.5.r, color: Theme.of(context).primaryColor); return BorderSide(
width: 1.5.r,
color: Theme.of(context)
.primaryColor);
} }
//绿 //绿
return BorderSide(width: 1.r, color: const Color(0xFF434343)); return BorderSide(
width: 1.r,
color: const Color(
0xFF434343));
}, },
)), )),
); );
@ -266,9 +294,12 @@ class _LoginPageState extends State<LoginPage> {
Utils.hideKeyboard(); Utils.hideKeyboard();
Get.focusScope?.nextFocus(); Get.focusScope?.nextFocus();
Get.focusScope?.nextFocus(); Get.focusScope?.nextFocus();
FocusScope.of(context).requestFocus(state.pwdFocus); FocusScope.of(context)
FocusScope.of(context).requestFocus(state.theFocus); .requestFocus(state.pwdFocus);
state.keepPwd.value = !state.keepPwd.value; FocusScope.of(context)
.requestFocus(state.theFocus);
state.keepPwd.value =
!state.keepPwd.value;
}, },
child: Text( child: Text(
'记住密码', '记住密码',
@ -283,21 +314,26 @@ class _LoginPageState extends State<LoginPage> {
), ),
InkWell( InkWell(
onTap: () => Get.toNamed(Routes.register), onTap: () => Get.toNamed(Routes.register),
child: quickText('账号注册', color: const Color(0xFF434343)), child: quickText('账号注册',
color: const Color(0xFF434343)),
) )
], ],
), ),
InkWell( InkWell(
onTap: () => easyThrottle('LOGIN_EASYTHROTTLE', () async{ onTap: () =>
easyThrottle('LOGIN_EASYTHROTTLE', () async {
Utils.hideKeyboard(); Utils.hideKeyboard();
await Future.delayed(Duration.zero); await Future.delayed(Duration.zero);
WidgetsBinding.instance.addPostFrameCallback((_) => logic.toLogin(context,upgradeLogic)); WidgetsBinding.instance.addPostFrameCallback(
(_) => logic.toLogin(context, upgradeLogic));
}), }),
child: Obx(() { child: Obx(() {
return Container( return Container(
margin: EdgeInsets.symmetric(vertical: 10.h), margin: EdgeInsets.symmetric(vertical: 10.h),
decoration: BoxDecoration( decoration: BoxDecoration(
color: state.canLogin.value ? const Color(0xFF8C68FF) : const Color(0xFFdddddd), color: state.canLogin.value
? const Color(0xFF8C68FF)
: const Color(0xFFdddddd),
/*boxShadow: [ /*boxShadow: [
BoxShadow( BoxShadow(
color: color:
@ -314,7 +350,8 @@ class _LoginPageState extends State<LoginPage> {
alignment: Alignment.center, alignment: Alignment.center,
width: double.infinity, width: double.infinity,
height: 50.h, height: 50.h,
child: quickText('登 录', size: 18.sp, color: Colors.white)); child: quickText('登 录',
size: 18.sp, color: Colors.white));
}), }),
), ),
Row( Row(
@ -335,16 +372,23 @@ class _LoginPageState extends State<LoginPage> {
state.pwdFocus); state.pwdFocus);
FocusScope.of(context).requestFocus( FocusScope.of(context).requestFocus(
state.theFocus);*/ state.theFocus);*/
state.readAgreement.value = value ?? false; state.readAgreement.value =
value ?? false;
}, },
side: WidgetStateBorderSide.resolveWith( side: WidgetStateBorderSide.resolveWith(
(Set<WidgetState> states) { (Set<WidgetState> states) {
if (states.contains(WidgetState.selected)) { if (states
.contains(WidgetState.selected)) {
// //
return BorderSide(width: 1.5.r, color: Theme.of(context).primaryColor); return BorderSide(
width: 1.5.r,
color: Theme.of(context)
.primaryColor);
} }
//绿 //绿
return BorderSide(width: 1.r, color: const Color(0xFF434343)); return BorderSide(
width: 1.r,
color: const Color(0xFF434343));
}, },
), ),
), ),
@ -353,7 +397,9 @@ class _LoginPageState extends State<LoginPage> {
), ),
InkWell( InkWell(
onTap: () { onTap: () {
Get.toNamed(Routes.agreementPage, arguments: {"type": AGREEMENT_KEY.USER_AGREEMENT.name}); Get.toNamed(Routes.agreementPage, arguments: {
"type": AGREEMENT_KEY.USER_AGREEMENT.name
});
}, },
child: quickText( child: quickText(
'请仔细阅读', '请仔细阅读',
@ -362,16 +408,22 @@ class _LoginPageState extends State<LoginPage> {
), ),
InkWell( InkWell(
onTap: () { onTap: () {
Get.toNamed(Routes.agreementPage, arguments: {"type": AGREEMENT_KEY.USER_AGREEMENT.name}); Get.toNamed(Routes.agreementPage, arguments: {
"type": AGREEMENT_KEY.USER_AGREEMENT.name
});
}, },
child: Text( child: Text(
'《用户协议》', '《用户协议》',
style: TextStyle(fontSize: 12.r, color: Theme.of(context).primaryColor), style: TextStyle(
fontSize: 12.r,
color: Theme.of(context).primaryColor),
), ),
), ),
InkWell( InkWell(
onTap: () { onTap: () {
Get.toNamed(Routes.agreementPage, arguments: {"type": AGREEMENT_KEY.PRIVACY_GREEMENT.name}); Get.toNamed(Routes.agreementPage, arguments: {
"type": AGREEMENT_KEY.PRIVACY_GREEMENT.name
});
}, },
child: quickText( child: quickText(
'《隐私协议》', '《隐私协议》',

View File

@ -1 +0,0 @@
flutter/ephemeral

View File

@ -1,145 +0,0 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "making_school_asignment_app")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.yuanxuan.making_school_asignment_app")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)
# Load bundled libraries from the lib/ directory relative to the binary.
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Root filesystem for cross-building.
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif()
# Define build configuration options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()
# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Define the application target. To change its name, change BINARY_NAME above,
# not the value here, or `flutter run` will no longer work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)
# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})
# Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)
# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location.
set_target_properties(${BINARY_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
install(FILES "${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endforeach(bundled_library)
# Copy the native assets provided by the build.dart from all packages.
set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/")
install(DIRECTORY "${NATIVE_ASSETS_DIR}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()

View File

@ -1,88 +0,0 @@
# This file controls Flutter-level build steps. It should not be edited.
cmake_minimum_required(VERSION 3.10)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
# which isn't available in 3.10.
function(list_prepend LIST_NAME PREFIX)
set(NEW_LIST "")
foreach(element ${${LIST_NAME}})
list(APPEND NEW_LIST "${PREFIX}${element}")
endforeach(element)
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
endfunction()
# === Flutter Library ===
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"fl_basic_message_channel.h"
"fl_binary_codec.h"
"fl_binary_messenger.h"
"fl_dart_project.h"
"fl_engine.h"
"fl_json_message_codec.h"
"fl_json_method_codec.h"
"fl_message_codec.h"
"fl_method_call.h"
"fl_method_channel.h"
"fl_method_codec.h"
"fl_method_response.h"
"fl_plugin_registrar.h"
"fl_plugin_registry.h"
"fl_standard_message_codec.h"
"fl_standard_method_codec.h"
"fl_string_codec.h"
"fl_value.h"
"fl_view.h"
"flutter_linux.h"
)
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
target_link_libraries(flutter INTERFACE
PkgConfig::GTK
PkgConfig::GLIB
PkgConfig::GIO
)
add_dependencies(flutter flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/_phony_
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
)

View File

@ -1,15 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
}

View File

@ -1,15 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

View File

@ -1,24 +0,0 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
url_launcher_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View File

@ -1,6 +0,0 @@
#include "my_application.h"
int main(int argc, char** argv) {
g_autoptr(MyApplication) app = my_application_new();
return g_application_run(G_APPLICATION(app), argc, argv);
}

View File

@ -1,124 +0,0 @@
#include "my_application.h"
#include <flutter_linux/flutter_linux.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#include "flutter/generated_plugin_registrant.h"
struct _MyApplication {
GtkApplication parent_instance;
char** dart_entrypoint_arguments;
};
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
MyApplication* self = MY_APPLICATION(application);
GtkWindow* window =
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
// Use a header bar when running in GNOME as this is the common style used
// by applications and is the setup most users will be using (e.g. Ubuntu
// desktop).
// If running on X and not using GNOME then just use a traditional title bar
// in case the window manager does more exotic layout, e.g. tiling.
// If running on Wayland assume the header bar will work (may need changing
// if future cases occur).
gboolean use_header_bar = TRUE;
#ifdef GDK_WINDOWING_X11
GdkScreen* screen = gtk_window_get_screen(window);
if (GDK_IS_X11_SCREEN(screen)) {
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
use_header_bar = FALSE;
}
}
#endif
if (use_header_bar) {
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "making_school_asignment_app");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
} else {
gtk_window_set_title(window, "making_school_asignment_app");
}
gtk_window_set_default_size(window, 1280, 720);
gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlDartProject) project = fl_dart_project_new();
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
FlView* view = fl_view_new(project);
gtk_widget_show(GTK_WIDGET(view));
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
gtk_widget_grab_focus(GTK_WIDGET(view));
}
// Implements GApplication::local_command_line.
static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
MyApplication* self = MY_APPLICATION(application);
// Strip out the first argument as it is the binary name.
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
g_autoptr(GError) error = nullptr;
if (!g_application_register(application, nullptr, &error)) {
g_warning("Failed to register: %s", error->message);
*exit_status = 1;
return TRUE;
}
g_application_activate(application);
*exit_status = 0;
return TRUE;
}
// Implements GApplication::startup.
static void my_application_startup(GApplication* application) {
//MyApplication* self = MY_APPLICATION(object);
// Perform any actions required at application startup.
G_APPLICATION_CLASS(my_application_parent_class)->startup(application);
}
// Implements GApplication::shutdown.
static void my_application_shutdown(GApplication* application) {
//MyApplication* self = MY_APPLICATION(object);
// Perform any actions required at application shutdown.
G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);
}
// Implements GObject::dispose.
static void my_application_dispose(GObject* object) {
MyApplication* self = MY_APPLICATION(object);
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
}
static void my_application_class_init(MyApplicationClass* klass) {
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
G_APPLICATION_CLASS(klass)->startup = my_application_startup;
G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;
G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
}
static void my_application_init(MyApplication* self) {}
MyApplication* my_application_new() {
return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID,
"flags", G_APPLICATION_NON_UNIQUE,
nullptr));
}

View File

@ -1,18 +0,0 @@
#ifndef FLUTTER_MY_APPLICATION_H_
#define FLUTTER_MY_APPLICATION_H_
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
GtkApplication)
/**
* my_application_new:
*
* Creates a new Flutter-based application.
*
* Returns: a new #MyApplication.
*/
MyApplication* my_application_new();
#endif // FLUTTER_MY_APPLICATION_H_

View File

@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 1.0.5+6 version: 1.0.8+9
environment: environment:
sdk: '>=3.4.1 <4.0.0' sdk: '>=3.4.1 <4.0.0'
@ -48,14 +48,14 @@ dependencies:
# http请求插件 # http请求插件
dio: ^5.4.2+1 dio: ^5.4.2+1
# 网络缓存图片 # 网络缓存图片
cached_network_image: ^3.2.1 cached_network_image: ^3.4.1
# 上拉加载和下拉刷新的组件 # 上拉加载和下拉刷新的组件
flutter_easyrefresh: ^2.2.2 flutter_easyrefresh: ^2.2.2
photo_view: ^0.15.0 photo_view: ^0.15.0
# 加密验签插件支持SHA MD5 HMAC # 加密验签插件支持SHA MD5 HMAC
crypto: ^3.0.2 crypto: ^3.0.2
# 获取app版本号 # 获取app版本号
package_info_plus: ^8.0.0 package_info_plus: ^9.0.0
# 网络监控 # 网络监控
connectivity_plus: ^6.0.3 connectivity_plus: ^6.0.3
# toast组件用于系统尚未初始化完成时 # toast组件用于系统尚未初始化完成时
@ -71,10 +71,9 @@ dependencies:
url: https://gitea.23544.com/wangyang/zoom_widget.git url: https://gitea.23544.com/wangyang/zoom_widget.git
# url: https://github.com/semakers/zoom-widget.git # url: https://github.com/semakers/zoom-widget.git
# ref: a35c9da6afe405c23b5897b449683d424016e9f1 # ref: a35c9da6afe405c23b5897b449683d424016e9f1
# start retrofit请求封装
retrofit: ^4.1.0 retrofit: 4.6.0
json_annotation: 4.9.0 json_annotation: ^4.9.0
# end retrofit请求封装
# 进度条 # 进度条
percent_indicator: ^4.2.3 percent_indicator: ^4.2.3
badges: ^3.1.2 badges: ^3.1.2
@ -89,14 +88,15 @@ dependencies:
flutter_spinkit: ^5.2.1 flutter_spinkit: ^5.2.1
event_bus: ^2.0.0 event_bus: ^2.0.0
path_provider: ^2.1.3 path_provider: ^2.1.3
uuid: ^3.0.7
flutter_echarts: ^2.4.0 flutter_echarts: ^2.4.0
# 饼图 # 饼图
flutter_echart: ^2.0.0 flutter_echart: ^2.0.0
url_launcher: ^6.1.11 url_launcher: ^6.1.11
app_installer: ^1.1.0 app_installer: ^1.1.0
auto_updater: ^0.2.1 # auto_updater: ^0.2.1
permission_handler: ^11.3.1 auto_updater: ^1.0.0
permission_handler: ^12.0.1
flutter_distributor: 0.6.5 flutter_distributor: 0.6.5
# fastforge: ^0.6.5 # fastforge: ^0.6.5
flutter_native_splash: ^2.4.6 flutter_native_splash: ^2.4.6
@ -104,9 +104,15 @@ dependencies:
app_settings: ^5.1.1 app_settings: ^5.1.1
device_info_plus: ^11.1.0 device_info_plus: ^11.1.0
auto_size_text: ^3.0.0 auto_size_text: ^3.0.0
yx_app_upgrade_flutter:
git:
url: https://gitea.23544.com/wangyang/yx_app_upgrade_flutter.git
ref: 2.0.4
dependency_overrides: dependency_overrides:
archive: ^4.0.2 archive: ^4.0.2
uuid: ^4.4.2
# analyzer: ^3..0
# meta: ^1.15.0 # meta: ^1.15.0
dev_dependencies: dev_dependencies: