优化完成批阅试图和视图视图滚动条联动

This commit is contained in:
DESKTOP-I3JPKHK\wy 2025-09-09 15:58:30 +08:00
parent 6252f930e1
commit 70f658f87d
60 changed files with 1986 additions and 912 deletions

View File

@ -0,0 +1,3 @@
{
"flutter": "3.32.0"
}

View File

@ -42,3 +42,6 @@ app.*.map.json
/android/app/debug /android/app/debug
/android/app/profile /android/app/profile
/android/app/release /android/app/release
# FVM Version Cache
.fvm/

View File

@ -0,0 +1,3 @@
{
"dart.flutterSdkPath": ".fvm/versions/3.32.0"
}

View File

@ -37,6 +37,10 @@ android {
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8
} }
kotlinOptions {
jvmTarget = "1.8"
}
signingConfigs { signingConfigs {
release { release {
keyAlias keystoreProperties['keyAlias'] keyAlias keystoreProperties['keyAlias']

View File

@ -4,6 +4,7 @@
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:enableOnBackInvokedCallback="true"> android:enableOnBackInvokedCallback="true">
<!-- windowTranslucentStatus 设置顶部状态栏使用-->
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
@ -12,6 +13,7 @@
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowTranslucentStatus="true"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as <!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user the Android process has started. This theme is visible to the user

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

After

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

After

Width:  |  Height:  |  Size: 341 KiB

View File

@ -3,10 +3,9 @@
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:forceDarkAllowed">false</item> <item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item> <item name="android:windowFullscreen">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#8C68FF</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item> <item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.

View File

@ -6,8 +6,8 @@
the Flutter engine draws its first frame --> the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item> <item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:forceDarkAllowed">false</item> <item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item> <item name="android:windowFullscreen">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.

View File

@ -3,10 +3,9 @@
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off --> <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:forceDarkAllowed">false</item> <item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item> <item name="android:windowFullscreen">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#8C68FF</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item> <item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.

View File

@ -6,8 +6,8 @@
the Flutter engine draws its first frame --> the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item> <item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:forceDarkAllowed">false</item> <item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item> <item name="android:windowFullscreen">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.

View File

@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-7.6.3-all.zip distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.10.2-all.zip

View File

@ -18,8 +18,8 @@ pluginManagement {
plugins { plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false id "com.android.application" version "8.8.0" apply false
id "org.jetbrains.kotlin.android" version "1.7.10" apply false id "org.jetbrains.kotlin.android" version "1.8.22" apply false
} }
include ":app" include ":app"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

After

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 69 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 69 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

After

Width:  |  Height:  |  Size: 69 B

View File

@ -17,7 +17,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView> <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView>
</subviews> </subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
@ -38,7 +38,7 @@
</scene> </scene>
</scenes> </scenes>
<resources> <resources>
<image name="LaunchImage" width="751" height="1734"/> <image name="LaunchImage" width="168" height="185"/>
<image name="LaunchBackground" width="1" height="1"/> <image name="LaunchBackground" width="1" height="1"/>
</resources> </resources>
</document> </document>

View File

@ -38,7 +38,7 @@
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
<string>Main</string> <string>Main</string>
<key>UIStatusBarHidden</key> <key>UIStatusBarHidden</key>
<false/> <true/>
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>

View File

@ -0,0 +1,44 @@
import 'package:get/get.dart';
import '../mixins/request_tool_mixin.dart';
/// getx GetxController的拓展
///
///
/// 1. State
/// ``` dart
/// dart class MyState {
/// final String name;
/// MyState(this.name);
/// }
/// ```
/// 2. Controller
/// ``` dart
/// class MyController extends GetxControllerExpand<MyState> {
/// @override
/// MyState get state => MyState('hello');
/// }
/// ```
/// 3. View
/// ``` dart
/// class MyView extends GetViewExpand<MyController, MyState> {
/// const MyView({super.key});
///
/// @override
/// Widget build(BuildContext context) {
/// return Text(state.name);
/// }
/// }
/// ```
abstract class GetViewExpand<T extends GetxControllerExpand<U>, U> extends GetView<T> {
const GetViewExpand({super.key});
U get state => controller.state;
@override
T get controller => GetInstance().find<T>(tag: tag);
}
abstract class GetxControllerExpand<U> extends GetxController with RequestToolMixin {
U get state;
}

View File

@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
abstract class GetxKeepAliveWidget<T extends GetxController> extends StatefulWidget {
const GetxKeepAliveWidget({super.key});
@override
State<GetxKeepAliveWidget<T>> createState() => _GetxKeepAliveWidgetState<T>();
Widget buildContent(BuildContext context, T controller);
void initState() {}
void dispose() {}
}
class _GetxKeepAliveWidgetState<T extends GetxController> extends State<GetxKeepAliveWidget<T>>
with AutomaticKeepAliveClientMixin {
T get controller => GetInstance().find<T>();
@override
bool get wantKeepAlive => true;
@override
void initState() {
widget.initState();
super.initState();
}
@override
void dispose() {
widget.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
super.build(context);
return GetBuilder<T>(
builder: (controller) => widget.buildContent(context, controller),
assignId: true,
);
}
}

View File

@ -1,15 +1,16 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:get/get.dart' as getx;
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:making_school_asignment_app/common/utils/utils.dart'; import 'package:get/get.dart' as getx;
import 'package:package_info_plus/package_info_plus.dart';
import 'package:making_school_asignment_app/common/api/retrofit_client.dart'; import 'package:making_school_asignment_app/common/api/retrofit_client.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/user_info.dart'; import 'package:making_school_asignment_app/common/job/user_info.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/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/routes/app_pages.dart'; import 'package:making_school_asignment_app/routes/app_pages.dart';
import 'package:package_info_plus/package_info_plus.dart';
import '../job/user_info_detail.dart'; import '../job/user_info_detail.dart';
import '../utils/storage.dart'; import '../utils/storage.dart';
@ -40,7 +41,7 @@ class RequestTool {
RequestTool._internal(this._client); RequestTool._internal(this._client);
static get instance { static RequestTool get instance {
if (_instance == null) { if (_instance == null) {
RetrofitClient client = _init(); RetrofitClient client = _init();
_instance = RequestTool._internal(client); _instance = RequestTool._internal(client);
@ -166,7 +167,7 @@ class TheError extends Interceptor {
var errorMap = response.data; var errorMap = response.data;
if (errorMap is String && errorMap.length > 0) { if (errorMap is String && errorMap.isNotEmpty) {
try { try {
var errorModel = jsonDecode(errorMap); var errorModel = jsonDecode(errorMap);
var error = errorModel['error']; var error = errorModel['error'];

View File

@ -0,0 +1,106 @@
// ramer_douglas_peucker.dart
import 'package:flutter/material.dart';
/// --Ramer-Douglas-Peucker, RDP
///
/// RDP算法用于将一条由大量点组成的曲线
///
class RamerDouglasPeucker {
/// 线
///
/// [point]:
/// [start]: 线
/// [end]: 线
/// 线线0
static double _getPerpendicularDistance(Offset point, Offset start, Offset end) {
final double dx = end.dx - start.dx;
final double dy = end.dy - start.dy;
final double lengthSquared = dx * dx + dy * dy;
// 线0
if (lengthSquared == 0.0) {
return (point - start).distance;
}
// 线
// t是投影点在线段方向向量上的比例因子
final double t = ((point.dx - start.dx) * dx + (point.dy - start.dy) * dy) / lengthSquared;
// t限制在[0, 1]线
// t<0startt>1end
final double tClamped = t.clamp(0.0, 1.0);
final Offset projection = Offset(start.dx + tClamped * dx, start.dy + tClamped * dy);
return (point - projection).distance;
}
/// 使RDP简化算法
///
/// [points]:
/// [epsilon]:
///
static List<Offset> simplify(List<Offset> points, double epsilon) {
final int totalPoints = points.length;
//
if (totalPoints < 3) {
return points;
}
// 使
final List<List<int>> stack = [];
// `keepPoint`
final List<bool> keepPoint = List.filled(totalPoints, false);
//
keepPoint[0] = true;
keepPoint[totalPoints - 1] = true;
// [0, totalPoints - 1]
stack.add([0, totalPoints - 1]);
while (stack.isNotEmpty) {
final List<int> currentSegment = stack.removeLast();
final int startIndex = currentSegment[0];
final int endIndex = currentSegment[1];
double maxDistance = 0.0;
int farthestIndex = 0;
// 线startIndex和endIndex构成
for (int i = startIndex + 1; i < endIndex; i++) {
final double distance = _getPerpendicularDistance(points[i], points[startIndex], points[endIndex]);
if (distance > maxDistance) {
maxDistance = distance;
farthestIndex = i;
}
}
// epsilon
if (maxDistance > epsilon) {
//
keepPoint[farthestIndex] = true;
// 线
//
if (farthestIndex - startIndex > 1) {
stack.add([startIndex, farthestIndex]);
}
//
if (endIndex - farthestIndex > 1) {
stack.add([farthestIndex, endIndex]);
}
}
}
//
final List<Offset> simplifiedPoints = [];
for (int i = 0; i < totalPoints; i++) {
if (keepPoint[i]) {
simplifiedPoints.add(points[i]);
}
}
return simplifiedPoints;
}
}

View File

@ -0,0 +1,412 @@
// shape_recognizer.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'ramer_douglas_peucker.dart';
///
enum GestureType {
none, //
check, // ()
cross, // (X)
halfCheck, // ( x)线
slash, // 线 (\ /)线线
}
///
/// Offset点
/// RDP算法线
class ShapeRecognizer {
// --- ---
/// Ramer-Douglas-Peucker epsilon
final double rdpEpsilon;
///
final double checkAngleMin;
///
final double checkAngleMax;
/// 线
final double crossAngleMin;
/// 线
final double crossAngleMax;
/// 线线
/// 0.15 线 15%
final double minIntersectionDistance;
///
///
final double checkLengthRatio;
/// 线线线
final double slashSlopeMin;
/// 线线线
final double slashSlopeMax;
/// 线线线
final double slashRadianMax;
///
ShapeRecognizer({
this.rdpEpsilon = 8.0,
this.checkAngleMin = 60.0,
this.checkAngleMax = 140.0,
this.crossAngleMin = 40.0,
this.crossAngleMax = 160.0,
this.minIntersectionDistance = 0.15,
this.checkLengthRatio = 1.1,
this.slashSlopeMin = 0.5,
this.slashSlopeMax = 2.0,
this.slashRadianMax = 140,
});
/// --- ---
///
/// 122
/// GestureType GestureType.none
GestureType recognize(List<List<Offset>> strokes) {
if (strokes.isEmpty) return GestureType.none; //
switch (strokes.length) {
case 1:
/// () 线 (\ /)
final points = strokes.first; //
if (points.length < 2) return GestureType.none; // 2线
var result = _recognizeCheck(points); //
if (result != GestureType.none) return result;
return _recognizeSlash(points); // 线
case 2:
// []
//
var halfCheckResult = _recognizeHalfCheck(strokes);
if (halfCheckResult != GestureType.none) return halfCheckResult;
//
return _recognizeCross(strokes);
// // (X) ( + 线)
// var crossResult = _recognizeCross(strokes); //
// if (crossResult != GestureType.none) return crossResult;
// return _recognizeHalfCheck(strokes); //
default:
return GestureType.none; //
}
}
/// --- ---
///
/// `_getCheckmarkDetails` `GestureType.check`
GestureType _recognizeCheck(List<Offset> rawPoints) {
return _getCheckmarkDetails(rawPoints) != null ? GestureType.check : GestureType.none;
}
/// 线
/// `_getSlashDetails` 线 `GestureType.slash`
GestureType _recognizeSlash(List<Offset> rawPoints) {
return _getSlashDetails(rawPoints) ? GestureType.slash : GestureType.none;
}
///
/// `_getCrossDetails` `GestureType.cross`
GestureType _recognizeCross(List<List<Offset>> rawStrokes) {
//
return _getCrossDetails(rawStrokes) != null ? GestureType.cross : GestureType.none;
}
///
/// 线
GestureType _recognizeHalfCheck(List<List<Offset>> strokes) {
if (strokes.length != 2) return GestureType.none;
/// 线
var firstStroke = strokes[0];
var lastStroke = strokes[1];
/// ,线
List<Offset>? checkDetails = _getCheckmarkDetails(firstStroke);
if (checkDetails == null) {
///
checkDetails = _getCheckmarkDetails(lastStroke);
if (checkDetails == null) {
return GestureType.none;
} else {
// checkDetails
final temp = firstStroke;
firstStroke = lastStroke;
lastStroke = temp;
}
}
// 1 stroke1 stroke2 线
if (_isHalfCheckCombination(
checkCandidate: firstStroke, crosserCandidate: lastStroke, checkDetails: checkDetails)) {
return GestureType.halfCheck;
}
return GestureType.none;
// if (twoStrokes.length != 2) return GestureType.none;
// final stroke1 = twoStrokes[0];
// final stroke2 = twoStrokes[1];
// // 1: stroke1是勾stroke2是交叉线
// final checkDetails1 = _getCheckmarkDetails(stroke1);
// if (checkDetails1 != null) {
// if (_checkHalfCheckLogic(checkDetails1, stroke2)) {
// return GestureType.halfCheck;
// }
// }
// // 2: stroke2是勾stroke1是交叉线
// final checkDetails2 = _getCheckmarkDetails(stroke2);
// if (checkDetails2 != null) {
// if (_checkHalfCheckLogic(checkDetails2, stroke1)) {
// return GestureType.halfCheck;
// }
// }
// return GestureType.none;
}
///
/// [checkCandidate]
/// [crosserCandidate] 线
/// [checkDetails]
bool _isHalfCheckCombination({
required List<Offset> checkCandidate,
required List<Offset> crosserCandidate,
required List<Offset> checkDetails,
}) {
//
final checkCorner = checkDetails[1];
final checkEndpoint = checkDetails[2];
final simplifiedCrosser = RamerDouglasPeucker.simplify(crosserCandidate, rdpEpsilon);
if (simplifiedCrosser.length < 2) return false;
// 2线
// 使
final intersection = _findIntersectionNearTip(
simplifiedCrosser.first, simplifiedCrosser.last, //
checkCorner, checkEndpoint, //
);
if (intersection != null) {
//
final vCross = simplifiedCrosser.last - simplifiedCrosser.first;
final vCheckLeg = checkEndpoint - checkCorner;
final angle = _calculateAngleBetweenVectors(vCross, vCheckLeg);
if (angle > crossAngleMin && angle < crossAngleMax) {
return true; //
}
}
return false; //
}
/// []
/// `checkLeg` `crossLine`
Offset? _findIntersectionNearTip(
Offset crossLineStart, Offset crossLineEnd, Offset checkLegStart, Offset checkLegEnd) {
final s1X = crossLineEnd.dx - crossLineStart.dx;
final s1Y = crossLineEnd.dy - crossLineStart.dy;
final s2X = checkLegEnd.dx - checkLegStart.dx;
final s2Y = checkLegEnd.dy - checkLegStart.dy;
final denominator = -s2X * s1Y + s1X * s2Y;
if (denominator.abs() < 1e-6) return null;
// t crossLine
final t =
(s2X * (crossLineStart.dy - checkLegStart.dy) - s2Y * (crossLineStart.dx - checkLegStart.dx)) / denominator;
// s checkLeg
final s =
(-s1Y * (crossLineStart.dx - checkLegStart.dx) + s1X * (crossLineStart.dy - checkLegStart.dy)) / denominator;
// 1: 线穿
final isCrosserIntersectedWell = t > minIntersectionDistance && t < (1 - minIntersectionDistance);
print("minIntersectionDistance: $minIntersectionDistance, t: $t");
// 2: (s > 0.1) (s <= 1.0)
final isCheckLegIntersectedNearTip = s >= 0.1 && s <= 1.0;
print(
"isCheckLegIntersectedNearTip: $isCheckLegIntersectedNearTip,isCrosserIntersectedWell: $isCrosserIntersectedWell, s: $s");
if (isCrosserIntersectedWell && isCheckLegIntersectedNearTip) {
return Offset(crossLineStart.dx + (t * s1X), crossLineStart.dy + (t * s1Y));
}
return null;
}
/// --- ---
///
/// `null`
List<Offset>? _getCheckmarkDetails(List<Offset> rawPoints) {
// 1. 使 RDP
final simplifiedPoints = RamerDouglasPeucker.simplify(rawPoints, rdpEpsilon);
// 3
if (simplifiedPoints.length < 3) return null;
// 2.
int cornerIndex = -1; //
double minAngle = 180.0; //
for (int i = 1; i < simplifiedPoints.length - 1; i++) {
//
final angle = _calculateAngle(simplifiedPoints[i - 1], simplifiedPoints[i], simplifiedPoints[i + 1]);
if (angle < minAngle) {
minAngle = angle;
cornerIndex = i;
}
}
// 3.
if (cornerIndex == -1 || minAngle < checkAngleMin || minAngle > checkAngleMax) return null;
// 4.
final p1 = simplifiedPoints.first, p2 = simplifiedPoints[cornerIndex], p3 = simplifiedPoints.last;
// 5. (p1-p2) (p2-p3)
// p2 p1 p3 p2 `p3.dy < p2.dy` V字
// p2.dy > p1.dy p2在p1下方p3.dy < p2.dy p3在p2上方
if (p2.dy > p1.dy && p3.dy < p2.dy) {
final length1 = (p2 - p1).distance, length2 = (p3 - p2).distance; // 线
// 6. `length2` `length1` `checkLengthRatio`
if (length1 > 0 && length2 > length1 * checkLengthRatio) return [p1, p2, p3]; //
}
return null; //
}
/// 线线
/// 线 RDP
bool _getSlashDetails(List<Offset> points) {
final simplified = RamerDouglasPeucker.simplify(points, rdpEpsilon);
// 1. 2线线
if (simplified.length == 2) {
final p1 = simplified.first, p2 = simplified.last;
final deltaX = (p2.dx - p1.dx).abs();
final deltaY = (p2.dy - p1.dy).abs();
if (deltaX < 1e-6) return true;
if (deltaY < 1e-6) return false;
final slope = deltaY / deltaX;
return slope > slashSlopeMin && slope < slashSlopeMax;
}
// 2. 3线
if (simplified.length == 3) {
final p1 = simplified[0];
final p2 = simplified[1]; //
final p3 = simplified[2];
//
final angle = _calculateAngle(p1, p2, p3);
// > 160线
// 线线
// 160
if (angle > slashRadianMax) {
// 线
final overallP1 = points.first;
final overallP3 = points.last;
final deltaX = (overallP3.dx - overallP1.dx).abs();
final deltaY = (overallP3.dy - overallP1.dy).abs();
if (deltaX < 1e-6) return true;
if (deltaY < 1e-6) return false;
final slope = deltaY / deltaX;
return slope > slashSlopeMin && slope < slashSlopeMax;
}
}
// 3. 14线
return false;
}
/// null
/// 线
Offset? _getCrossDetails(List<List<Offset>> twoStrokes) {
if (twoStrokes.length != 2) return null; //
//
final simplifiedStroke1 = RamerDouglasPeucker.simplify(twoStrokes[0], rdpEpsilon);
final simplifiedStroke2 = RamerDouglasPeucker.simplify(twoStrokes[1], rdpEpsilon);
if (simplifiedStroke1.length < 2 || simplifiedStroke2.length < 2) return null; // 线2
// 线使
final intersection = _findIntersection(
simplifiedStroke1.first, simplifiedStroke1.last, simplifiedStroke2.first, simplifiedStroke2.last);
if (intersection == null) return null; //
// 线
final angle = _calculateAngleBetweenVectors(
simplifiedStroke1.last - simplifiedStroke1.first, // 线
simplifiedStroke2.last - simplifiedStroke2.first); // 线
if (angle > crossAngleMin && angle < crossAngleMax) return intersection; //
return null;
}
/// --- ---
/// `p1``p2``p3` 线 `p2`
/// `p1-p2` `p3-p2`
double _calculateAngle(Offset p1, Offset p2, Offset p3) {
return _calculateAngleBetweenVectors(p1 - p2, p3 - p2);
}
/// `v1` `v2`
/// 使 `cosTheta`
double _calculateAngleBetweenVectors(Offset v1, Offset v2) {
final double dotProduct = v1.dx * v2.dx + v1.dy * v2.dy; //
final double magnitude1 = v1.distance; // v1
final double magnitude2 = v2.distance; // v2
if (magnitude1 == 0 || magnitude2 == 0) return 0; // 00
// cosTheta [-1, 1] acos
final cosTheta = (dotProduct / (magnitude1 * magnitude2)).clamp(-1.0, 1.0);
return acos(cosTheta) * 180 / pi; //
}
/// 线 (p1, p2) (p3, p4)
/// 使线
/// 线线线线 `null`
Offset? _findIntersection(Offset p1, Offset p2, Offset p3, Offset p4) {
final double s1X = p2.dx - p1.dx, s1Y = p2.dy - p1.dy; // 线
final double s2X = p4.dx - p3.dx, s2Y = p4.dy - p3.dy; // 线
final double denominator = -s2X * s1Y + s1X * s2Y; // 线
if (denominator == 0) return null; // 线线
// s t
// s 线(p3, p4)
final double s = (-s1Y * (p1.dx - p3.dx) + s1X * (p1.dy - p3.dy)) / denominator;
// t 线(p1, p2)
final double t = (s2X * (p1.dy - p3.dy) - s2Y * (p1.dx - p3.dx)) / denominator;
// 线线
// `s` `t` `minIntersectionDistance` `1 - minIntersectionDistance`
// 线
if (s > minIntersectionDistance &&
s < (1 - minIntersectionDistance) &&
t > minIntersectionDistance &&
t < (1 - minIntersectionDistance)) {
return Offset(p1.dx + (t * s1X), p1.dy + (t * s1Y)); //
}
return null; // 线
}
}

View File

@ -2,14 +2,16 @@ import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart'; import 'package:get_storage/get_storage.dart';
class StorageService extends GetxService { class StorageService extends GetxService {
static StorageService get to => Get.find(); static StorageService get to => Get.find<StorageService>();
late final GetStorage _getStorage; late final GetStorage _getStorage;
get storage => _getStorage; get storage => _getStorage;
Future<StorageService> init() async { ///
StorageService._(this._getStorage);
static Future<StorageService> init() async {
await GetStorage.init(); await GetStorage.init();
_getStorage = GetStorage(); return StorageService._(GetStorage());
return this;
} }
T? read<T>(String key) { T? read<T>(String key) {

View File

@ -8,9 +8,9 @@
*/ */
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
class ToastUtils { class ToastUtils {
static void getFluttertoast({ static void getFluttertoast({
@ -60,8 +60,16 @@ class ToastUtils {
EasyLoading.showError(showMsg); EasyLoading.showError(showMsg);
} }
static showLoading() { static showLoading({
EasyLoading.show(status: 'loading...'); bool? dismissOnTap = false,
//
EasyLoadingMaskType? maskType = EasyLoadingMaskType.clear,
}) {
EasyLoading.show(
maskType: maskType, // 使
dismissOnTap: dismissOnTap,
status: 'loading...',
);
} }
static showInfo(String showMsg, {Duration? duration}) { static showInfo(String showMsg, {Duration? duration}) {

View File

@ -1,8 +1,11 @@
import 'dart:io'; import 'dart:io';
import 'package:auto_updater/auto_updater.dart'; import 'package:auto_updater/auto_updater.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_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_native_splash/flutter_native_splash.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:making_school_asignment_app/common/config/app_config.dart'; import 'package:making_school_asignment_app/common/config/app_config.dart';
@ -11,25 +14,20 @@ import 'package:making_school_asignment_app/common/store/user_store.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/utils.dart'; 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 'package:flutter_easyloading/flutter_easyloading.dart';
import 'common/config/request_config.dart'; import 'common/config/request_config.dart';
import 'common/utils/app_upgrade/upgradeLogic.dart'; import 'common/utils/app_upgrade/upgradeLogic.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
void main() async { void main() async {
// Get // Get
Get.testMode = true; Get.testMode = true;
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); FlutterNativeSplash.preserve(widgetsBinding: WidgetsFlutterBinding.ensureInitialized());
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
/// ///
await Get.putAsync(() => StorageService().init()); await Get.putAsync<StorageService>(() => StorageService.init());
/// UserStore /// UserStore
Get.put<UserStore>(UserStore().init()); await Get.putAsync<UserStore>(() async => UserStore().init());
Get.put(UpgradeLogic());
WidgetsFlutterBinding.ensureInitialized();
// Windows // Windows
if (Platform.isWindows) { if (Platform.isWindows) {
@ -46,6 +44,8 @@ void main() async {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.top]); // SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.top]); //
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); // await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); //
runApp(const MyApp()); runApp(const MyApp());
Future.delayed(const Duration(seconds: 3), () => FlutterNativeSplash.remove());
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
@ -121,12 +121,13 @@ class MyApp extends StatelessWidget {
initialRoute: Routes.startPage, initialRoute: Routes.startPage,
/// ///
initialBinding: MainBinding(),
getPages: AppPages.pages, getPages: AppPages.pages,
builder: EasyLoading.init( builder: EasyLoading.init(
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(textScaleFactor: 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),
), ),
@ -138,3 +139,12 @@ class MyApp extends StatelessWidget {
); );
} }
} }
/// MainBinding
class MainBinding implements Bindings {
@override
void dependencies() async {
/// APP
Get.put(UpgradeLogic(), permanent: true);
}
}

View File

@ -0,0 +1,300 @@
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:making_school_asignment_app/common/utils/gesture_recognition/ramer_douglas_peucker.dart';
import 'package:making_school_asignment_app/common/utils/gesture_recognition/shape_recognizer.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(375, 812),
builder: () => MaterialApp(
title: '手势识别实验',
theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
home: const GestureCanvasPage(),
),
);
}
}
class GestureCanvasPage extends StatefulWidget {
const GestureCanvasPage({super.key});
@override
State<GestureCanvasPage> createState() => _GestureCanvasPageState();
}
class _GestureCanvasPageState extends State<GestureCanvasPage> {
final List<List<Offset>> _allStrokes = [];
final _currentStrokeNotifier = ValueNotifier<List<Offset>>([]);
ui.Image? _cachedImage;
GestureType _recognizedGesture = GestureType.none;
late ShapeRecognizer _recognizer;
// []
bool _showSimplifiedPoints = true;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
// 便
_recognizer = ShapeRecognizer(
rdpEpsilon: 8.r, //
///
checkAngleMin: 30.0,
///
checkAngleMax: 150.0,
crossAngleMin: 30.0,
crossAngleMax: 160.0,
minIntersectionDistance: 0.1,
checkLengthRatio: 1.1,
// [] 线
slashSlopeMin: 0.2, // 线11线
slashSlopeMax:
5.0, // 线79线线 _isSlash 线 (deltaX < 1e-6)线
);
}
});
}
Future<void> _updateCachedImage() async {
final recorder = ui.PictureRecorder();
final canvas = Canvas(recorder);
final size = context.size!;
_GestureCachePainter(allStrokes: _allStrokes).paint(canvas, size);
final picture = recorder.endRecording();
final newImage = await picture.toImage(size.width.toInt(), size.height.toInt());
if (mounted) {
setState(() {
_cachedImage = newImage;
});
}
}
void _onPanStart(DragStartDetails details) {
_currentStrokeNotifier.value = [details.localPosition];
}
void _onPanUpdate(DragUpdateDetails details) {
_currentStrokeNotifier.value = List.from(_currentStrokeNotifier.value)..add(details.localPosition);
}
void _onPanEnd(DragEndDetails details) {
if (_currentStrokeNotifier.value.isNotEmpty) {
_allStrokes.add(List.from(_currentStrokeNotifier.value));
_currentStrokeNotifier.value = [];
_updateCachedImage().then((_) {
if (mounted) {
setState(() {
_recognizedGesture = _recognizer.recognize(_allStrokes);
print('识别到的手势: $_recognizedGesture');
});
}
});
}
}
void _clearCanvas() {
setState(() {
_allStrokes.clear();
_recognizedGesture = GestureType.none;
_cachedImage = null;
});
_currentStrokeNotifier.value = [];
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('手势识别 (可视化调试)'),
actions: [
// [] AppBar中添加一个信息提示
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Icon(
Icons.science_outlined,
color: _showSimplifiedPoints ? Colors.green : Colors.grey,
),
)
],
),
body: Stack(
children: [
GestureDetector(
onPanStart: _onPanStart,
onPanUpdate: _onPanUpdate,
onPanEnd: _onPanEnd,
child: ValueListenableBuilder<List<Offset>>(
valueListenable: _currentStrokeNotifier,
builder: (context, currentStroke, child) {
return CustomPaint(
painter: _GestureCanvasPainter(
cachedImage: _cachedImage,
currentStroke: currentStroke,
// [] Painter
allStrokes: _allStrokes,
rdpEpsilon: _recognizer.rdpEpsilon,
showSimplifiedPoints: _showSimplifiedPoints,
),
size: Size.infinite,
);
},
),
),
Align(
alignment: Alignment.topLeft,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'识别结果: ${_recognizedGesture.toString().split('.').last}\nEpsilon: ${_recognizer.rdpEpsilon.toStringAsFixed(2)}',
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.blue),
),
),
),
],
),
// [] 使Column包裹多个FAB
floatingActionButton: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: _clearCanvas,
tooltip: '清空画布',
child: const Icon(Icons.clear),
),
const SizedBox(height: 10),
// []
FloatingActionButton(
onPressed: () {
setState(() {
_showSimplifiedPoints = !_showSimplifiedPoints;
});
},
tooltip: '切换特征点显示',
backgroundColor: _showSimplifiedPoints ? Colors.green : Colors.grey[400],
child: const Icon(Icons.science_outlined),
),
],
),
);
}
}
class _GestureCanvasPainter extends CustomPainter {
final ui.Image? cachedImage;
final List<Offset> currentStroke;
final Paint _paint;
// []
final List<List<Offset>> allStrokes;
final double rdpEpsilon;
final bool showSimplifiedPoints;
final Paint _debugPaint; // []
_GestureCanvasPainter({
required this.cachedImage,
required this.currentStroke,
required this.allStrokes,
required this.rdpEpsilon,
required this.showSimplifiedPoints,
}) : _paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0
..style = PaintingStyle.stroke,
// []
_debugPaint = Paint()
..color = Colors.red
..strokeCap = StrokeCap.round
..strokeWidth = 8.0;
@override
void paint(Canvas canvas, Size size) {
// 1: ()
if (cachedImage != null) {
canvas.drawImage(cachedImage!, Offset.zero, Paint());
}
// 2:
if (currentStroke.length > 1) {
final path = Path();
path.moveTo(currentStroke.first.dx, currentStroke.first.dy);
for (int i = 1; i < currentStroke.length; i++) {
path.lineTo(currentStroke[i].dx, currentStroke[i].dy);
}
canvas.drawPath(path, _paint);
}
// [] 3:
if (showSimplifiedPoints) {
//
for (final stroke in allStrokes) {
if (stroke.length > 1) {
final simplifiedPoints = RamerDouglasPeucker.simplify(stroke, rdpEpsilon);
canvas.drawPoints(ui.PointMode.points, simplifiedPoints, _debugPaint);
}
}
//
if (currentStroke.length > 1) {
final simplifiedPoints = RamerDouglasPeucker.simplify(currentStroke, rdpEpsilon);
canvas.drawPoints(ui.PointMode.points, simplifiedPoints, _debugPaint);
}
}
}
@override
bool shouldRepaint(covariant _GestureCanvasPainter oldDelegate) {
// []
return oldDelegate.currentStroke != currentStroke ||
oldDelegate.showSimplifiedPoints != showSimplifiedPoints ||
oldDelegate.allStrokes.length != allStrokes.length;
}
}
//
class _GestureCachePainter extends CustomPainter {
final List<List<Offset>> allStrokes;
final Paint _paint;
_GestureCachePainter({required this.allStrokes})
: _paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0
..style = PaintingStyle.stroke;
@override
void paint(Canvas canvas, Size size) {
for (final stroke in allStrokes) {
if (stroke.length > 1) {
final path = Path();
path.moveTo(stroke.first.dx, stroke.first.dy);
for (int i = 1; i < stroke.length; i++) {
path.lineTo(stroke[i].dx, stroke[i].dy);
}
canvas.drawPath(path, _paint);
}
}
}
@override
bool shouldRepaint(covariant _GestureCachePainter oldDelegate) {
return oldDelegate.allStrokes != allStrokes;
}
}

View File

@ -18,7 +18,7 @@ class ReturnToHomepage extends StatelessWidget {
child: Container( child: Container(
padding: EdgeInsets.only(right: 4.5.w), padding: EdgeInsets.only(right: 4.5.w),
alignment: Alignment.center, alignment: Alignment.center,
child: Icon(Icons.home_rounded, size: 22.sp, color: bgColor), child: Icon(Icons.home_rounded, size: 30.r, color: bgColor),
), ),
); );
} }

View File

@ -18,6 +18,7 @@ Text quickText(text,
FontWeight? fontWeight, FontWeight? fontWeight,
TextOverflow overflow = TextOverflow.ellipsis, TextOverflow overflow = TextOverflow.ellipsis,
int maxLines = 1, int maxLines = 1,
double? height,
TextDecoration decoration = TextDecoration.none}) { TextDecoration decoration = TextDecoration.none}) {
return Text( return Text(
text.toString(), text.toString(),
@ -30,6 +31,7 @@ Text quickText(text,
color: color, color: color,
fontWeight: fontWeight, fontWeight: fontWeight,
wordSpacing: wordSpacing, wordSpacing: wordSpacing,
height: height,
), ),
); );
} }

View File

@ -1,21 +1,20 @@
import 'dart:async'; import 'dart:async';
import 'package:get/get.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:making_school_asignment_app/routes/app_pages.dart'; import 'package:get/get.dart';
import 'package:making_school_asignment_app/common/job/user_info.dart'; import 'package:making_school_asignment_app/common/job/user_info.dart';
import 'package:making_school_asignment_app/common/utils/storage.dart';
import 'package:making_school_asignment_app/common/store/user_store.dart';
import 'package:making_school_asignment_app/common/utils/toast_utils.dart';
import 'package:making_school_asignment_app/page/home_page/home_view.dart';
import 'package:making_school_asignment_app/page/home_page/home_logic.dart';
import 'package:making_school_asignment_app/page/work_page/work_logic.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/page/home_page/children/my_info.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/utils/app_upgrade/upgradeLogic.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/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/home_view.dart';
import 'package:making_school_asignment_app/routes/app_pages.dart';
class StartPage extends StatefulWidget { class StartPage extends StatefulWidget {
const StartPage({super.key}); const StartPage({super.key});
@ -37,14 +36,13 @@ class _StartPageState extends State<StartPage> with RequestToolMixin {
void initState() { void initState() {
super.initState(); super.initState();
Future.delayed(const Duration(seconds: 3), () => FlutterNativeSplash.remove());
// const WorkPage(), // const WorkPage(),
_bodyList = [const HomePage(), const MyInfo()]; _bodyList = [const HomePage(), const MyInfo()];
// APP // APP
WidgetsBinding.instance.addPostFrameCallback((e) { WidgetsBinding.instance.addPostFrameCallback((e) {
/// 40APP /// 40APP
if (!_upgradeLogic.showUpgrade.value && UserStore.to.userDetailInfo.value != null) _upgradeLogic.getAppUpgrade(context); if (!_upgradeLogic.showUpgrade.value && UserStore.to.userDetailInfo.value != null)
_upgradeLogic.getAppUpgrade(context);
}); });
_timer?.cancel(); _timer?.cancel();
_timer = Timer.periodic(const Duration(seconds: 40), (e) { _timer = Timer.periodic(const Duration(seconds: 40), (e) {
@ -91,7 +89,8 @@ class _StartPageState extends State<StartPage> with RequestToolMixin {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return WillPopScope( return PopScope(
canPop: false,
child: Scaffold( child: Scaffold(
body: PageView( body: PageView(
controller: _pageController._pageIndexState.pageController, controller: _pageController._pageIndexState.pageController,
@ -141,15 +140,19 @@ class _StartPageState extends State<StartPage> with RequestToolMixin {
); );
}), }),
), ),
onWillPop: () 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(context: context, msg: '连续两次返回就退出'); ToastUtils.getFluttertoast(
return Future.value(false); context: context,
msg: '连续两次返回就退出',
gravity: ToastGravity.CENTER,
fontSize: 18,
);
} else { } else {
lastPopTime = DateTime.now(); lastPopTime = DateTime.now();
// 退app // 退app
return Future.value(true); SystemNavigator.pop();
} }
}, },
); );

View File

@ -1,3 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart'; import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@ -35,8 +37,7 @@ class AnnotateClassLogic extends GetxController with RequestToolMixin {
} }
void getList() async { void getList() async {
List<AnnotatedClass> data = List<AnnotatedClass> data = await getClient().getAnnotatedClassList(state.homeworkId.value);
await getClient().getAnnotatedClassList(state.homeworkId.value);
state.classList.value = data; state.classList.value = data;
/* print('state.classList[0]=${state.classList[0].finishTime}'); /* print('state.classList[0]=${state.classList[0].finishTime}');
print('state.classList[1]=${state.classList[1].finishTime}');*/ print('state.classList[1]=${state.classList[1].finishTime}');*/
@ -61,9 +62,7 @@ class AnnotateClassLogic extends GetxController with RequestToolMixin {
void getAllCorrect(classId) async { void getAllCorrect(classId) async {
EasyLoading.show(status: 'loading...'); EasyLoading.show(status: 'loading...');
try { try {
await getClient() await getClient().getAllCorrect(state.homeworkId.value, classId).then((e) {
.getAllCorrect(state.homeworkId.value, classId)
.then((e) {
getList(); getList();
}); });
} catch (e) { } catch (e) {
@ -108,4 +107,25 @@ class AnnotateClassLogic extends GetxController with RequestToolMixin {
super.dispose(); super.dispose();
refreshController.dispose(); refreshController.dispose();
} }
///
/// [homeworkId]
/// [homeworkName]
/// [classId]
/// [subject]
void toGoReviewhomework({required homeworkId, required homeworkName, required classId, required subject}) async {
// UI
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky, overlays: <SystemUiOverlay>[]);
WidgetsBinding.instance.addPostFrameCallback((_) async {
await Future.delayed(const Duration(milliseconds: 300));
await Get.toNamed(Routes.reviewHomework, arguments: {
'homeworkId': homeworkId,
'homeworkName': homeworkName,
'classId': classId,
'subject': subject
})?.then((value) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge, overlays: [SystemUiOverlay.top]);
});
});
}
} }

View File

@ -1,18 +1,17 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_easyloading/flutter_easyloading.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:making_school_asignment_app/common/utils/anti_shake_throttling.dart';
import 'package:making_school_asignment_app/page/global_widget/show_student_list.dart';
import 'package:percent_indicator/percent_indicator.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/common/utils/anti_shake_throttling.dart';
import 'package:making_school_asignment_app/common/utils/enum_untils.dart'; import 'package:making_school_asignment_app/common/utils/enum_untils.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/global_widget/show_student_list.dart';
import 'package:making_school_asignment_app/page/home_page/children/annotate_class/annotate_class_logic.dart'; import 'package:making_school_asignment_app/page/home_page/children/annotate_class/annotate_class_logic.dart';
import 'package:making_school_asignment_app/page/home_page/children/annotate_class/widget/item_btn.dart'; import 'package:making_school_asignment_app/page/home_page/children/annotate_class/widget/item_btn.dart';
import 'package:making_school_asignment_app/page/home_page/widget/progress_bar.dart'; import 'package:making_school_asignment_app/page/home_page/widget/progress_bar.dart';
import 'package:making_school_asignment_app/routes/app_pages.dart'; import 'package:making_school_asignment_app/routes/app_pages.dart';
import 'package:percent_indicator/percent_indicator.dart';
class AnnotateItem extends StatefulWidget { class AnnotateItem extends StatefulWidget {
final String homeworkId; final String homeworkId;
@ -20,7 +19,13 @@ class AnnotateItem extends StatefulWidget {
final double font; final double font;
final String name; final String name;
final AnnotateClassLogic logic; final AnnotateClassLogic logic;
const AnnotateItem({super.key, required this.homeworkId, required this.item, required this.font, required this.name, required this.logic}); const AnnotateItem(
{super.key,
required this.homeworkId,
required this.item,
required this.font,
required this.name,
required this.logic});
@override @override
State<AnnotateItem> createState() => _AnnotateItemState(); State<AnnotateItem> createState() => _AnnotateItemState();
@ -159,10 +164,16 @@ class _AnnotateItemState extends State<AnnotateItem> {
Expanded( Expanded(
flex: 4, flex: 4,
child: ItemBtn( child: ItemBtn(
title: "收藏夹${widget.item.homeworkFavs.isNotEmpty ? '(${widget.item.homeworkFavs.length})' : ''}", title:
"收藏夹${widget.item.homeworkFavs.isNotEmpty ? '(${widget.item.homeworkFavs.length})' : ''}",
font: widget.font - 2.sp, font: widget.font - 2.sp,
clickFunction: () { clickFunction: () {
Get.toNamed(Routes.favStudentPage, arguments: {'homeworkName': widget.name, 'classId': widget.item.classId, 'homeworkId': widget.logic.state.homeworkId.value, 'grade': widget.item.grade}); Get.toNamed(Routes.favStudentPage, arguments: {
'homeworkName': widget.name,
'classId': widget.item.classId,
'homeworkId': widget.logic.state.homeworkId.value,
'grade': widget.item.grade
});
}, },
), ),
), ),
@ -207,10 +218,16 @@ class _AnnotateItemState extends State<AnnotateItem> {
Expanded( Expanded(
flex: 4, flex: 4,
child: ItemBtn( child: ItemBtn(
title: "收藏夹${widget.item.homeworkFavs.isNotEmpty ? '(${widget.item.homeworkFavs.length})' : ''}", title:
"收藏夹${widget.item.homeworkFavs.isNotEmpty ? '(${widget.item.homeworkFavs.length})' : ''}",
font: widget.font - 2.sp, font: widget.font - 2.sp,
clickFunction: () { clickFunction: () {
Get.toNamed(Routes.favStudentPage, arguments: {'homeworkName': widget.name, 'classId': widget.item.classId, 'homeworkId': widget.logic.state.homeworkId.value, 'grade': widget.item.grade}); Get.toNamed(Routes.favStudentPage, arguments: {
'homeworkName': widget.name,
'classId': widget.item.classId,
'homeworkId': widget.logic.state.homeworkId.value,
'grade': widget.item.grade
});
}, },
), ),
), ),
@ -221,7 +238,10 @@ class _AnnotateItemState extends State<AnnotateItem> {
padding: EdgeInsets.only(top: 10.r, left: 14.r, right: 14.r), padding: EdgeInsets.only(top: 10.r, left: 14.r, right: 14.r),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [quickText('批阅进度', size: widget.font - 2.sp, color: const Color(0xFF8E8E8E)), quickText('${widget.item.annotateRate}%', size: widget.font + 10.sp, color: const Color(0xFF464646))], children: [
quickText('批阅进度', size: widget.font - 2.sp, color: const Color(0xFF8E8E8E)),
quickText('${widget.item.annotateRate}%', size: widget.font + 10.sp, color: const Color(0xFF464646))
],
), ),
), ),
Padding( Padding(
@ -249,9 +269,27 @@ class _AnnotateItemState extends State<AnnotateItem> {
barRadius: Radius.circular(10.r), barRadius: Radius.circular(10.r),
), ),
), ),
ProgressBar(title: '客观题正确率:', color: const Color(0xFFADDCA5), percent: widget.item.kgtCorrectRate / 100, marginEdg: EdgeInsets.zero, padingEdg: EdgeInsets.only(top: 8.h, left: 14.r, right: 14.r), fontSize: widget.font - 2.sp), ProgressBar(
ProgressBar(title: '主观题正确率:', color: const Color(0xFFADDCA5), percent: widget.item.zgtCorrectRate / 100, padingEdg: EdgeInsets.symmetric(horizontal: 10.r), marginEdg: EdgeInsets.only(top: 8.h), fontSize: widget.font - 2.sp), title: '客观题正确率:',
ProgressBar(title: '总正确率:', color: const Color(0xFFADDCA5), percent: widget.item.correctRate / 100, padingEdg: EdgeInsets.symmetric(horizontal: 10.r), marginEdg: EdgeInsets.only(top: 8.h), fontSize: widget.font - 2.sp), color: const Color(0xFFADDCA5),
percent: widget.item.kgtCorrectRate / 100,
marginEdg: EdgeInsets.zero,
padingEdg: EdgeInsets.only(top: 8.h, left: 14.r, right: 14.r),
fontSize: widget.font - 2.sp),
ProgressBar(
title: '主观题正确率:',
color: const Color(0xFFADDCA5),
percent: widget.item.zgtCorrectRate / 100,
padingEdg: EdgeInsets.symmetric(horizontal: 10.r),
marginEdg: EdgeInsets.only(top: 8.h),
fontSize: widget.font - 2.sp),
ProgressBar(
title: '总正确率:',
color: const Color(0xFFADDCA5),
percent: widget.item.correctRate / 100,
padingEdg: EdgeInsets.symmetric(horizontal: 10.r),
marginEdg: EdgeInsets.only(top: 8.h),
fontSize: widget.font - 2.sp),
Container( Container(
margin: EdgeInsets.only(top: 10.r), margin: EdgeInsets.only(top: 10.r),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -277,7 +315,9 @@ class _AnnotateItemState extends State<AnnotateItem> {
}, },
child: Container( child: Container(
padding: EdgeInsets.symmetric(vertical: 10.r), padding: EdgeInsets.symmetric(vertical: 10.r),
decoration: BoxDecoration(border: Border(right: BorderSide(width: 1.r, color: const Color.fromRGBO(221, 221, 221, 1)))), decoration: BoxDecoration(
border: Border(
right: BorderSide(width: 1.r, color: const Color.fromRGBO(221, 221, 221, 1)))),
alignment: Alignment.center, alignment: Alignment.center,
child: quickText('数据快查', color: const Color.fromRGBO(118, 118, 118, 1), size: widget.font), child: quickText('数据快查', color: const Color.fromRGBO(118, 118, 118, 1), size: widget.font),
), ),
@ -298,29 +338,20 @@ class _AnnotateItemState extends State<AnnotateItem> {
: [ : [
Expanded( Expanded(
child: InkWell( child: InkWell(
onTap: () => easyThrottle('TO_GO_REVIEWHOMEWORK', () async { onTap: () => easyThrottle('TO_GO_REVIEWHOMEWORK', () {
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); ///
WidgetsBinding.instance.addPostFrameCallback((_) { widget.logic.toGoReviewhomework(
Future.delayed(const Duration(milliseconds: 300), () { homeworkId: widget.homeworkId,
Get.toNamed(Routes.reviewHomework, arguments: { homeworkName: widget.name,
'homeworkId': widget.homeworkId, classId: itemData.classId,
'homeworkName': widget.name, subject: widget.logic.state.subject,
'classId': itemData.classId, );
'subject': widget.logic.state.subject,
});
});
});
// Get.toNamed(Routes.reviewHomework, arguments: {
// 'homeworkId': widget.homeworkId,
// 'homeworkName': widget.name,
// 'classId': itemData.classId,
// 'subject': widget.logic.state.subject,
// });
}), }),
child: Container( child: Container(
padding: EdgeInsets.symmetric(vertical: 10.r), padding: EdgeInsets.symmetric(vertical: 10.r),
decoration: BoxDecoration(border: Border(right: BorderSide(width: 1.r, color: const Color.fromRGBO(221, 221, 221, 1)))), decoration: BoxDecoration(
border: Border(
right: BorderSide(width: 1.r, color: const Color.fromRGBO(221, 221, 221, 1)))),
alignment: Alignment.center, alignment: Alignment.center,
child: quickText('批阅', color: const Color.fromRGBO(118, 118, 118, 1), size: widget.font), child: quickText('批阅', color: const Color.fromRGBO(118, 118, 118, 1), size: widget.font),
), ),

View File

@ -1,5 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:ffi';
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';
@ -19,7 +19,8 @@ class BottomAnnotationSwitch extends StatefulWidget {
State<BottomAnnotationSwitch> createState() => _BottomAnnotationSwitchJobState(); State<BottomAnnotationSwitch> createState() => _BottomAnnotationSwitchJobState();
} }
class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch> with SingleTickerProviderStateMixin, EventBusMixin<BottomOperationBar> { class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch>
with SingleTickerProviderStateMixin, EventBusMixin<BottomOperationBar> {
late AnimationController _animationController; // late AnimationController _animationController; //
final _homeworkLogic = Get.find<HomeworkReviewLogic>(); final _homeworkLogic = Get.find<HomeworkReviewLogic>();
final _logicControl = Get.find<HomeworkReviewLogic>().annotationState; final _logicControl = Get.find<HomeworkReviewLogic>().annotationState;
@ -65,7 +66,11 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch> with
Color actionColor = Colors.white; Color actionColor = Colors.white;
Color defaultColor = const Color.fromRGBO(132, 146, 163, 1); Color defaultColor = const Color.fromRGBO(132, 146, 163, 1);
return Container( return SafeArea(
left: false,
right: false,
top: false,
child: Container(
width: double.infinity, width: double.infinity,
height: _animationController.value * 64.h, height: _animationController.value * 64.h,
color: const Color.fromRGBO(83, 83, 83, 1), color: const Color.fromRGBO(83, 83, 83, 1),
@ -87,7 +92,8 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch> with
height: double.infinity, height: double.infinity,
child: TextButton( child: TextButton(
child: quickText('全 对', color: Colors.white, size: 12.sp), child: quickText('全 对', color: Colors.white, size: 12.sp),
onPressed: () => easyThrottle('homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.allPairs(context)), onPressed: () => easyThrottle('homework_bottom_operation_bar_scoring_related',
() => _homeworkLogic.allPairs(context)),
), ),
), ),
), ),
@ -148,7 +154,8 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch> with
width: double.infinity, width: double.infinity,
height: double.infinity, height: double.infinity,
child: TextButton( child: TextButton(
onPressed: () => easyThrottle('homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.allWrongRating(context)), onPressed: () => easyThrottle('homework_bottom_operation_bar_scoring_related',
() => _homeworkLogic.allWrongRating(context)),
child: quickText('全 错', color: Colors.white, size: 12.sp), child: quickText('全 错', color: Colors.white, size: 12.sp),
), ),
), ),
@ -164,7 +171,8 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch> with
'homework_bottom_action_bar_annotations', 'homework_bottom_action_bar_annotations',
() => eventFire(model: BottomOperationBar(revokeAll: false, revokePreStep: true)), () => eventFire(model: BottomOperationBar(revokeAll: false, revokePreStep: true)),
), ),
icon: Icon(const IconData(0xe638, fontFamily: "AlibabaIcon"), size: iconSize, color: defaultColor), icon: Icon(const IconData(0xe638, fontFamily: "AlibabaIcon"),
size: iconSize, color: defaultColor),
), ),
), ),
), ),
@ -175,7 +183,8 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch> with
width: double.infinity, width: double.infinity,
height: double.infinity, height: double.infinity,
child: IconButton( child: IconButton(
icon: Icon(const IconData(0xe637, fontFamily: "AlibabaIcon"), size: iconSize, color: defaultColor), icon: Icon(const IconData(0xe637, fontFamily: "AlibabaIcon"),
size: iconSize, color: defaultColor),
onPressed: () => easyThrottle( onPressed: () => easyThrottle(
'homework_bottom_action_bar_annotations', 'homework_bottom_action_bar_annotations',
() => eventFire(model: BottomOperationBar(revokeAll: true, revokePreStep: false)), () => eventFire(model: BottomOperationBar(revokeAll: true, revokePreStep: false)),
@ -199,7 +208,8 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch> with
width: double.infinity, width: double.infinity,
height: double.infinity, height: double.infinity,
child: TextButton( child: TextButton(
onPressed: () => easyThrottle('homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.cancelAllRatings()), onPressed: () => easyThrottle(
'homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.cancelAllRatings()),
child: quickText('取 消', size: 14.sp, color: Colors.white), child: quickText('取 消', size: 14.sp, color: Colors.white),
), ),
), ),
@ -210,7 +220,8 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch> with
width: double.infinity, width: double.infinity,
height: double.infinity, height: double.infinity,
child: TextButton( child: TextButton(
onPressed: () => easyThrottle('homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.submit(context)), onPressed: () => easyThrottle(
'homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.submit(context)),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
@ -219,7 +230,8 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch> with
if (_homeworkLogic.state.submitLoading.value) { if (_homeworkLogic.state.submitLoading.value) {
return Padding( return Padding(
padding: EdgeInsets.only(right: 4.w), padding: EdgeInsets.only(right: 4.w),
child: const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2)), child: const SizedBox(
width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2)),
); );
} }
return const SizedBox(); return const SizedBox();
@ -235,6 +247,6 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch> with
), ),
], ],
), ),
); ));
} }
} }

View File

@ -1,6 +1,6 @@
import 'package:get/get.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: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/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';
@ -19,7 +19,8 @@ class FavoriteWidget extends StatelessWidget {
return Container( return Container(
padding: EdgeInsets.symmetric(horizontal: 4.r), padding: EdgeInsets.symmetric(horizontal: 4.r),
child: InkWell( child: InkWell(
onTap: () => easyThrottle('homework_review_collect_btn', () => logic.toFavorite(), duration: const Duration(milliseconds: 500)), onTap: () => easyThrottle('homework_review_collect_btn', () => logic.toFavorite(),
duration: const Duration(milliseconds: 500)),
splashColor: Colors.transparent, splashColor: Colors.transparent,
highlightColor: Colors.transparent, highlightColor: Colors.transparent,
child: Row( child: Row(
@ -28,8 +29,10 @@ class FavoriteWidget extends StatelessWidget {
Obx(() { Obx(() {
return Icon( return Icon(
const IconData(0xe63c, fontFamily: "AlibabaIcon"), const IconData(0xe63c, fontFamily: "AlibabaIcon"),
size: 18.sp, size: 24.r,
color: sateData.favorite.value ? const Color.fromRGBO(255, 172, 48, 1) : const Color.fromRGBO(164, 164, 164, 1), color: sateData.favorite.value
? const Color.fromRGBO(255, 172, 48, 1)
: const Color.fromRGBO(164, 164, 164, 1),
); );
}), }),
SizedBox(width: 6.w), SizedBox(width: 6.w),

View File

@ -3,13 +3,14 @@ import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:functional_widget_annotation/functional_widget_annotation.dart'; import 'package:functional_widget_annotation/functional_widget_annotation.dart';
import 'package:get/get.dart';
import 'package:making_school_asignment_app/common/job/marking_models/do_paper_details_result.dart'; import 'package:making_school_asignment_app/common/job/marking_models/do_paper_details_result.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/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:percent_indicator/linear_percent_indicator.dart'; import 'package:percent_indicator/linear_percent_indicator.dart';
import 'package:vector_math/vector_math_64.dart' as vm64;
import '../configuration_files/index.dart'; import '../configuration_files/index.dart';
import '../configuration_files/zoom_logic.dart'; import '../configuration_files/zoom_logic.dart';
@ -37,7 +38,11 @@ class QuestionNumberView extends GetView<HomeworkReviewLogic> {
if (zoomFile == null || studentQuestions.isEmpty) return const SizedBox(); if (zoomFile == null || studentQuestions.isEmpty) return const SizedBox();
return $QuestionNumberScrollView(controller: controller, sateData: sateData, sateZoomData: sateZoomData, studentQuestions: studentQuestions); return $QuestionNumberScrollView(
controller: controller,
sateData: sateData,
sateZoomData: sateZoomData,
studentQuestions: studentQuestions);
}), }),
), ),
); );
@ -67,7 +72,6 @@ Widget $questionNumberScrollView({
scrollControllerNum.jumpTo(0); scrollControllerNum.jumpTo(0);
}); });
var stream = sateZoomData.initScale.listen((e) { var stream = sateZoomData.initScale.listen((e) {
// print("initScale : $e"); // print("initScale : $e");
useZoom.value = e ?? 1; useZoom.value = e ?? 1;
@ -101,6 +105,18 @@ Widget $questionNumberScrollView({
scrollControllerNum.addListener(() { scrollControllerNum.addListener(() {
/// ///
// if (sateData.panQuestView == false) sateData.slide.value = scrollControllerNum.offset; // if (sateData.panQuestView == false) sateData.slide.value = scrollControllerNum.offset;
final currentMatrix = controller.zoomController.value;
//
final newMatrix = Matrix4.copy(currentMatrix)
..setTranslation(vm64.Vector3(
currentMatrix.getTranslation().x, //
-scrollControllerNum.offset, //
currentMatrix.getTranslation().z, // Z
));
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) {
@ -116,9 +132,7 @@ Widget $questionNumberScrollView({
// var actualImgHeight = useImageInfo.value?.actualImgHeight ?? 0; // // var actualImgHeight = useImageInfo.value?.actualImgHeight ?? 0; //
// print("图片高度:$actualImgHeight"); print("图片高度:${usePiddingTop.value}");
print("FFFFFFFFF ${usePiddingTop.value}");
return SingleChildScrollView( return SingleChildScrollView(
controller: scrollControllerNum, controller: scrollControllerNum,

View File

@ -2,10 +2,10 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart' hide TransformationController;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:functional_widget_annotation/functional_widget_annotation.dart'; import 'package:functional_widget_annotation/functional_widget_annotation.dart';
import 'package:get/get.dart'; 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';
@ -30,8 +30,7 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
HomeworkReviewState get sateData => controller.state; HomeworkReviewState get sateData => controller.state;
ZoomState get zoomState => controller.zoomLogic.zoomState; ZoomState get zoomState => controller.zoomLogic.zoomState;
HomeworkReviewAnnotationsControlState get annotationState => HomeworkReviewAnnotationsControlState get annotationState => controller.annotationState;
controller.annotationState;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -47,10 +46,8 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
if (zoomFileModel == null) { if (zoomFileModel == null) {
/// ///
return LayoutBuilder(builder: return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
(BuildContext context, BoxConstraints constraints) { WidgetsBinding.instance.addPostFrameCallback((_) => zoomState.zoomFile.value = ZoomFileModel(
WidgetsBinding.instance.addPostFrameCallback(
(_) => zoomState.zoomFile.value = ZoomFileModel(
viewWidth: constraints.maxWidth, viewWidth: constraints.maxWidth,
viewHeight: constraints.maxHeight, viewHeight: constraints.maxHeight,
)); ));
@ -67,10 +64,14 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
children: [ children: [
// //
QuestionImageView( QuestionImageView(
maxWidth, maxHeight, annotationState, controller, maxWidth,
maxHeight,
annotationState,
controller,
zoomState: zoomState, zoomState: zoomState,
sateData: sateData, sateData: sateData,
actualHeight: zoomFileModel.actualHeight!), actualHeight: zoomFileModel.actualHeight!,
),
// //
// Positioned(right: 3.w, bottom: 4.h, child: const $ContinueToReview(isFloatingAction: true)), // Positioned(right: 3.w, bottom: 4.h, child: const $ContinueToReview(isFloatingAction: true)),
// //
@ -84,19 +85,15 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
heroTag: '点击前往上一题', heroTag: '点击前往上一题',
tooltip: '点击前往上一题', tooltip: '点击前往上一题',
focusColor: Theme.of(context).primaryColor, focusColor: Theme.of(context).primaryColor,
backgroundColor: backgroundColor: const Color.fromRGBO(24, 32, 32, 0.05),
const Color.fromRGBO(24, 32, 32, 0.05),
elevation: 10.r, elevation: 10.r,
onPressed: () => onPressed: () => easyThrottle('TestQuestionSwitch', () {
easyThrottle('TestQuestionSwitch', () {
var param = sateData.param.value; var param = sateData.param.value;
param.studentId = lastPageVal.studentId; param.studentId = lastPageVal.studentId;
param.templateId = lastPageVal.templateId; param.templateId = lastPageVal.templateId;
sateData.param.value = sateData.param.value = DoPaperDetailsParam.fromJson(param.toJson());
DoPaperDetailsParam.fromJson(param.toJson());
}), }),
child: Icon(Icons.arrow_back_ios, child: Icon(Icons.arrow_back_ios, color: Colors.white, size: 22.sp),
color: Colors.white, size: 22.sp),
); );
}), }),
), ),
@ -112,18 +109,14 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
heroTag: '点击前往下一题', heroTag: '点击前往下一题',
tooltip: '点击前往下一题', tooltip: '点击前往下一题',
elevation: 10.r, elevation: 10.r,
backgroundColor: backgroundColor: const Color.fromRGBO(24, 32, 32, 0.05),
const Color.fromRGBO(24, 32, 32, 0.05), onPressed: () => easyThrottle('TestQuestionSwitch', () {
onPressed: () =>
easyThrottle('TestQuestionSwitch', () {
var param = sateData.param.value; var param = sateData.param.value;
param.studentId = nextPageVal.studentId; param.studentId = nextPageVal.studentId;
param.templateId = nextPageVal.templateId; param.templateId = nextPageVal.templateId;
sateData.param.value = sateData.param.value = DoPaperDetailsParam.fromJson(param.toJson());
DoPaperDetailsParam.fromJson(param.toJson());
}), }),
child: Icon(Icons.arrow_forward_ios, child: Icon(Icons.arrow_forward_ios, color: Colors.white, size: 22.sp),
color: Colors.white, size: 22.sp),
); );
}), }),
), ),
@ -160,8 +153,7 @@ class DataErrorThenRequestAgainButton extends StatelessWidget {
child: CupertinoButton( child: CupertinoButton(
color: Colors.grey[300], color: Colors.grey[300],
onPressed: () => easyThrottle('home_work_reload_data', () { onPressed: () => easyThrottle('home_work_reload_data', () {
sateData.param.value = sateData.param.value = DoPaperDetailsParam.fromJson(sateData.param.value.toJson());
DoPaperDetailsParam.fromJson(sateData.param.value.toJson());
}), }),
child: quickText('再次请求', color: Colors.black38), child: quickText('再次请求', color: Colors.black38),
), ),
@ -172,8 +164,7 @@ class DataErrorThenRequestAgainButton extends StatelessWidget {
// //
@swidget @swidget
Widget $totalSubmitCountView( Widget $totalSubmitCountView(BuildContext context, HomeworkReviewState sateData) {
BuildContext context, HomeworkReviewState sateData) {
return Obx(() { return Obx(() {
var data = sateData.data.value; var data = sateData.data.value;
if (data == null) return Container(); if (data == null) return Container();
@ -189,9 +180,7 @@ Widget $totalSubmitCountView(
elevation: 10, elevation: 10,
backgroundColor: Colors.white, backgroundColor: Colors.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(topLeft: Radius.circular(10.r), topRight: Radius.circular(10.r))),
topLeft: Radius.circular(10.r),
topRight: Radius.circular(10.r))),
builder: (BuildContext context) { builder: (BuildContext context) {
return Padding( return Padding(
padding: EdgeInsets.symmetric(horizontal: 2.w), padding: EdgeInsets.symmetric(horizontal: 2.w),
@ -209,8 +198,7 @@ Widget $totalSubmitCountView(
SizedBox(height: 10.h), SizedBox(height: 10.h),
Expanded( Expanded(
child: ListView( child: ListView(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 4.w),
vertical: 8.h, horizontal: 4.w),
children: [ children: [
Wrap( Wrap(
spacing: 7.2.w, // () spacing: 7.2.w, // ()
@ -221,39 +209,29 @@ Widget $totalSubmitCountView(
alignment: const FractionalOffset(0.05, 0.09), alignment: const FractionalOffset(0.05, 0.09),
children: [ children: [
Container( Container(
padding: EdgeInsets.only( padding: EdgeInsets.only(top: 1.2.h, bottom: 1.5.h, left: 13.w, right: 5.w),
top: 1.2.h,
bottom: 1.5.h,
left: 13.w,
right: 5.w),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.r), borderRadius: BorderRadius.circular(4.r),
color: const Color.fromRGBO( color: const Color.fromRGBO(239, 242, 255, 1),
239, 242, 255, 1),
), ),
child: quickText( child: quickText(
e.name, e.name,
size: 12.sp, size: 12.sp,
wordSpacing: 2, wordSpacing: 2,
color: color: const Color.fromRGBO(80, 94, 110, 1),
const Color.fromRGBO(80, 94, 110, 1),
), ),
), ),
Stack( Stack(
alignment: alignment: const FractionalOffset(0.52, 0.24),
const FractionalOffset(0.52, 0.24),
children: [ children: [
Icon( Icon(
const IconData(0xe63d, const IconData(0xe63d, fontFamily: "AlibabaIcon"),
fontFamily: "AlibabaIcon"),
size: 12.sp, size: 12.sp,
color: e.isPriority color: e.isPriority
? Theme.of(context).primaryColor ? Theme.of(context).primaryColor
: const Color.fromRGBO( : const Color.fromRGBO(164, 164, 164, 1),
164, 164, 164, 1),
), ),
quickText('优先', quickText('优先', size: 4.sp, color: Colors.white),
size: 4.sp, color: Colors.white),
], ],
), ),
], ],
@ -275,15 +253,11 @@ Widget $totalSubmitCountView(
children: [ children: [
Padding( Padding(
padding: EdgeInsets.only(bottom: 1.h), padding: EdgeInsets.only(bottom: 1.h),
child: quickText('已阅', child: quickText('已阅', color: const Color.fromRGBO(117, 117, 117, 1), size: 10.sp),
color: const Color.fromRGBO(117, 117, 117, 1), size: 10.sp),
), ),
quickText(data.annotatedCount, quickText(data.annotatedCount,
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor, size: 12.sp, fontWeight: FontWeight.bold),
size: 12.sp, quickText('/', color: const Color.fromRGBO(117, 117, 117, 1), size: 12.sp),
fontWeight: FontWeight.bold),
quickText('/',
color: const Color.fromRGBO(117, 117, 117, 1), size: 12.sp),
quickText(data.submitCount - data.annotatedCount, quickText(data.submitCount - data.annotatedCount,
color: const Color.fromRGBO(117, 117, 117, 1), size: 10.sp), color: const Color.fromRGBO(117, 117, 117, 1), size: 10.sp),
], ],
@ -294,8 +268,7 @@ Widget $totalSubmitCountView(
} }
// //
class QuestionImageView extends HookWidget class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar> {
with EventBusMixin<BottomOperationBar> {
final double maxWidth; final double maxWidth;
final double maxHeight; final double maxHeight;
final double actualHeight; final double actualHeight;
@ -304,11 +277,15 @@ class QuestionImageView extends HookWidget
final HomeworkReviewState sateData; final HomeworkReviewState sateData;
final HomeworkReviewAnnotationsControlState annotationState; final HomeworkReviewAnnotationsControlState annotationState;
QuestionImageView( QuestionImageView(
this.maxWidth, this.maxHeight, this.annotationState, this.logic, this.maxWidth,
{required this.actualHeight, this.maxHeight,
this.annotationState,
this.logic, {
required this.actualHeight,
required this.zoomState, required this.zoomState,
required this.sateData, required this.sateData,
super.key}); super.key,
});
/// ///
int _findTargetIndex<T>(List<T> list, T target, [int reciprocal = 2]) { int _findTargetIndex<T>(List<T> list, T target, [int reciprocal = 2]) {
@ -362,12 +339,10 @@ class QuestionImageView extends HookWidget
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theMaxHeight = useState<double>(maxHeight); final theMaxHeight = useState<double>(maxHeight);
useValueChanged<double, void>( useValueChanged<double, void>(maxHeight, (oldValue, __) => theMaxHeight.value = maxHeight);
maxHeight, (oldValue, __) => theMaxHeight.value = maxHeight);
var zoomKey = useState<GlobalKey>(GlobalKey()); var zoomKey = useState<GlobalKey>(GlobalKey());
useValueChanged<int?, void>(zoomState.zoomFile.value?.templateId, useValueChanged<int?, void>(zoomState.zoomFile.value?.templateId, (old, __) {
(old, __) {
zoomKey.value = GlobalKey(); zoomKey.value = GlobalKey();
}); });
@ -388,11 +363,9 @@ class QuestionImageView extends HookWidget
var streamSubscriptionSlide = sateData.slide.listen((e) { var streamSubscriptionSlide = sateData.slide.listen((e) {
if (sateData.panQuestView != null && if (sateData.panQuestView != null &&
sateData.panQuestView == false && sateData.panQuestView == false &&
initPosition.value?.dy.abs().toInt().toDouble() != initPosition.value?.dy.abs().toInt().toDouble() != sateData.slide.value) {
sateData.slide.value) {
if (sateData.zoomOffset != null) { if (sateData.zoomOffset != null) {
sateData.zoomOffset = sateData.zoomOffset = Offset(sateData.zoomOffset!.dx, -sateData.slide.value);
Offset(sateData.zoomOffset!.dx, -sateData.slide.value);
} }
initPosition.value = sateData.zoomOffset; initPosition.value = sateData.zoomOffset;
} }
@ -413,15 +386,10 @@ class QuestionImageView extends HookWidget
bool? res = await showDialog<bool>( bool? res = await showDialog<bool>(
context: Get.context ?? context, context: Get.context ?? context,
builder: (context1) { builder: (context1) {
return AlertDialog( return AlertDialog(content: quickText("是否撤销全部批注痕迹?"), actions: <Widget>[
content: quickText("是否撤销全部批注痕迹?"), TextButton(child: quickText("取消"), onPressed: () => Navigator.pop(context1, false)),
actions: <Widget>[
TextButton( TextButton(
child: quickText("取消"), child: quickText("确定", color: Theme.of(context1).primaryColor),
onPressed: () => Navigator.pop(context1, false)),
TextButton(
child: quickText("确定",
color: Theme.of(context1).primaryColor),
onPressed: () => Navigator.pop(context1, true)) onPressed: () => Navigator.pop(context1, true))
]); ]);
}, },
@ -433,15 +401,10 @@ class QuestionImageView extends HookWidget
await showDialog<bool>( await showDialog<bool>(
context: Get.context ?? context, context: Get.context ?? context,
builder: (context1) { builder: (context1) {
return AlertDialog( return AlertDialog(content: quickText("是否撤销上次批阅批注痕迹?"), actions: <Widget>[
content: quickText("是否撤销上次批阅批注痕迹?"), TextButton(child: quickText("取消"), onPressed: () => Navigator.pop(context1, false)),
actions: <Widget>[
TextButton( TextButton(
child: quickText("取消"), child: quickText("确定", color: Theme.of(context1).primaryColor),
onPressed: () => Navigator.pop(context1, false)),
TextButton(
child: quickText("确定",
color: Theme.of(context1).primaryColor),
onPressed: () => easyThrottle( onPressed: () => easyThrottle(
'REVOKE_THE_LAST_ANNOTATION_AND_SUBMIT', 'REVOKE_THE_LAST_ANNOTATION_AND_SUBMIT',
() { () {
@ -450,16 +413,11 @@ class QuestionImageView extends HookWidget
sateData.data.value?.showZgtAnnotate = null; sateData.data.value?.showZgtAnnotate = null;
if (sateData.data.value != null) { if (sateData.data.value != null) {
sateData.data.update((_) { sateData.data.update((_) {
var theStudentQuestions = var theStudentQuestions = sateData.studentQuestions.value;
sateData.studentQuestions.value; if (theStudentQuestions?.isNotEmpty ?? false) {
if (theStudentQuestions?.isNotEmpty ?? var noMarking = theStudentQuestions?.firstWhereOrNull((e) => e.studentScore == null);
false) {
var noMarking =
theStudentQuestions?.firstWhereOrNull(
(e) => e.studentScore == null);
if (noMarking != null) { if (noMarking != null) {
ToastUtils.showInfo( ToastUtils.showInfo("未提交!请为第${noMarking.questionNo}题打分,再手动提交");
"未提交!请为第${noMarking.questionNo}题打分,再手动提交");
return; return;
} }
} }
@ -484,15 +442,10 @@ class QuestionImageView extends HookWidget
await showDialog<bool>( await showDialog<bool>(
context: context, context: context,
builder: (context1) { builder: (context1) {
return AlertDialog( return AlertDialog(content: quickText("是否撤销上次批阅批注痕迹?"), actions: <Widget>[
content: quickText("是否撤销上次批阅批注痕迹?"), TextButton(child: quickText("取消"), onPressed: () => Navigator.pop(context1, false)),
actions: <Widget>[
TextButton( TextButton(
child: quickText("取消"), child: quickText("确定", color: Theme.of(context).primaryColor),
onPressed: () => Navigator.pop(context1, false)),
TextButton(
child: quickText("确定",
color: Theme.of(context).primaryColor),
onPressed: () => easyThrottle( onPressed: () => easyThrottle(
'REVOKE_THE_LAST_ANNOTATION_AND_SUBMIT', 'REVOKE_THE_LAST_ANNOTATION_AND_SUBMIT',
() { () {
@ -500,8 +453,7 @@ class QuestionImageView extends HookWidget
sateData.data.value?.zgtAnnotate = null; sateData.data.value?.zgtAnnotate = null;
sateData.data.value?.showZgtAnnotate = null; sateData.data.value?.showZgtAnnotate = null;
if (sateData.data.value != null) { if (sateData.data.value != null) {
sateData.data.update( sateData.data.update((_) => logic.submit(Get.context ?? context));
(_) => logic.submit(Get.context ?? context));
} }
}, },
), ),
@ -566,17 +518,15 @@ class QuestionImageView extends HookWidget
Offset localPosition = event.localPosition; // Offset localPosition = event.localPosition; //
var zoomFile = zoomState.zoomFile.value!; var zoomFile = zoomState.zoomFile.value!;
// var imageHeightOffsetStart = zoomFile.imageHeightOffsetStart??0; // var imageHeightOffsetStart = zoomFile.imageHeightOffsetStart??0;
var imageHeightOffsetStart = zoomState.zoomFile.value! var imageHeightOffsetStart = zoomState.zoomFile.value!.getZoomFileOffsetStart(zoomState.initScale.value ?? 1);
.getZoomFileOffsetStart(zoomState.initScale.value ?? 1);
// print("位置:$localPosition; 图片所在位置:$imageHeightOffsetStart"); // print("位置:$localPosition; 图片所在位置:$imageHeightOffsetStart");
if (imageHeightOffsetStart == 0) return; if (imageHeightOffsetStart == 0) return;
var dy = localPosition.dy; var dy = localPosition.dy;
// print(zoomFile.getZoomFileHeightOffsetEnd(zoomState.initScale.value ?? 1)); // print(zoomFile.getZoomFileHeightOffsetEnd(zoomState.initScale.value ?? 1));
if (dy < imageHeightOffsetStart || if (dy < imageHeightOffsetStart || dy > zoomFile.getZoomFileHeightOffsetEnd(zoomState.initScale.value ?? 1)) {
dy > return; //
zoomFile.getZoomFileHeightOffsetEnd( }
zoomState.initScale.value ?? 1)) return; //
var theScale = zoomState.initScale.value ?? 1; var theScale = zoomState.initScale.value ?? 1;
// if (theScale != 1) { // if (theScale != 1) {
@ -597,21 +547,16 @@ class QuestionImageView extends HookWidget
// } // }
// (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 zoomWtdthSpaceVal = zoomFile var zoomWtdthSpaceVal = zoomFile.getZoomFileOffsetStartWidth(zoomState.initScale.value ?? 1);
.getZoomFileOffsetStartWidth(zoomState.initScale.value ?? 1);
localPosition = Offset( localPosition = Offset(
(localPosition.dx - (localPosition.dx -
zoomFile.getZoomFileOffsetStartWidth( zoomFile.getZoomFileOffsetStartWidth(zoomState.initScale.value ?? 1) +
zoomState.initScale.value ?? 1) + ((zoomWtdthSpaceVal <= 0.1) ? (sateData.zoomOffset?.dx.abs() ?? 0) : 0)) /
((zoomWtdthSpaceVal <= 0.1)
? (sateData.zoomOffset?.dx.abs() ?? 0)
: 0)) /
theScale, theScale,
(dy - (dy -
max(0, imageHeightOffsetStart) + max(0, imageHeightOffsetStart) +
((zoomFile.imageHeightOffsetStart == null || ((zoomFile.imageHeightOffsetStart == null || zoomFile.imageHeightOffsetStart! <= 0.1)
zoomFile.imageHeightOffsetStart! <= 0.1)
? (sateData.zoomOffset?.dy.abs() ?? 0) ? (sateData.zoomOffset?.dy.abs() ?? 0)
: 0)) / : 0)) /
theScale, theScale,
@ -619,18 +564,15 @@ class QuestionImageView extends HookWidget
/// ///
if (Platform.isAndroid) { if (Platform.isAndroid) {
var lastDrop = getLastDrop( var lastDrop = getLastDrop(vnHandWritings.value, vnHandWritings.value.length - 1);
vnHandWritings.value, vnHandWritings.value.length - 1);
if (lastDrop != null && if (lastDrop != null &&
((lastDrop.dx - localPosition.dx).abs() > 65 || ((lastDrop.dx - localPosition.dx).abs() > 65 || (lastDrop.dy - localPosition.dy).abs() > 65)) {
(lastDrop.dy - localPosition.dy).abs() > 65)) {
/// X点和上一个x点相差 10 /// X点和上一个x点相差 10
return; return;
} }
} }
// print("最终位置 $localPosition"); // print("最终位置 $localPosition");
vnHandWritings.value = List.from(vnHandWritings.value) vnHandWritings.value = List.from(vnHandWritings.value)..add(localPosition);
..add(localPosition);
sateData.handwritings = vnHandWritings.value; sateData.handwritings = vnHandWritings.value;
}, },
child: Obx(() { child: Obx(() {
@ -644,7 +586,21 @@ class QuestionImageView extends HookWidget
alignment: Alignment.center, alignment: Alignment.center,
child: IgnorePointer( child: IgnorePointer(
ignoring: isPen, ignoring: isPen,
child: Zoom( child:
// 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,
// ),
Zoom(
key: zoomKey.value, key: zoomKey.value,
// initTotalZoomOut: true, // // initTotalZoomOut: true, //
zoomSensibility: 0.05, zoomSensibility: 0.05,
@ -653,38 +609,36 @@ class QuestionImageView extends HookWidget
maxZoomWidth: maxWidth, maxZoomWidth: maxWidth,
maxZoomHeight: actualHeight, maxZoomHeight: actualHeight,
canvasColor: Colors.transparent, canvasColor: Colors.transparent,
doubleTapScaleChange: 1,
// initPosition: initPosition.value, // initPosition: initPosition.value,
// initScale: logic.zoomLogic.zoomState.initScale.value ?? 1, // initScale: logic.zoomLogic.zoomState.initScale.value ?? 1,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
onScaleUpdate: logic.zoomLogic.onScaleUpdate, onScaleUpdate: (double scale, double zoom) => logic.zoomLogic.onScaleUpdate(zoom),
onPositionUpdate: logic.zoomLogic.onPanUpPosition, onPositionUpdate: logic.zoomLogic.onPanUpPosition,
transformationController: logic.zoomController,
child: Stack( child: Stack(
children: [ children: [
$TheCachedNetworkImage( $TheCachedNetworkImage(
imgWidth: maxWidth, imgWidth: maxWidth,
imageUrl: sateData.data.value!.zgtAnswer, imageUrl: sateData.data.value!.zgtAnswer,
(_, imageProvider) => (_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
Image(image: imageProvider, fit: BoxFit.fitWidth),
), ),
RepaintBoundary( RepaintBoundary(
key: logic.pictureOverviewKey, key: logic.pictureOverviewKey,
child: CustomPaint( child: CustomPaint(
// isComplex: true, // isComplex: true,
size: Size( size: Size(maxWidth, zoomState.zoomFile.value!.actualHeight!),
maxWidth, zoomState.zoomFile.value!.actualHeight!),
foregroundPainter: DrawingPainter(ctrl: vnHandWritings), foregroundPainter: DrawingPainter(ctrl: vnHandWritings),
// child: $TheCachedNetworkImage( // child: $TheCachedNetworkImage(
// imgWidth: maxWidth, // imgWidth: maxWidth,
// imageUrl: showZgtAnnotate ?? sateData.data.value!.zgtAnswer, // imageUrl: showZgtAnnotate ?? sateData.data.value!.zgtAnswer,
// (_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth), // (_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
// ), // ),
child: showZgtAnnotate != null child: showZgtAnnotate != null
? $TheCachedNetworkImage( ? $TheCachedNetworkImage(
imgWidth: maxWidth, imgWidth: maxWidth,
imageUrl: showZgtAnnotate, imageUrl: showZgtAnnotate,
(_, imageProvider) => Image( (_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth))
image: imageProvider, fit: BoxFit.fitWidth))
: null, : null,
), ),
), ),
@ -714,10 +668,11 @@ class DrawingPainter extends CustomPainter {
for (int i = 0; i < pointsLength; i++) { for (int i = 0; i < pointsLength; i++) {
Offset? offsetData = points[i]; Offset? offsetData = points[i];
Offset? nextOffsetData = pointsLength - 1 == i ? null : points[i + 1]; Offset? nextOffsetData = pointsLength - 1 == i ? null : points[i + 1];
if (offsetData != null && nextOffsetData != null) if (offsetData != null && nextOffsetData != null) {
canvas.drawLine(offsetData, nextOffsetData, paintBrush); canvas.drawLine(offsetData, nextOffsetData, paintBrush);
} }
} }
}
@override @override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false; bool shouldRepaint(covariant CustomPainter oldDelegate) => false;

View File

@ -0,0 +1,108 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:making_school_asignment_app/common/utils/cached_network_img.dart';
/// [url]
/// [viewWidth]
/// [viewHeight]
/// [imageDisplayHeight]
/// [onTrans]
/// [onScale]
/// [onContentOffset]
/// [transformationController]
class ZoomView extends HookWidget {
final String url;
final double viewWidth;
final double viewHeight;
final double imageDisplayHeight;
final Function(Offset)? onTrans;
final Function(double)? onScale;
final Function(Offset)? onContentOffset;
final TransformationController? transformationController;
const ZoomView({
required this.url,
required this.viewWidth,
required this.viewHeight,
required this.imageDisplayHeight,
this.onTrans,
this.onScale,
this.onContentOffset,
this.transformationController,
super.key,
});
@override
Widget build(BuildContext context) {
final controller = transformationController ?? useTransformationController();
useEffect(() {
double? lastScale;
Offset? lastOffset;
Offset? lastContentOffset;
void notifyIfChanged() {
final matrix = controller.value;
final scale = matrix.getMaxScaleOnAxis();
final translation = matrix.getTranslation();
final offset = Offset(translation.x, translation.y);
// scale
if (onScale != null && (lastScale == null || (lastScale! - scale).abs() > 0.001)) {
onScale!(scale);
lastScale = scale;
}
// offset
if (onTrans != null && (lastOffset == null || (lastOffset! - offset).distance > 0.5)) {
onTrans!(offset);
lastOffset = offset;
}
//
if (onContentOffset != null) {
final scaledHeight = imageDisplayHeight * scale;
final dx = translation.x;
double dy = viewHeight > scaledHeight ? (viewHeight - scaledHeight) / 2.0 : 0;
dy += translation.y;
final contentOffset = Offset(dx, dy);
if (lastContentOffset == null || (lastContentOffset! - contentOffset).distance > 0.5) {
onContentOffset!(contentOffset);
lastContentOffset = contentOffset;
}
}
//
// final scaledHeight = imageDisplayHeight * scale;
// if (imageDisplayHeight < viewHeight && scaledHeight <= viewHeight) {
// final centerY = (viewHeight - scaledHeight) / 2.0;
// if ((translation.y - centerY).abs() > 0.5 || translation.x.abs() > 0.5) {
// final newMatrix = Matrix4.identity()
// ..translate(0.0, centerY)
// ..scale(scale);
// controller.value = newMatrix;
// }
// }
}
controller.addListener(notifyIfChanged);
return () {
controller.removeListener(notifyIfChanged);
};
}, [controller, imageDisplayHeight, viewHeight]);
return InteractiveViewer(
minScale: 1,
maxScale: 2.0,
panEnabled: true,
constrained: imageDisplayHeight < viewHeight,
transformationController: controller,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Center(
child: $TheCachedNetworkImage(
imageUrl: url,
imgWidth: viewWidth,
(_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
),
),
);
}
}

View File

@ -1,5 +1,4 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
@ -8,7 +7,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.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/job/annotate_list_to_refresh.dart'; import 'package:making_school_asignment_app/common/job/annotate_list_to_refresh.dart';
import 'package:making_school_asignment_app/common/job/marking_models/do_paper_details_param.dart'; import 'package:making_school_asignment_app/common/job/marking_models/do_paper_details_param.dart';
import 'package:making_school_asignment_app/common/job/marking_models/do_paper_details_result.dart'; import 'package:making_school_asignment_app/common/job/marking_models/do_paper_details_result.dart';
@ -22,6 +20,7 @@ import 'package:making_school_asignment_app/common/utils/toast_utils.dart';
import 'package:making_school_asignment_app/common/utils/upload_oss_img_utils.dart'; import 'package:making_school_asignment_app/common/utils/upload_oss_img_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:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:zoom_widget/zoom_widget.dart' as zoomWidget;
import 'zoom_logic.dart'; import 'zoom_logic.dart';
@ -35,7 +34,7 @@ class HomeworkReviewState {
late Rx<DoPaperDetailsResult?> data = Rx<DoPaperDetailsResult?>(null); late Rx<DoPaperDetailsResult?> data = Rx<DoPaperDetailsResult?>(null);
late Rx<List<StudentQuestions>?> studentQuestions = Rx<List<StudentQuestions>?>(null); late Rx<List<StudentQuestions>?> studentQuestions = Rx<List<StudentQuestions>?>(null);
late Rx<double> slide = 0.0.obs; // late Rx<double> slide = 0.0.obs; //
bool? panQuestView = null; bool? panQuestView;
// late Rx<TestQuestionsImageInfo?> imageScale; // late Rx<TestQuestionsImageInfo?> imageScale;
// late Rx<TestQuestionsImageInfo?> imageScaleZoom = Rx<TestQuestionsImageInfo?>(null); // late Rx<TestQuestionsImageInfo?> imageScaleZoom = Rx<TestQuestionsImageInfo?>(null);
@ -80,10 +79,12 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
late StreamSubscription<DoPaperDetailsResult?> _dataListen; late StreamSubscription<DoPaperDetailsResult?> _dataListen;
final HomeworkReviewState state = HomeworkReviewState(); final HomeworkReviewState state = HomeworkReviewState();
final HomeworkReviewAnnotationsControlState annotationState = HomeworkReviewAnnotationsControlState(); final HomeworkReviewAnnotationsControlState annotationState = HomeworkReviewAnnotationsControlState();
double appBarHeight = 56; double appBarHeight = kToolbarHeight;
StreamSubscription<TestQuestionsImageInfo?>? imageScaleZoomStream; StreamSubscription<TestQuestionsImageInfo?>? imageScaleZoomStream;
late final zoomWidget.TransformationController zoomController;
@override @override
void onInit() { void onInit() {
appBarHeight = MediaQuery.of(Get.context!).padding.top; appBarHeight = MediaQuery.of(Get.context!).padding.top;
@ -91,7 +92,7 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
// WidgetsFlutterBinding.ensureInitialized(); // WidgetsFlutterBinding.ensureInitialized();
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); // // SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); //
zoomController = zoomWidget.TransformationController();
state.param = DoPaperDetailsParam( state.param = DoPaperDetailsParam(
homeworkId: Get.arguments['homeworkId'], homeworkId: Get.arguments['homeworkId'],
homeworkName: Get.arguments['homeworkName'], homeworkName: Get.arguments['homeworkName'],
@ -107,8 +108,6 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
if (e == null) return; if (e == null) return;
var zoomState = zoomLogic.zoomState; var zoomState = zoomLogic.zoomState;
final currentTemplateId = zoomState.zoomFile.value?.templateId; // ID final currentTemplateId = zoomState.zoomFile.value?.templateId; // ID
if (currentTemplateId != null && currentTemplateId != e.templateId) { if (currentTemplateId != null && currentTemplateId != e.templateId) {
// zoom zoom文件 // zoom zoom文件
@ -133,7 +132,8 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
void onReady() { void onReady() {
Future.delayed(Duration.zero, () { Future.delayed(Duration.zero, () {
DeviceInfoPlugin().androidInfo.then((androidInfo) { DeviceInfoPlugin().androidInfo.then((androidInfo) {
Permission storagePermission = androidInfo.version.sdkInt >= 33 ? Permission.manageExternalStorage : Permission.storage; Permission storagePermission =
androidInfo.version.sdkInt >= 33 ? Permission.manageExternalStorage : Permission.storage;
PermissionDescribeUtil.instance.toLaunchPermissionRequest( PermissionDescribeUtil.instance.toLaunchPermissionRequest(
Get.context, Get.context,
title: '储存权限请求', title: '储存权限请求',
@ -148,7 +148,8 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
@override @override
void onClose() { void onClose() {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.top]); // zoomController.dispose();
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.top]); //
eventCancel(); eventCancel();
_dataListen.cancel(); _dataListen.cancel();
_paramListen.cancel(); _paramListen.cancel();
@ -233,7 +234,8 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
if (data == null) return null; if (data == null) return null;
// OSS url // OSS url
String imgKey = UploadOssImgUtils.getInstance().setImgKey(param.homeworkId, data.studentId.toString(), data.templateId.toString()); String imgKey = UploadOssImgUtils.getInstance()
.setImgKey(param.homeworkId, data.studentId.toString(), data.templateId.toString());
var resUrl = await getClient().getOssPresignedUri(imgKey); var resUrl = await getClient().getOssPresignedUri(imgKey);
if (resUrl == null) return null; if (resUrl == null) return null;
@ -280,7 +282,9 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
var data = state.data.value; var data = state.data.value;
if (data == null) return; if (data == null) return;
if ((state.data.value?.studentQuestions.isEmpty ?? true) || (state.studentQuestions.value?.isEmpty ?? true)) return; if ((state.data.value?.studentQuestions.isEmpty ?? true) || (state.studentQuestions.value?.isEmpty ?? true)) {
return;
}
var studentQuestions = state.studentQuestions.value!.where((e) => e.useTime != null && e.useTime! > 0).toList(); var studentQuestions = state.studentQuestions.value!.where((e) => e.useTime != null && e.useTime! > 0).toList();
// //
var noRatingElement = studentQuestions.firstWhereOrNull((e) => e.studentScore == null); var noRatingElement = studentQuestions.firstWhereOrNull((e) => e.studentScore == null);

View File

@ -1,12 +1,9 @@
import 'dart:ui' show ImmutableBuffer, ImageDescriptor;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:making_school_asignment_app/common/utils/anti_shake_throttling.dart' as anti_shake_throttling;
import 'package:flutter/widgets.dart';
import 'dart:async'; import 'dart:async';
import 'dart:ui' show ImmutableBuffer, ImageDescriptor;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'index.dart'; import 'index.dart';
@ -78,8 +75,8 @@ class ZoomLogic extends GetxController {
} }
// ==> // ==>
void onScaleUpdate(double scale, double zoom) async { void onScaleUpdate(double zoom) async {
print("$scale $zoom"); print("缩放比例:$zoom");
/// ///
zoomState.initScale.value = zoom; zoomState.initScale.value = zoom;
@ -90,15 +87,12 @@ class ZoomLogic extends GetxController {
void onPanUpPosition(Offset val) async { void onPanUpPosition(Offset val) async {
// //
var state = Get.find<HomeworkReviewLogic>().state; var state = Get.find<HomeworkReviewLogic>().state;
if (state.zoomOffset?.dy.toStringAsFixed(2) != val.dy.toStringAsFixed(2) || print('**************** 正在移动位置 YYY:${val.dy}');
state.zoomOffset?.dx.toStringAsFixed(2) != val.dx.toStringAsFixed(2)) { print('**************** 正在移动位置 XXX:${val.dx}');
// 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();
} }
} }
}
class HomeworkReviewZoomBinding extends Bindings { class HomeworkReviewZoomBinding extends Bindings {
@override @override

View File

@ -5,10 +5,10 @@ import 'package:making_school_asignment_app/common/job/annotate_list_to_refresh.
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/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/annotate_class/annotate_class_logic.dart'; import 'package:making_school_asignment_app/page/home_page/children/annotate_class/annotate_class_logic.dart';
import 'package:making_school_asignment_app/page/home_page/children/homework_review/components/bottom_operation_bar.dart';
import 'package:making_school_asignment_app/page/home_page/children/homework_review/components/question_paper_view.dart'; import 'package:making_school_asignment_app/page/home_page/children/homework_review/components/question_paper_view.dart';
import 'package:making_school_asignment_app/page/home_page/home_logic.dart'; import 'package:making_school_asignment_app/page/home_page/home_logic.dart';
import 'components/bottom_operation_bar.dart';
import 'components/dropdown_switch_students_type.dart'; import 'components/dropdown_switch_students_type.dart';
import 'components/favorite_widget.dart'; import 'components/favorite_widget.dart';
import 'configuration_files/index.dart'; import 'configuration_files/index.dart';
@ -18,35 +18,28 @@ class HomeworkReview extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final logic = Get.find<HomeworkReviewLogic>(); return GetBuilder<HomeworkReviewLogic>(builder: (logic) {
final sateData = logic.state; final sateData = logic.state;
final AnnotateClassLogic controller = Get.find<AnnotateClassLogic>();
final HomeLogic homeLogicController = Get.find<HomeLogic>();
return PopScope( return PopScope(
canPop: false, canPop: false,
onPopInvoked: (e) { onPopInvoked: (e) {
if (e && sateData.needRefresh) { if (e && sateData.needRefresh) {
controller.getList(); Get.find<AnnotateClassLogic>().getList();
homeLogicController.getList(); Get.find<HomeLogic>().getList();
logic.eventFire(model: AnnotateListToRefresh()); logic.eventFire(model: AnnotateListToRefresh());
} }
}, },
child: Scaffold( child: Scaffold(
appBar: AppBar( backgroundColor: Colors.transparent,
// titleSpacing: 0, body: Stack(
leading: IconButton(icon: const Icon(Icons.arrow_back_ios), onPressed: () => Get.back()), children: [
iconTheme: const IconThemeData(color: Colors.black), // body内容AppBar高度
title: quickText(sateData.param.value.homeworkName), const Padding(
backgroundColor: Colors.white, padding: EdgeInsets.only(top: kToolbarHeight),
elevation: 0, child: Column(
actions: [const FavoriteWidget(), SizedBox(width: 5.w), const ReturnToHomepage()],
),
body: const Column(
children: [ children: [
// //
DropdownSwitchStudentsType(), DropdownSwitchStudentsType(),
// SizedBox(height: 10),
// //
Expanded(child: QuestionPaperView()), Expanded(child: QuestionPaperView()),
// //
@ -54,6 +47,27 @@ class HomeworkReview extends StatelessWidget {
], ],
), ),
), ),
// AppBar
Container(
height: kToolbarHeight,
color: Colors.white,
child: Row(
children: [
SizedBox(width: 5.w),
IconButton(
icon: const Icon(Icons.arrow_back_ios, color: Colors.black), onPressed: () => Get.back()),
Expanded(child: quickText(sateData.param.value.homeworkName, size: 18.sp).paddingOnly(top: 2.h)),
const FavoriteWidget(),
SizedBox(width: 10.w),
const ReturnToHomepage(),
SizedBox(width: 8.w),
],
),
),
],
),
),
); );
});
} }
} }

View File

@ -81,7 +81,6 @@ class _MyInfoState extends State<MyInfo> with AutomaticKeepAliveClientMixin {
fontSize: 13.sp, fontSize: 13.sp,
); );
return OrientationBuilder(builder: (BuildContext context, Orientation orientation) {
return Stack( return Stack(
children: [ children: [
SizedBox( SizedBox(
@ -134,7 +133,8 @@ class _MyInfoState extends State<MyInfo> with AutomaticKeepAliveClientMixin {
margin: EdgeInsets.only(top: 0.h), margin: EdgeInsets.only(top: 0.h),
child: Text( child: Text(
userInfo.value?.name ?? '请前往登录', userInfo.value?.name ?? '请前往登录',
style: TextStyle(fontSize: 16.sp, color: const Color(0xFF332A2A), fontWeight: FontWeight.w500), style:
TextStyle(fontSize: 16.sp, color: const Color(0xFF332A2A), fontWeight: FontWeight.w500),
), ),
), ),
), ),
@ -174,7 +174,10 @@ class _MyInfoState extends State<MyInfo> with AutomaticKeepAliveClientMixin {
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [Text('账号', style: personalInfoTitleStly), Text(userInfo.value?.name ?? '请前往登录', style: personalInfoValStly)], children: [
Text('账号', style: personalInfoTitleStly),
Text(userInfo.value?.name ?? '请前往登录', style: personalInfoValStly)
],
), ),
), ),
Container( Container(
@ -195,7 +198,10 @@ class _MyInfoState extends State<MyInfo> with AutomaticKeepAliveClientMixin {
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [Text('所在学校', style: personalInfoTitleStly), Text(userInfo.value?.schoolName ?? '', style: personalInfoValStly)], children: [
Text('所在学校', style: personalInfoTitleStly),
Text(userInfo.value?.schoolName ?? '', style: personalInfoValStly)
],
), ),
), ),
Container( Container(
@ -237,6 +243,5 @@ class _MyInfoState extends State<MyInfo> with AutomaticKeepAliveClientMixin {
), ),
], ],
); );
});
} }
} }

View File

@ -1,7 +1,7 @@
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/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/read_over/widget/annotate_list.dart'; import 'package:making_school_asignment_app/page/home_page/children/read_over/widget/annotate_list.dart';
import 'package:making_school_asignment_app/routes/app_pages.dart'; import 'package:making_school_asignment_app/routes/app_pages.dart';
@ -37,7 +37,7 @@ class _ReadOverPageState extends State<ReadOverPage> {
statusBarBrightness: Brightness.light, statusBarBrightness: Brightness.light,
), ),
child: Scaffold( child: Scaffold(
backgroundColor: const Color.fromRGBO(244, 244, 244, 1), backgroundColor: Colors.white,
body: OrientationBuilder( body: OrientationBuilder(
builder: (BuildContext context, Orientation orientation) { builder: (BuildContext context, Orientation orientation) {
return Column( return Column(
@ -117,7 +117,9 @@ class _ReadOverPageState extends State<ReadOverPage> {
child: quickText( child: quickText(
'待批阅', '待批阅',
size: 14.sp, size: 14.sp,
color: state.tabIndex.value == 0 ? Theme.of(context).primaryColor : const Color.fromRGBO(80, 94, 110, 1), color: state.tabIndex.value == 0
? Theme.of(context).primaryColor
: const Color.fromRGBO(80, 94, 110, 1),
fontWeight: state.tabIndex.value == 0 ? FontWeight.bold : null, fontWeight: state.tabIndex.value == 0 ? FontWeight.bold : null,
), ),
); );
@ -137,7 +139,9 @@ class _ReadOverPageState extends State<ReadOverPage> {
child: quickText( child: quickText(
'已批阅', '已批阅',
size: 14.sp, size: 14.sp,
color: state.tabIndex.value == 1 ? Theme.of(context).primaryColor : const Color.fromRGBO(80, 94, 110, 1), color: state.tabIndex.value == 1
? Theme.of(context).primaryColor
: const Color.fromRGBO(80, 94, 110, 1),
fontWeight: state.tabIndex.value == 1 ? FontWeight.bold : null, fontWeight: state.tabIndex.value == 1 ? FontWeight.bold : null,
), ),
); );
@ -153,13 +157,16 @@ class _ReadOverPageState extends State<ReadOverPage> {
onTap: () { onTap: () {
Get.toNamed(Routes.studentHistoryWorkPage, arguments: {'page': 'set'}); Get.toNamed(Routes.studentHistoryWorkPage, arguments: {'page': 'set'});
}, },
child: Icon(const IconData(0xe63e, fontFamily: "AlibabaIcon"), color: const Color.fromRGBO(44, 48, 63, 1), size: 24.sp), child: Icon(const IconData(0xe63e, fontFamily: "AlibabaIcon"),
color: const Color.fromRGBO(44, 48, 63, 1), size: 24.sp),
), ),
), ),
], ],
), ),
), ),
Expanded( Expanded(
child: Container(
color: const Color.fromRGBO(245, 245, 245, 1),
child: Obx(() { child: Obx(() {
return AnnotateList( return AnnotateList(
tabIndex: state.tabIndex.value, tabIndex: state.tabIndex.value,
@ -167,6 +174,7 @@ class _ReadOverPageState extends State<ReadOverPage> {
); );
}), }),
), ),
),
], ],
); );
}, },

View File

@ -1,15 +1,12 @@
import 'package:badges/badges.dart' as badges;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart'; import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:functional_widget_annotation/functional_widget_annotation.dart'; import 'package:functional_widget_annotation/functional_widget_annotation.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:badges/badges.dart' as badges; import 'package:making_school_asignment_app/common/mixins/getx_keepalive_widget.dart';
import 'package:percent_indicator/percent_indicator.dart';
import 'package:making_school_asignment_app/common/job/work_student.dart';
import 'package:making_school_asignment_app/common/utils/enum_untils.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/MyEmptyWidget.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';
@ -17,48 +14,36 @@ import 'home_logic.dart';
part 'home_view.g.dart'; part 'home_view.g.dart';
class HomePage extends StatefulWidget { class HomePage extends GetxKeepAliveWidget<HomeLogic> {
const HomePage({super.key}); const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin {
final logic = Get.find<HomeLogic>();
final state = Get.find<HomeLogic>().state;
@override
bool get wantKeepAlive => true;
@override @override
void initState() { void initState() {
super.initState();
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent, // statusBarColor: Colors.transparent, //
statusBarIconBrightness: Brightness.light, statusBarIconBrightness: Brightness.light,
systemStatusBarContrastEnforced: false, systemStatusBarContrastEnforced: false,
)); ));
super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget buildContent(context, controller) {
super.build(context);
var spaceWidth = SizedBox(height: ScreenUtil().screenWidth / 30);
return Scaffold( return Scaffold(
backgroundColor: Colors.white, backgroundColor: Colors.white,
body: OrientationBuilder( body: OrientationBuilder(
builder: (BuildContext context, Orientation orientation) { builder: (BuildContext context, Orientation orientation) {
final state = controller.state;
return EasyRefresh( return EasyRefresh(
firstRefresh: false, firstRefresh: false,
taskIndependence: true, taskIndependence: true,
enableControlFinishLoad: true, enableControlFinishLoad: true,
controller: logic.refreshController, controller: controller.refreshController,
header: MaterialHeader(), header: MaterialHeader(),
// footer: TaurusFooter(), // footer: TaurusFooter(),
onRefresh: () async { onRefresh: () async {
state.pageNumber = 1; state.pageNumber = 1;
return logic.getList(); return controller.getList();
}, },
// onLoad: () async { // onLoad: () async {
// if (state.workList.length < state.totalCount.value) { // if (state.workList.length < state.totalCount.value) {
@ -77,24 +62,21 @@ class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin
margin: EdgeInsets.only(top: 300.h), margin: EdgeInsets.only(top: 300.h),
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20.r), padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20.r),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.only(topLeft: Radius.circular(30.r), topRight: Radius.circular(30.r))), color: Colors.white,
borderRadius: BorderRadius.only(topLeft: Radius.circular(30.r), topRight: Radius.circular(30.r))),
child: Column( child: Column(
children: [ children: [
SizedBox( SizedBox(height: 15.r),
height: 15.r,
),
Text( Text(
'我的作业管理', '我的作业管理',
style: TextStyle(fontSize: 20.sp, color: const Color(0xFF676666), fontWeight: FontWeight.w600), style: TextStyle(fontSize: 20.sp, color: const Color(0xFF676666), fontWeight: FontWeight.w600),
), ),
SizedBox( SizedBox(height: 15.r),
height: 15.r,
),
Row( Row(
children: [ children: [
Expanded( Expanded(
child: Obx(() { child: Obx(() {
return menuItem( return $MenuItem(
bgImg: 'assets/images/home_bg_01.png', bgImg: 'assets/images/home_bg_01.png',
name: '作业批阅', name: '作业批阅',
value: state.totalCount.value.toString(), value: state.totalCount.value.toString(),
@ -102,45 +84,43 @@ class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin
); );
}), }),
), ),
SizedBox( SizedBox(width: 20.r),
width: 20.r, const Expanded(
), child: $MenuItem(
Expanded( bgImg: 'assets/images/home_bg_02.png',
child: menuItem(bgImg: 'assets/images/home_bg_02.png', name: '知识点掌握', url: Routes.studentHistoryWorkPage, page: 'points'), name: '知识点掌握',
url: Routes.studentHistoryWorkPage,
page: 'points'),
), ),
], ],
), ),
SizedBox( SizedBox(height: 20.r),
height: 20.r,
),
Row( Row(
children: [ children: [
Expanded( const Expanded(
child: child: $MenuItem(
menuItem(bgImg: 'assets/images/home_bg_03.png', name: '学生历史作业', url: Routes.studentHistoryWorkPage, page: 'history'), bgImg: 'assets/images/home_bg_03.png',
name: '学生历史作业',
url: Routes.studentHistoryWorkPage,
page: 'history'),
), ),
SizedBox( SizedBox(width: 20.r),
width: 20.r, const Expanded(
), child: $MenuItem(
Expanded( bgImg: 'assets/images/home_bg_04.png', name: '答题轨迹', url: Routes.answerTrajectoryPage),
child: menuItem(bgImg: 'assets/images/home_bg_04.png', name: '答题轨迹', url: Routes.answerTrajectoryPage),
), ),
], ],
), ),
SizedBox( SizedBox(height: 20.r),
height: 20.r,
),
Row( Row(
children: [ children: [
menuItem( const $MenuItem(
bgImg: 'assets/images/home_bg_05.png', bgImg: 'assets/images/home_bg_05.png',
name: '优先批阅设定', name: '优先批阅设定',
url: Routes.studentHistoryWorkPage, url: Routes.studentHistoryWorkPage,
page: 'set', page: 'set',
), ),
SizedBox( SizedBox(width: 20.r),
width: 20.r,
),
], ],
), ),
SizedBox(height: 15.h), SizedBox(height: 15.h),
@ -154,15 +134,10 @@ class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin
), ),
); );
} }
@override
void dispose() {
Get.delete<HomeLogic>();
super.dispose();
}
} }
Widget menuItem({required String bgImg, required String name, String? value, required String url, String? page}) { @swidget
Widget $menuItem({required String bgImg, required String name, String? value, required String url, String? page}) {
return InkWell( return InkWell(
onTap: () { onTap: () {
Get.toNamed(url, arguments: {'page': page ?? ''}); Get.toNamed(url, arguments: {'page': page ?? ''});
@ -198,7 +173,7 @@ Widget menuItem({required String bgImg, required String name, String? value, req
borderRadius: BorderRadius.all(Radius.circular(9.r)), borderRadius: BorderRadius.all(Radius.circular(9.r)),
), ),
child: Text( child: Text(
value!, value,
style: TextStyle(fontSize: 10.sp, color: Colors.white, fontWeight: FontWeight.w600), style: TextStyle(fontSize: 10.sp, color: Colors.white, fontWeight: FontWeight.w600),
), ),
) )
@ -243,7 +218,7 @@ Widget $termRow(BuildContext context, List<EntranceModel> items, int? data) {
Expanded(flex: 9, child: $TermItem(items[0], data!)), Expanded(flex: 9, child: $TermItem(items[0], data!)),
// const Expanded(flex: 1, child: SizedBox()), // const Expanded(flex: 1, child: SizedBox()),
SizedBox(width: ScreenUtil().screenWidth / 30), SizedBox(width: ScreenUtil().screenWidth / 30),
Expanded(flex: 9, child: $TermItem(items[1], data!)), Expanded(flex: 9, child: $TermItem(items[1], data)),
]); ]);
break; break;
case 3: case 3:
@ -304,7 +279,8 @@ Widget $termItem(BuildContext context, EntranceModel e, int data, {double? theHe
badgeStyle: badges.BadgeStyle( badgeStyle: badges.BadgeStyle(
badgeColor: const Color.fromRGBO(255, 105, 105, 1), badgeColor: const Color.fromRGBO(255, 105, 105, 1),
shape: badges.BadgeShape.square, shape: badges.BadgeShape.square,
borderRadius: BorderRadius.only(topLeft: Radius.circular(10.r), topRight: Radius.circular(8.5.r), bottomRight: Radius.circular(8.5.r)), borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.r), topRight: Radius.circular(8.5.r), bottomRight: Radius.circular(8.5.r)),
// borderSide: BorderSide(color: Colors.white, width: 2), // borderSide: BorderSide(color: Colors.white, width: 2),
elevation: 1, elevation: 1,
padding: EdgeInsets.symmetric(horizontal: Utils.isPad() ? 11.w : 16.w, vertical: 2.h), padding: EdgeInsets.symmetric(horizontal: Utils.isPad() ? 11.w : 16.w, vertical: 2.h),
@ -332,7 +308,8 @@ Widget $termItem(BuildContext context, EntranceModel e, int data, {double? theHe
children: [ children: [
Image.asset(e.image, height: 32.r, width: 32.r, fit: BoxFit.cover), Image.asset(e.image, height: 32.r, width: 32.r, fit: BoxFit.cover),
SizedBox(height: 6.r), SizedBox(height: 6.r),
quickText(e.title, size: 12.sp, color: const Color.fromRGBO(79, 79, 79, 1), fontWeight: FontWeight.w500), quickText(e.title,
size: 12.sp, color: const Color.fromRGBO(79, 79, 79, 1), fontWeight: FontWeight.w500),
], ],
) )
: Row( : Row(
@ -340,7 +317,8 @@ Widget $termItem(BuildContext context, EntranceModel e, int data, {double? theHe
children: [ children: [
Image.asset(e.image, height: 32.r, width: 32.r, fit: BoxFit.cover), Image.asset(e.image, height: 32.r, width: 32.r, fit: BoxFit.cover),
SizedBox(width: 6.r), SizedBox(width: 6.r),
quickText(e.title, size: 12.sp, color: const Color.fromRGBO(79, 79, 79, 1), fontWeight: FontWeight.w500), quickText(e.title,
size: 12.sp, color: const Color.fromRGBO(79, 79, 79, 1), fontWeight: FontWeight.w500),
], ],
), ),
), ),

View File

@ -1,47 +1,22 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.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:get/get.dart';
import 'package:making_school_asignment_app/common/utils/utils.dart'; import 'package:making_school_asignment_app/common/mixins/getx_keepalive_widget.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/read_over/read_over_logic.dart';
import 'package:making_school_asignment_app/page/home_page/children/read_over/read_over_view.dart';
import 'package:making_school_asignment_app/page/home_page/children/read_over/widget/annotate_list.dart'; import 'package:making_school_asignment_app/page/home_page/children/read_over/widget/annotate_list.dart';
import 'package:making_school_asignment_app/page/home_page/children/student_work_detail/widget/job_condition_filter.dart';
import 'package:making_school_asignment_app/routes/app_pages.dart'; import 'package:making_school_asignment_app/routes/app_pages.dart';
import 'package:syncfusion_flutter_datepicker/datepicker.dart';
import 'work_logic.dart'; import 'work_logic.dart';
class WorkPage extends StatefulWidget { class WorkPage extends GetxKeepAliveWidget<WorkLogic> {
const WorkPage({super.key}); const WorkPage({super.key});
@override @override
State<WorkPage> createState() => _WorkPageState(); Widget buildContent(BuildContext context, WorkLogic controller) {
} final state = controller.state;
class _WorkPageState extends State<WorkPage> with AutomaticKeepAliveClientMixin {
final logic = Get.find<WorkLogic>();
final state = Get.find<WorkLogic>().state;
@override
bool get wantKeepAlive => true;
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
Widget build(BuildContext context) {
super.build(context);
return Scaffold( return Scaffold(
backgroundColor: const Color.fromRGBO(244, 244, 244, 1), backgroundColor: const Color.fromRGBO(244, 244, 244, 1),
body: OrientationBuilder( body: Column(
builder: (BuildContext context, Orientation orientation) {
return Column(
children: <Widget>[ children: <Widget>[
Container( Container(
color: Colors.white, color: Colors.white,
@ -70,7 +45,7 @@ class _WorkPageState extends State<WorkPage> with AutomaticKeepAliveClientMixin
indicatorPadding: EdgeInsets.zero, indicatorPadding: EdgeInsets.zero,
indicatorWeight: 0, indicatorWeight: 0,
labelPadding: EdgeInsets.symmetric(horizontal: 2.w), labelPadding: EdgeInsets.symmetric(horizontal: 2.w),
controller: logic.tabController, controller: controller.tabController,
unselectedLabelStyle: TextStyle( unselectedLabelStyle: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: const Color.fromRGBO(69, 83, 100, 1), color: const Color.fromRGBO(69, 83, 100, 1),
@ -104,7 +79,9 @@ class _WorkPageState extends State<WorkPage> with AutomaticKeepAliveClientMixin
child: quickText( child: quickText(
'待批阅', '待批阅',
size: 14.sp, size: 14.sp,
color: state.tabIndex.value == 0 ? Theme.of(context).primaryColor : const Color.fromRGBO(80, 94, 110, 1), color: state.tabIndex.value == 0
? Theme.of(context).primaryColor
: const Color.fromRGBO(80, 94, 110, 1),
fontWeight: state.tabIndex.value == 0 ? FontWeight.bold : null, fontWeight: state.tabIndex.value == 0 ? FontWeight.bold : null,
), ),
); );
@ -124,7 +101,9 @@ class _WorkPageState extends State<WorkPage> with AutomaticKeepAliveClientMixin
child: quickText( child: quickText(
'已批阅', '已批阅',
size: 14.sp, size: 14.sp,
color: state.tabIndex.value == 1 ? Theme.of(context).primaryColor : const Color.fromRGBO(80, 94, 110, 1), color: state.tabIndex.value == 1
? Theme.of(context).primaryColor
: const Color.fromRGBO(80, 94, 110, 1),
fontWeight: state.tabIndex.value == 1 ? FontWeight.bold : null, fontWeight: state.tabIndex.value == 1 ? FontWeight.bold : null,
), ),
); );
@ -140,7 +119,8 @@ class _WorkPageState extends State<WorkPage> with AutomaticKeepAliveClientMixin
onTap: () { onTap: () {
Get.toNamed(Routes.studentHistoryWorkPage, arguments: {'page': 'set'}); Get.toNamed(Routes.studentHistoryWorkPage, arguments: {'page': 'set'});
}, },
child: Icon(const IconData(0xe63e, fontFamily: "AlibabaIcon"), color: const Color.fromRGBO(44, 48, 63, 1), size: 24.sp), child: Icon(const IconData(0xe63e, fontFamily: "AlibabaIcon"),
color: const Color.fromRGBO(44, 48, 63, 1), size: 24.sp),
), ),
), ),
], ],
@ -155,15 +135,7 @@ class _WorkPageState extends State<WorkPage> with AutomaticKeepAliveClientMixin
}), }),
), ),
], ],
);
},
), ),
); );
} }
@override
void dispose() {
Get.delete<WorkLogic>();
super.dispose();
}
} }

View File

@ -12,8 +12,9 @@ import connectivity_plus
import device_info_plus import device_info_plus
import package_info_plus import package_info_plus
import path_provider_foundation import path_provider_foundation
import sqflite import sqflite_darwin
import url_launcher_macos import url_launcher_macos
import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AppInstallerPlugin.register(with: registry.registrar(forPlugin: "AppInstallerPlugin")) AppInstallerPlugin.register(with: registry.registrar(forPlugin: "AppInstallerPlugin"))
@ -25,4 +26,5 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin"))
} }

View File

@ -62,14 +62,17 @@ dependencies:
fluttertoast: ^8.0.9 fluttertoast: ^8.0.9
# #
functional_widget_annotation: ^0.10.0 functional_widget_annotation: ^0.10.0
flutter_widget_from_html_core: ^0.14.12 flutter_widget_from_html_core: ^0.17.0
# APP内存及本地存储插件 # APP内存及本地存储插件
get_storage: ^2.0.3 get_storage: ^2.0.3
zoom_widget: ^2.0.1 # zoom_widget: ^2.0.1
zoom_widget:
git:
url: https://github.com/semakers/zoom-widget.git
ref: a35c9da6afe405c23b5897b449683d424016e9f1
# start retrofit请求封装 # start retrofit请求封装
retrofit: ^4.1.0 retrofit: ^4.1.0
json_annotation: ^4.9.0 json_annotation: 4.9.0
# end retrofit请求封装 # end retrofit请求封装
# 进度条 # 进度条
percent_indicator: ^4.2.3 percent_indicator: ^4.2.3
@ -79,7 +82,7 @@ dependencies:
data_table_2: 2.5.10 data_table_2: 2.5.10
flutter_staggered_grid_view: ^0.7.0 flutter_staggered_grid_view: ^0.7.0
dropdown_button2: ^2.3.9 dropdown_button2: ^2.3.9
syncfusion_flutter_datepicker: ^25.2.5 syncfusion_flutter_datepicker: ^30.2.7
easy_debounce: ^2.0.3 # 防抖节流 easy_debounce: ^2.0.3 # 防抖节流
flutter_hooks: ^0.20.5 flutter_hooks: ^0.20.5
flutter_spinkit: ^5.2.1 flutter_spinkit: ^5.2.1
@ -93,24 +96,26 @@ dependencies:
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 permission_handler: ^11.3.1
flutter_distributor: ^0.4.5 flutter_distributor: 0.6.5
flutter_native_splash: ^2.4.1 # fastforge: ^0.6.5
flutter_native_splash: ^2.4.6
icons_launcher: ^2.1.7 icons_launcher: ^2.1.7
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
# dependency_overrides: dependency_overrides:
archive: ^4.0.2
# meta: ^1.15.0 # meta: ^1.15.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
retrofit_generator: ^8.1.0 retrofit_generator: 9.6.0
build_runner: ^2.4.10 build_runner: 2.5.4
json_serializable: ^6.6.2 json_serializable: 6.9.5
# 分离样式 # 分离样式
functional_widget: ^0.10.2 functional_widget: ^0.10.3
# The "flutter_lints" package below contains a set of recommended lints to # The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is # encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your # activated in the `analysis_options.yaml` file located at the root of your

View File

@ -7,8 +7,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:making_school_asignment_app/main_offest.dart';
import 'package:making_school_asignment_app/main.dart';
void main() { void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async { testWidgets('Counter increments smoke test', (WidgetTester tester) async {

View File

@ -211,6 +211,10 @@
@ -224,7 +228,8 @@
body { body {
margin: 0; margin: 0;
min-height: 100%; min-height: 100%;
background-color: #8C68FF; background-color: #ffffff;
background-image: url("splash/img/light-background.png");
background-size: 100% 100%; background-size: 100% 100%;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB