Compare commits
1 Commits
master
...
glassy_sta
| Author | SHA1 | Date |
|---|---|---|
|
|
8309a2d2e2 |
|
|
@ -0,0 +1,101 @@
|
||||||
|
# macOS Flutter APK 构建脚本使用说明
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
`flutter_build_apk_onmac.sh` 是基于Windows版本的 `flutter_build_apk_onwindows.bat` 脚本改写的macOS版本,用于在macOS系统上构建Flutter APK。
|
||||||
|
|
||||||
|
## 主要功能
|
||||||
|
1. **进程清理**: 终止相关的Java和Gradle进程
|
||||||
|
2. **项目清理**: 执行 `flutter clean`
|
||||||
|
3. **文件清理**: 删除 `build`、`.dart_tool`、`pubspec.lock` 等文件夹
|
||||||
|
4. **依赖管理**: 执行 `flutter pub get`
|
||||||
|
5. **代码生成**: 运行 `build_runner`
|
||||||
|
6. **APK构建**: 构建release版本的APK
|
||||||
|
7. **结果展示**: 自动打开APK所在文件夹并显示文件信息
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
### 1. 设置执行权限
|
||||||
|
在终端中执行以下命令为脚本添加执行权限:
|
||||||
|
```bash
|
||||||
|
chmod +x flutter_build_apk_onmac.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 运行脚本
|
||||||
|
```bash
|
||||||
|
./flutter_build_apk_onmac.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
或者直接用bash运行:
|
||||||
|
```bash
|
||||||
|
bash flutter_build_apk_onmac.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 系统要求
|
||||||
|
- macOS 系统
|
||||||
|
- 已安装 Flutter SDK
|
||||||
|
- 已安装 Android SDK
|
||||||
|
- 项目已正确配置Android构建环境
|
||||||
|
|
||||||
|
## 脚本特性
|
||||||
|
|
||||||
|
### 错误处理
|
||||||
|
- 使用 `set -e` 确保任何命令失败时脚本立即退出
|
||||||
|
- 详细的错误日志输出
|
||||||
|
- 进程清理的容错处理
|
||||||
|
|
||||||
|
### 日志系统
|
||||||
|
脚本包含彩色日志输出:
|
||||||
|
- 🔵 **INFO**: 一般信息
|
||||||
|
- 🟢 **SUCCESS**: 成功操作
|
||||||
|
- 🟡 **WARNING**: 警告信息
|
||||||
|
- 🔴 **ERROR**: 错误信息
|
||||||
|
|
||||||
|
### 强制删除功能
|
||||||
|
- 实现了类似Windows版本的强制删除机制
|
||||||
|
- 支持重试机制(最多3次)
|
||||||
|
- 指数退避策略
|
||||||
|
|
||||||
|
### 自动化功能
|
||||||
|
- 自动打开APK所在文件夹(使用macOS的`open`命令)
|
||||||
|
- 显示生成的APK文件列表
|
||||||
|
- 构建完成后的详细信息展示
|
||||||
|
|
||||||
|
## 与Windows版本的主要差异
|
||||||
|
|
||||||
|
| 功能 | Windows版本 | macOS版本 |
|
||||||
|
|------|------------|-----------|
|
||||||
|
| 脚本语言 | Batch (.bat) | Bash (.sh) |
|
||||||
|
| 进程终止 | `taskkill` | `pkill` |
|
||||||
|
| 文件删除 | `del`, `rmdir` | `rm -rf` |
|
||||||
|
| 打开文件夹 | `explorer` | `open` |
|
||||||
|
| 编码设置 | `chcp 65001` | UTF-8默认支持 |
|
||||||
|
| 错误处理 | `ERRORLEVEL` | 退出码 `$?` |
|
||||||
|
|
||||||
|
## 故障排除
|
||||||
|
|
||||||
|
### 权限问题
|
||||||
|
如果遇到权限错误,确保脚本有执行权限:
|
||||||
|
```bash
|
||||||
|
ls -la flutter_build_apk_onmac.sh
|
||||||
|
# 应该显示类似: -rwxr-xr-x 的权限
|
||||||
|
```
|
||||||
|
|
||||||
|
### Flutter环境问题
|
||||||
|
确保Flutter环境正确配置:
|
||||||
|
```bash
|
||||||
|
flutter doctor
|
||||||
|
```
|
||||||
|
|
||||||
|
### Android SDK问题
|
||||||
|
确保Android SDK路径正确配置:
|
||||||
|
```bash
|
||||||
|
echo $ANDROID_HOME
|
||||||
|
```
|
||||||
|
|
||||||
|
## 输出文件位置
|
||||||
|
构建成功后,APK文件将位于:
|
||||||
|
```
|
||||||
|
build/app/outputs/flutter-apk/app-release.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
脚本会自动打开此文件夹并显示详细信息。
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# macOS版本的Flutter APK构建脚本
|
||||||
|
# 基于Windows版本的flutter_build_apk_onwindows.bat改写
|
||||||
|
|
||||||
|
# 设置错误处理
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 颜色定义
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# 日志函数
|
||||||
|
log_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 获取项目根目录
|
||||||
|
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
|
||||||
|
log_info "项目目录: $PROJECT_DIR"
|
||||||
|
|
||||||
|
# 1. 终止相关进程
|
||||||
|
log_info "终止相关进程..."
|
||||||
|
pkill -f "java.*gradle" 2>/dev/null || true
|
||||||
|
pkill -f "gradle" 2>/dev/null || true
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# 强制删除函数
|
||||||
|
force_delete() {
|
||||||
|
local target="$1"
|
||||||
|
local max_retries=3
|
||||||
|
local retry_delay=1
|
||||||
|
|
||||||
|
if [ ! -e "$target" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
for ((i=1; i<=max_retries; i++)); do
|
||||||
|
log_info "尝试删除 $target (第 $i 次,共 $max_retries 次)"
|
||||||
|
|
||||||
|
# 尝试删除
|
||||||
|
rm -rf "$target" 2>/dev/null || true
|
||||||
|
|
||||||
|
# 检查是否删除成功
|
||||||
|
if [ ! -e "$target" ]; then
|
||||||
|
log_success "成功删除 $target"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 如果未成功,等待后重试
|
||||||
|
if [ $i -lt $max_retries ]; then
|
||||||
|
sleep $retry_delay
|
||||||
|
retry_delay=$((retry_delay * 2)) # 指数退避策略
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log_warning "警告: 无法完全删除 $target,某些文件可能正在使用中"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Flutter清理
|
||||||
|
log_info "运行 flutter clean..."
|
||||||
|
if ! flutter clean; then
|
||||||
|
log_error "flutter clean 失败,错误码: $?"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. 强制删除构建目录
|
||||||
|
log_info "删除残留文件"
|
||||||
|
force_delete "build"
|
||||||
|
force_delete ".dart_tool"
|
||||||
|
force_delete "pubspec.lock"
|
||||||
|
|
||||||
|
# 4. 获取依赖包
|
||||||
|
log_info "运行 flutter pub get..."
|
||||||
|
if ! flutter pub get; then
|
||||||
|
log_error "flutter pub get 失败,错误码: $?"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 5. 运行build_runner
|
||||||
|
log_info "运行 build_runner..."
|
||||||
|
if ! flutter packages pub run build_runner build --delete-conflicting-outputs; then
|
||||||
|
log_error "build_runner 失败,错误码: $?"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 6. 构建release APK
|
||||||
|
log_info "构建 release APK..."
|
||||||
|
if ! flutter build apk --release; then
|
||||||
|
log_error "APK 构建失败,错误码: $?"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 7. 打开APK所在文件夹
|
||||||
|
log_info "打开 APK 目录"
|
||||||
|
APK_DIR="$PROJECT_DIR/build/app/outputs/flutter-apk"
|
||||||
|
if [ -d "$APK_DIR" ]; then
|
||||||
|
# macOS使用open命令打开Finder
|
||||||
|
open "$APK_DIR"
|
||||||
|
else
|
||||||
|
log_warning "APK 目录不存在: $APK_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
log_success "所有步骤成功完成!"
|
||||||
|
log_success "APK 文件生成位置: $APK_DIR"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# 显示生成的APK文件信息
|
||||||
|
if [ -d "$APK_DIR" ]; then
|
||||||
|
echo "生成的APK文件:"
|
||||||
|
ls -la "$APK_DIR"/*.apk 2>/dev/null || log_warning "未找到APK文件"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
exit 0
|
||||||
|
|
@ -14,6 +14,8 @@
|
||||||
* @LastEditors: Please set LastEditors
|
* @LastEditors: Please set LastEditors
|
||||||
* @LastEditTime: 2021-01-12 15:08:43
|
* @LastEditTime: 2021-01-12 15:08:43
|
||||||
*/
|
*/
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
|
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:making_school_asignment_app/common/utils/app_upgrade/DownloadApk.dart';
|
import 'package:making_school_asignment_app/common/utils/app_upgrade/DownloadApk.dart';
|
||||||
|
|
@ -22,8 +24,6 @@ import 'package:making_school_asignment_app/common/utils/app_upgrade/upgradeLogi
|
||||||
import 'package:making_school_asignment_app/common/utils/utils.dart';
|
import 'package:making_school_asignment_app/common/utils/utils.dart';
|
||||||
import 'package:making_school_asignment_app/page/global_widget/my_text.dart';
|
import 'package:making_school_asignment_app/page/global_widget/my_text.dart';
|
||||||
import 'package:percent_indicator/linear_percent_indicator.dart';
|
import 'package:percent_indicator/linear_percent_indicator.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
|
|
@ -61,7 +61,7 @@ class UpdateDialog extends Dialog {
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
margin: EdgeInsets.only(top:isPad()? 136.h : 128.h, bottom: 10.h),
|
margin: EdgeInsets.only(top: isPad() ? 136.h : 128.h, bottom: 10.h),
|
||||||
child: quickText(
|
child: quickText(
|
||||||
updateAppEvent.title,
|
updateAppEvent.title,
|
||||||
size: 18.sp,
|
size: 18.sp,
|
||||||
|
|
@ -184,7 +184,8 @@ class DownloadButton extends StatelessWidget {
|
||||||
gradient: LinearGradient(colors: [primaryColor, primaryColor.withOpacity(0.7)]),
|
gradient: LinearGradient(colors: [primaryColor, primaryColor.withOpacity(0.7)]),
|
||||||
),
|
),
|
||||||
child: MaterialButton(
|
child: MaterialButton(
|
||||||
onPressed: () => easyThrottle('DownloadButton_App_Upgrade', duration: const Duration(milliseconds: 1000), () async {
|
onPressed: () =>
|
||||||
|
easyThrottle('DownloadButton_App_Upgrade', duration: const Duration(milliseconds: 1000), () async {
|
||||||
if (deviceInfo == "android" && updateAppEvent.equipment == Equipment.android) {
|
if (deviceInfo == "android" && updateAppEvent.equipment == Equipment.android) {
|
||||||
// 权限检查 判断是否有读写内存的权限
|
// 权限检查 判断是否有读写内存的权限
|
||||||
bool flag = await UpgradePermission(updateAppEvent.deviceInfo).checkPermission(context, updateAppEvent);
|
bool flag = await UpgradePermission(updateAppEvent.deviceInfo).checkPermission(context, updateAppEvent);
|
||||||
|
|
@ -215,7 +216,8 @@ class DownloadButton extends StatelessWidget {
|
||||||
// await autoUpdater.setScheduledCheckInterval(0);
|
// await autoUpdater.setScheduledCheckInterval(0);
|
||||||
// }
|
// }
|
||||||
}),
|
}),
|
||||||
child: quickText(!logic.loadingApk.value ? '立即体验' : '正在下载...', size: 16.sp, color: Colors.white, fontWeight: FontWeight.w500),
|
child: quickText(!logic.loadingApk.value ? '立即体验' : '正在下载...',
|
||||||
|
size: 16.sp, color: Colors.white, fontWeight: FontWeight.w500),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
|
@ -62,156 +63,379 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch>
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
left: false,
|
left: false,
|
||||||
right: false,
|
right: false,
|
||||||
top: false,
|
top: false,
|
||||||
child: Container(
|
child: ClipRRect(
|
||||||
width: double.infinity,
|
borderRadius: BorderRadius.only(
|
||||||
height: _animationController.value * 70.h,
|
topLeft: Radius.circular(25.r),
|
||||||
decoration: BoxDecoration(
|
topRight: Radius.circular(25.r),
|
||||||
color: const Color.fromRGBO(83, 83, 83, 1),
|
),
|
||||||
boxShadow: [
|
child: BackdropFilter(
|
||||||
BoxShadow(
|
filter: ImageFilter.blur(sigmaX: 25, sigmaY: 25),
|
||||||
color: Colors.black.withOpacity(0.1),
|
child: Container(
|
||||||
blurRadius: 8,
|
width: double.infinity,
|
||||||
offset: const Offset(0, -2),
|
height: _animationController.value * 70.h,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
// 增强的液态玻璃背景
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
colors: [
|
||||||
|
Colors.white.withOpacity(0.12),
|
||||||
|
Colors.white.withOpacity(0.08),
|
||||||
|
Colors.white.withOpacity(0.06),
|
||||||
|
Colors.black.withOpacity(0.03),
|
||||||
|
],
|
||||||
|
stops: const [0.0, 0.3, 0.7, 1.0],
|
||||||
),
|
),
|
||||||
],
|
// 圆润的液态边缘
|
||||||
),
|
borderRadius: BorderRadius.only(
|
||||||
child: Row(
|
topLeft: Radius.circular(25.r),
|
||||||
children: [
|
topRight: Radius.circular(25.r),
|
||||||
Expanded(
|
),
|
||||||
flex: 7,
|
// 多层阴影增强液态质感
|
||||||
child: Column(
|
boxShadow: [
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
// 主阴影 - 深度感
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.25),
|
||||||
|
blurRadius: 30,
|
||||||
|
offset: const Offset(0, -8),
|
||||||
|
spreadRadius: -10,
|
||||||
|
),
|
||||||
|
// 内部高光 - 玻璃反射
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.white.withOpacity(0.15),
|
||||||
|
blurRadius: 3,
|
||||||
|
offset: const Offset(0, 2),
|
||||||
|
),
|
||||||
|
// 彩色光晕 - 液态折射
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.blue.withOpacity(0.05),
|
||||||
|
blurRadius: 20,
|
||||||
|
offset: const Offset(0, -3),
|
||||||
|
spreadRadius: -5,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
// 增强的液态边缘装饰层
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
height: 5.h,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(25.r),
|
||||||
|
topRight: Radius.circular(25.r),
|
||||||
|
),
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: [
|
||||||
|
Colors.white.withOpacity(0.25),
|
||||||
|
Colors.white.withOpacity(0.12),
|
||||||
|
Colors.white.withOpacity(0.05),
|
||||||
|
Colors.transparent,
|
||||||
|
],
|
||||||
|
stops: const [0.0, 0.3, 0.6, 1.0],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 液态波纹效果
|
||||||
|
Positioned.fill(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(25.r),
|
||||||
|
topRight: Radius.circular(25.r),
|
||||||
|
),
|
||||||
|
gradient: RadialGradient(
|
||||||
|
center: const Alignment(0.0, -0.8),
|
||||||
|
radius: 1.5,
|
||||||
|
colors: [
|
||||||
|
Colors.white.withOpacity(0.08),
|
||||||
|
Colors.white.withOpacity(0.04),
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.white.withOpacity(0.02),
|
||||||
|
Colors.transparent,
|
||||||
|
],
|
||||||
|
stops: const [0.0, 0.2, 0.4, 0.7, 1.0],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 液态流动光效
|
||||||
|
Positioned.fill(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(25.r),
|
||||||
|
topRight: Radius.circular(25.r),
|
||||||
|
),
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: const Alignment(-1.0, -1.0),
|
||||||
|
end: const Alignment(1.0, 1.0),
|
||||||
|
colors: [
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.white.withOpacity(0.06),
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.white.withOpacity(0.03),
|
||||||
|
Colors.transparent,
|
||||||
|
],
|
||||||
|
stops: const [0.0, 0.25, 0.5, 0.75, 1.0],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 顶部液滴效果
|
||||||
|
Positioned(
|
||||||
|
top: -2,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
_buildLiquidDroplet(width: 60.w, height: 4.h),
|
||||||
|
_buildLiquidDroplet(width: 40.w, height: 3.h),
|
||||||
|
_buildLiquidDroplet(width: 50.w, height: 3.5.h),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 主要内容
|
||||||
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Row(
|
flex: 7,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
// 全对按钮
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildActionButton(
|
child: Row(
|
||||||
'全对',
|
children: [
|
||||||
Icons.check_circle_outline,
|
// 全对按钮
|
||||||
() => easyThrottle('homework_bottom_operation_bar_scoring_related',
|
Expanded(
|
||||||
() => _homeworkLogic.allPairs(context)),
|
child: _buildActionButton(
|
||||||
isEnabled: !_homeworkLogic.state.submitLoading.value,
|
'全对',
|
||||||
isPrimary: true,
|
Icons.check_circle_outline,
|
||||||
|
() => easyThrottle('homework_bottom_operation_bar_scoring_related',
|
||||||
|
() => _homeworkLogic.allPairs(context)),
|
||||||
|
isEnabled: !_homeworkLogic.state.submitLoading.value,
|
||||||
|
isPrimary: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildGlassDivider(),
|
||||||
|
// 注解笔
|
||||||
|
Expanded(
|
||||||
|
child: Obx(() => _buildIconButton(
|
||||||
|
const IconData(0xe635, fontFamily: "AlibabaIcon"),
|
||||||
|
() => easyThrottle('homework_bottom_action_bar_annotations', () {
|
||||||
|
// 互斥:启用注解笔,禁用滑动
|
||||||
|
_logicControl.gestureMove.value = false;
|
||||||
|
_logicControl.pen.value = true;
|
||||||
|
}),
|
||||||
|
isActive: _logicControl.pen.value,
|
||||||
|
tooltip: '注解笔',
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
_buildGlassDivider(),
|
||||||
|
// 滑动试题
|
||||||
|
Expanded(
|
||||||
|
child: Obx(() => _buildIconButton(
|
||||||
|
const IconData(0xe636, fontFamily: "AlibabaIcon"),
|
||||||
|
() => easyThrottle('homework_bottom_action_bar_annotations', () {
|
||||||
|
// 互斥:启用滑动,禁用注解笔
|
||||||
|
_logicControl.pen.value = false;
|
||||||
|
_logicControl.gestureMove.value = true;
|
||||||
|
}),
|
||||||
|
isActive: _logicControl.gestureMove.value,
|
||||||
|
tooltip: '滑动试题',
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
|
_buildHorizontalGlassDivider(),
|
||||||
// 注解笔
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Obx(() => _buildIconButton(
|
child: Row(
|
||||||
const IconData(0xe635, fontFamily: "AlibabaIcon"),
|
children: [
|
||||||
() => easyThrottle('homework_bottom_action_bar_annotations', () {
|
// 全错按钮
|
||||||
// 互斥:启用注解笔,禁用滑动
|
Expanded(
|
||||||
_logicControl.gestureMove.value = false;
|
child: _buildActionButton(
|
||||||
_logicControl.pen.value = true;
|
'全错',
|
||||||
}),
|
Icons.cancel_outlined,
|
||||||
isActive: _logicControl.pen.value,
|
() => easyThrottle('homework_bottom_operation_bar_scoring_related',
|
||||||
tooltip: '注解笔',
|
() => _homeworkLogic.allWrongRating(context)),
|
||||||
)),
|
isEnabled: !_homeworkLogic.state.submitLoading.value,
|
||||||
),
|
isPrimary: false,
|
||||||
Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
|
),
|
||||||
// 滑动试题
|
),
|
||||||
Expanded(
|
_buildGlassDivider(),
|
||||||
child: Obx(() => _buildIconButton(
|
// 撤销上一步
|
||||||
const IconData(0xe636, fontFamily: "AlibabaIcon"),
|
Expanded(
|
||||||
() => easyThrottle('homework_bottom_action_bar_annotations', () {
|
child: _buildIconButton(
|
||||||
// 互斥:启用滑动,禁用注解笔
|
const IconData(0xe638, fontFamily: "AlibabaIcon"),
|
||||||
_logicControl.pen.value = false;
|
() => easyThrottle(
|
||||||
_logicControl.gestureMove.value = true;
|
'homework_bottom_action_bar_annotations',
|
||||||
}),
|
() => eventFire(model: BottomOperationBar(revokeAll: false, revokePreStep: true)),
|
||||||
isActive: _logicControl.gestureMove.value,
|
),
|
||||||
tooltip: '滑动试题',
|
tooltip: '撤销上一步',
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
|
_buildGlassDivider(),
|
||||||
|
// 全部撤销
|
||||||
|
Expanded(
|
||||||
|
child: _buildIconButton(
|
||||||
|
const IconData(0xe637, fontFamily: "AlibabaIcon"),
|
||||||
|
() => easyThrottle(
|
||||||
|
'homework_bottom_action_bar_annotations',
|
||||||
|
() => eventFire(model: BottomOperationBar(revokeAll: true, revokePreStep: false)),
|
||||||
|
),
|
||||||
|
tooltip: '全部撤销',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(width: double.infinity, color: Colors.white.withOpacity(0.3), height: 0.5.h),
|
_buildGlassDivider(),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
// 全错按钮
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildActionButton(
|
child: _buildActionButton(
|
||||||
'全错',
|
'取消',
|
||||||
Icons.cancel_outlined,
|
Icons.clear,
|
||||||
() => easyThrottle('homework_bottom_operation_bar_scoring_related',
|
() => easyThrottle('homework_bottom_operation_bar_scoring_related',
|
||||||
() => _homeworkLogic.allWrongRating(context)),
|
() => _homeworkLogic.cancelAllRatings()),
|
||||||
isEnabled: !_homeworkLogic.state.submitLoading.value,
|
isEnabled: true,
|
||||||
isPrimary: false,
|
isPrimary: false,
|
||||||
|
isSecondary: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
|
_buildGlassDivider(),
|
||||||
// 撤销上一步
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildIconButton(
|
child: Obx(() {
|
||||||
const IconData(0xe638, fontFamily: "AlibabaIcon"),
|
final submitLoading = _homeworkLogic.state.submitLoading.value;
|
||||||
() => easyThrottle(
|
return _buildActionButton(
|
||||||
'homework_bottom_action_bar_annotations',
|
'提交',
|
||||||
() => eventFire(model: BottomOperationBar(revokeAll: false, revokePreStep: true)),
|
Icons.send,
|
||||||
),
|
() => easyThrottle('homework_bottom_operation_bar_scoring_related',
|
||||||
tooltip: '撤销上一步',
|
() => _homeworkLogic.submit(context)),
|
||||||
),
|
isEnabled: !submitLoading,
|
||||||
),
|
isPrimary: true,
|
||||||
Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
|
isLoading: submitLoading,
|
||||||
// 全部撤销
|
);
|
||||||
Expanded(
|
}),
|
||||||
child: _buildIconButton(
|
|
||||||
const IconData(0xe637, fontFamily: "AlibabaIcon"),
|
|
||||||
() => easyThrottle(
|
|
||||||
'homework_bottom_action_bar_annotations',
|
|
||||||
() => eventFire(model: BottomOperationBar(revokeAll: true, revokePreStep: false)),
|
|
||||||
),
|
|
||||||
tooltip: '全部撤销',
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
|
),
|
||||||
Expanded(
|
|
||||||
flex: 3,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: _buildActionButton(
|
|
||||||
'取消',
|
|
||||||
Icons.clear,
|
|
||||||
() => easyThrottle(
|
|
||||||
'homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.cancelAllRatings()),
|
|
||||||
isEnabled: true,
|
|
||||||
isPrimary: false,
|
|
||||||
isSecondary: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(width: 0.5.w, height: double.infinity, color: Colors.white.withOpacity(0.3)),
|
|
||||||
Expanded(
|
|
||||||
child: Obx(() {
|
|
||||||
final submitLoading = _homeworkLogic.state.submitLoading.value;
|
|
||||||
return _buildActionButton(
|
|
||||||
'提交',
|
|
||||||
Icons.send,
|
|
||||||
() => easyThrottle(
|
|
||||||
'homework_bottom_operation_bar_scoring_related', () => _homeworkLogic.submit(context)),
|
|
||||||
isEnabled: !submitLoading,
|
|
||||||
isPrimary: true,
|
|
||||||
isLoading: submitLoading,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
));
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 液滴效果
|
||||||
|
Widget _buildLiquidDroplet({required double width, required double height}) {
|
||||||
|
return Container(
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
bottomLeft: Radius.circular(width / 2),
|
||||||
|
bottomRight: Radius.circular(width / 2),
|
||||||
|
),
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: [
|
||||||
|
Colors.white.withOpacity(0.3),
|
||||||
|
Colors.white.withOpacity(0.15),
|
||||||
|
Colors.white.withOpacity(0.05),
|
||||||
|
Colors.transparent,
|
||||||
|
],
|
||||||
|
stops: const [0.0, 0.3, 0.7, 1.0],
|
||||||
|
),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.white.withOpacity(0.2),
|
||||||
|
blurRadius: 4,
|
||||||
|
offset: const Offset(0, 1),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 增强的垂直玻璃分隔线
|
||||||
|
Widget _buildGlassDivider() {
|
||||||
|
return Container(
|
||||||
|
width: 1.w,
|
||||||
|
height: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: [
|
||||||
|
Colors.white.withOpacity(0.05),
|
||||||
|
Colors.white.withOpacity(0.15),
|
||||||
|
Colors.white.withOpacity(0.25),
|
||||||
|
Colors.white.withOpacity(0.15),
|
||||||
|
Colors.white.withOpacity(0.05),
|
||||||
|
],
|
||||||
|
stops: const [0.0, 0.2, 0.5, 0.8, 1.0],
|
||||||
|
),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.white.withOpacity(0.1),
|
||||||
|
blurRadius: 2,
|
||||||
|
spreadRadius: 0.5,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 增强的水平玻璃分隔线
|
||||||
|
Widget _buildHorizontalGlassDivider() {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 1.h,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.centerLeft,
|
||||||
|
end: Alignment.centerRight,
|
||||||
|
colors: [
|
||||||
|
Colors.white.withOpacity(0.03),
|
||||||
|
Colors.white.withOpacity(0.08),
|
||||||
|
Colors.white.withOpacity(0.2),
|
||||||
|
Colors.white.withOpacity(0.08),
|
||||||
|
Colors.white.withOpacity(0.03),
|
||||||
|
],
|
||||||
|
stops: const [0.0, 0.2, 0.5, 0.8, 1.0],
|
||||||
|
),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.white.withOpacity(0.15),
|
||||||
|
blurRadius: 3,
|
||||||
|
spreadRadius: 0.5,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildActionButton(
|
Widget _buildActionButton(
|
||||||
|
|
@ -230,6 +454,9 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch>
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: isEnabled ? onPressed : null,
|
onTap: isEnabled ? onPressed : null,
|
||||||
|
borderRadius: BorderRadius.circular(8.r),
|
||||||
|
splashColor: isPrimary ? Colors.white.withOpacity(0.2) : primaryColor.withOpacity(0.2),
|
||||||
|
highlightColor: isPrimary ? Colors.white.withOpacity(0.1) : primaryColor.withOpacity(0.1),
|
||||||
child: Semantics(
|
child: Semantics(
|
||||||
label: text,
|
label: text,
|
||||||
hint: isLoading ? '正在处理中' : (isEnabled ? '点击执行$text操作' : '$text按钮已禁用'),
|
hint: isLoading ? '正在处理中' : (isEnabled ? '点击执行$text操作' : '$text按钮已禁用'),
|
||||||
|
|
@ -239,12 +466,35 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch>
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: double.infinity,
|
height: double.infinity,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isPrimary
|
// 液态玻璃背景
|
||||||
? (isEnabled ? primaryColor : primaryColor.withOpacity(0.5))
|
gradient: isPrimary
|
||||||
: isSecondary
|
? LinearGradient(
|
||||||
? Colors.transparent
|
begin: Alignment.topLeft,
|
||||||
: Colors.transparent,
|
end: Alignment.bottomRight,
|
||||||
borderRadius: BorderRadius.circular(4.r),
|
colors: isEnabled
|
||||||
|
? [
|
||||||
|
primaryColor.withOpacity(0.8),
|
||||||
|
primaryColor.withOpacity(0.6),
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
primaryColor.withOpacity(0.3),
|
||||||
|
primaryColor.withOpacity(0.2),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: LinearGradient(
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
colors: [
|
||||||
|
Colors.white.withOpacity(0.08),
|
||||||
|
Colors.white.withOpacity(0.03),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(8.r),
|
||||||
|
// 液态玻璃边框
|
||||||
|
border: Border.all(
|
||||||
|
color: isPrimary ? Colors.white.withOpacity(0.2) : Colors.white.withOpacity(0.1),
|
||||||
|
width: 0.5,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
|
@ -300,14 +550,39 @@ class _BottomAnnotationSwitchJobState extends State<BottomAnnotationSwitch>
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: onPressed,
|
onTap: onPressed,
|
||||||
|
borderRadius: BorderRadius.circular(8.r),
|
||||||
|
splashColor: primaryColor.withOpacity(0.2),
|
||||||
|
highlightColor: primaryColor.withOpacity(0.1),
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
message: tooltip ?? '',
|
message: tooltip ?? '',
|
||||||
child: Container(
|
child: Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: double.infinity,
|
height: double.infinity,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isActive ? primaryColor.withOpacity(0.2) : Colors.transparent,
|
// 液态玻璃背景
|
||||||
borderRadius: BorderRadius.circular(4.r),
|
gradient: isActive
|
||||||
|
? LinearGradient(
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
colors: [
|
||||||
|
primaryColor.withOpacity(0.15),
|
||||||
|
primaryColor.withOpacity(0.08),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: LinearGradient(
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
colors: [
|
||||||
|
Colors.white.withOpacity(0.05),
|
||||||
|
Colors.white.withOpacity(0.02),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(8.r),
|
||||||
|
// 液态玻璃边框
|
||||||
|
border: Border.all(
|
||||||
|
color: isActive ? primaryColor.withOpacity(0.3) : Colors.white.withOpacity(0.08),
|
||||||
|
width: 0.5,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart' hide TransformationController;
|
import 'package:flutter/cupertino.dart' hide TransformationController;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
@ -80,12 +81,9 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
|
||||||
child: Obx(() {
|
child: Obx(() {
|
||||||
LastPage? lastPageVal = sateData.data.value?.lastPage;
|
LastPage? lastPageVal = sateData.data.value?.lastPage;
|
||||||
if (lastPageVal == null) return const SizedBox();
|
if (lastPageVal == null) return const SizedBox();
|
||||||
return FloatingActionButton(
|
return GlassmorphicButton(
|
||||||
heroTag: '点击前往上一题',
|
heroTag: '点击前往上一题',
|
||||||
tooltip: '点击前往上一题',
|
tooltip: '点击前往上一题',
|
||||||
focusColor: Theme.of(context).primaryColor,
|
|
||||||
backgroundColor: const Color.fromRGBO(24, 32, 32, 0.05),
|
|
||||||
elevation: 10.r,
|
|
||||||
onPressed: () => easyThrottle('TestQuestionSwitch', () {
|
onPressed: () => easyThrottle('TestQuestionSwitch', () {
|
||||||
var param = sateData.param.value;
|
var param = sateData.param.value;
|
||||||
param.studentId = lastPageVal.studentId;
|
param.studentId = lastPageVal.studentId;
|
||||||
|
|
@ -104,11 +102,9 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
|
||||||
NextPage? nextPageVal = sateData.data.value?.nextPage;
|
NextPage? nextPageVal = sateData.data.value?.nextPage;
|
||||||
if (nextPageVal == null) return const SizedBox();
|
if (nextPageVal == null) return const SizedBox();
|
||||||
|
|
||||||
return FloatingActionButton(
|
return GlassmorphicButton(
|
||||||
heroTag: '点击前往下一题',
|
heroTag: '点击前往下一题',
|
||||||
tooltip: '点击前往下一题',
|
tooltip: '点击前往下一题',
|
||||||
elevation: 10.r,
|
|
||||||
backgroundColor: 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;
|
||||||
|
|
@ -683,3 +679,292 @@ class DrawingPainter extends CustomPainter {
|
||||||
@override
|
@override
|
||||||
bool shouldRebuildSemantics(CustomPainter oldDelegate) => false;
|
bool shouldRebuildSemantics(CustomPainter oldDelegate) => false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// iOS风格的液态玻璃态按钮
|
||||||
|
class GlassmorphicButton extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
final VoidCallback? onPressed;
|
||||||
|
final String? heroTag;
|
||||||
|
final String? tooltip;
|
||||||
|
final double size;
|
||||||
|
final Color? backgroundColor;
|
||||||
|
final Color? borderColor;
|
||||||
|
|
||||||
|
const GlassmorphicButton({
|
||||||
|
super.key,
|
||||||
|
required this.child,
|
||||||
|
this.onPressed,
|
||||||
|
this.heroTag,
|
||||||
|
this.tooltip,
|
||||||
|
this.size = 56.0,
|
||||||
|
this.backgroundColor,
|
||||||
|
this.borderColor,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(size / 2),
|
||||||
|
child: BackdropFilter(
|
||||||
|
filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(size / 2),
|
||||||
|
// 几乎不可见的背景
|
||||||
|
gradient: RadialGradient(
|
||||||
|
center: Alignment.topLeft,
|
||||||
|
radius: 1.2,
|
||||||
|
colors: [
|
||||||
|
Colors.white.withOpacity(0.002),
|
||||||
|
Colors.white.withOpacity(0.004),
|
||||||
|
Colors.white.withOpacity(0.001),
|
||||||
|
],
|
||||||
|
stops: const [0.0, 0.4, 1.0],
|
||||||
|
),
|
||||||
|
// 液态玻璃边缘 - 多层渐变边框效果
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.transparent,
|
||||||
|
width: 0,
|
||||||
|
),
|
||||||
|
// 几乎不可见的阴影
|
||||||
|
boxShadow: [
|
||||||
|
// 微弱阴影
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.005),
|
||||||
|
blurRadius: 4,
|
||||||
|
offset: const Offset(0, 1),
|
||||||
|
spreadRadius: -1,
|
||||||
|
),
|
||||||
|
// 极微弱高光
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.white.withOpacity(0.008),
|
||||||
|
blurRadius: 0.5,
|
||||||
|
offset: const Offset(0, -0.3),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(size / 2),
|
||||||
|
// 几乎不可见的流动
|
||||||
|
gradient: SweepGradient(
|
||||||
|
center: Alignment.center,
|
||||||
|
startAngle: 0,
|
||||||
|
endAngle: 6.28,
|
||||||
|
colors: [
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.white.withOpacity(0.0008),
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.white.withOpacity(0.0015),
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.white.withOpacity(0.0008),
|
||||||
|
Colors.transparent,
|
||||||
|
],
|
||||||
|
stops: const [0.0, 0.15, 0.3, 0.5, 0.65, 0.85, 1.0],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(size / 2),
|
||||||
|
onTap: onPressed,
|
||||||
|
splashColor: Colors.white.withOpacity(0.008),
|
||||||
|
highlightColor: Colors.white.withOpacity(0.003),
|
||||||
|
child: Container(
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(size / 2),
|
||||||
|
// 几乎不存在的表面
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: [
|
||||||
|
Colors.white.withOpacity(0.001),
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.white.withOpacity(0.0005),
|
||||||
|
],
|
||||||
|
stops: const [0.0, 0.2, 0.8, 1.0],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
// 液态边缘效果 - 表面张力
|
||||||
|
Positioned.fill(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(size / 2),
|
||||||
|
// 边缘渐变 - 模拟液体表面张力
|
||||||
|
gradient: RadialGradient(
|
||||||
|
center: Alignment.center,
|
||||||
|
radius: 0.5,
|
||||||
|
colors: [
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.white.withOpacity(0.008),
|
||||||
|
Colors.white.withOpacity(0.015),
|
||||||
|
Colors.white.withOpacity(0.025),
|
||||||
|
],
|
||||||
|
stops: const [0.0, 0.7, 0.85, 0.93, 1.0],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 液态边缘流动效果
|
||||||
|
Positioned.fill(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(size / 2),
|
||||||
|
// 环形边缘高光
|
||||||
|
gradient: SweepGradient(
|
||||||
|
center: Alignment.center,
|
||||||
|
colors: [
|
||||||
|
Colors.white.withOpacity(0.012),
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.white.withOpacity(0.008),
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.white.withOpacity(0.015),
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.white.withOpacity(0.01),
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.white.withOpacity(0.012),
|
||||||
|
],
|
||||||
|
stops: const [0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 液态玻璃流动光效
|
||||||
|
Positioned.fill(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(size / 2),
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: const Alignment(-0.8, -0.8),
|
||||||
|
end: const Alignment(0.8, 0.8),
|
||||||
|
colors: [
|
||||||
|
Colors.white.withOpacity(0.0008),
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.white.withOpacity(0.0003),
|
||||||
|
],
|
||||||
|
stops: const [0.0, 0.3, 0.7, 1.0],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 液体高光点 - 模拟液体表面反射
|
||||||
|
Positioned(
|
||||||
|
top: size * 0.2,
|
||||||
|
left: size * 0.3,
|
||||||
|
child: Container(
|
||||||
|
width: size * 0.08,
|
||||||
|
height: size * 0.08,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
gradient: RadialGradient(
|
||||||
|
colors: [
|
||||||
|
Colors.white.withOpacity(0.0015),
|
||||||
|
Colors.transparent,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 另一个液体反光点
|
||||||
|
Positioned(
|
||||||
|
bottom: size * 0.3,
|
||||||
|
right: size * 0.25,
|
||||||
|
child: Container(
|
||||||
|
width: size * 0.05,
|
||||||
|
height: size * 0.05,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
gradient: RadialGradient(
|
||||||
|
colors: [
|
||||||
|
Colors.white.withOpacity(0.001),
|
||||||
|
Colors.transparent,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 液体流动线条
|
||||||
|
Positioned(
|
||||||
|
top: size * 0.4,
|
||||||
|
left: size * 0.15,
|
||||||
|
child: Container(
|
||||||
|
width: size * 0.7,
|
||||||
|
height: 0.8,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: [
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.white.withOpacity(0.0012),
|
||||||
|
Colors.transparent,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(0.5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 边缘液滴效果 - 顶部
|
||||||
|
Positioned(
|
||||||
|
top: -2,
|
||||||
|
left: size * 0.35,
|
||||||
|
child: Container(
|
||||||
|
width: size * 0.3,
|
||||||
|
height: 4,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(2),
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: [
|
||||||
|
Colors.white.withOpacity(0.02),
|
||||||
|
Colors.white.withOpacity(0.008),
|
||||||
|
Colors.transparent,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 边缘液滴效果 - 底部
|
||||||
|
Positioned(
|
||||||
|
bottom: -2,
|
||||||
|
left: size * 0.4,
|
||||||
|
child: Container(
|
||||||
|
width: size * 0.2,
|
||||||
|
height: 3,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(1.5),
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.bottomCenter,
|
||||||
|
end: Alignment.topCenter,
|
||||||
|
colors: [
|
||||||
|
Colors.white.withOpacity(0.015),
|
||||||
|
Colors.white.withOpacity(0.005),
|
||||||
|
Colors.transparent,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 按钮图标
|
||||||
|
Center(child: child),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -131,8 +131,9 @@ 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 =
|
Permission storagePermission = androidInfo.version.sdkInt >= 33
|
||||||
androidInfo.version.sdkInt >= 33 ? Permission.manageExternalStorage : Permission.storage;
|
? Permission.manageExternalStorage
|
||||||
|
: Permission.storage;
|
||||||
PermissionDescribeUtil.instance.toLaunchPermissionRequest(
|
PermissionDescribeUtil.instance.toLaunchPermissionRequest(
|
||||||
Get.context!,
|
Get.context!,
|
||||||
title: '储存权限请求',
|
title: '储存权限请求',
|
||||||
|
|
@ -229,8 +230,11 @@ 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()
|
String imgKey = UploadOssImgUtils.getInstance().setImgKey(
|
||||||
.setImgKey(param.homeworkId, data.studentId.toString(), data.templateId.toString());
|
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;
|
||||||
|
|
||||||
|
|
@ -302,7 +306,8 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
|
||||||
if (newzgtAnnotate != null) zgtAnnotate = newzgtAnnotate;
|
if (newzgtAnnotate != null) zgtAnnotate = newzgtAnnotate;
|
||||||
|
|
||||||
await getClient()
|
await getClient()
|
||||||
.reviewSubmission(ReviewSubmissionParams(
|
.reviewSubmission(
|
||||||
|
ReviewSubmissionParams(
|
||||||
homeworkId: state.param.value.homeworkId,
|
homeworkId: state.param.value.homeworkId,
|
||||||
templateId: data.templateId,
|
templateId: data.templateId,
|
||||||
studentId: data.studentId,
|
studentId: data.studentId,
|
||||||
|
|
@ -314,31 +319,33 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
|
||||||
questionNo: e.questionNo,
|
questionNo: e.questionNo,
|
||||||
studentScore: studentScore,
|
studentScore: studentScore,
|
||||||
);
|
);
|
||||||
}).toList()))
|
}).toList(),
|
||||||
|
),
|
||||||
|
)
|
||||||
.then((e) async {
|
.then((e) async {
|
||||||
state.needRefresh = true;
|
state.needRefresh = true;
|
||||||
var totalUnAnnotateCount = data.totalUnAnnotateCount;
|
var totalUnAnnotateCount = data.totalUnAnnotateCount;
|
||||||
if (data.needAnnotate) totalUnAnnotateCount -= 1; // 是否需要批阅
|
if (data.needAnnotate) totalUnAnnotateCount -= 1; // 是否需要批阅
|
||||||
|
|
||||||
if (totalUnAnnotateCount <= 0) {
|
if (totalUnAnnotateCount <= 0) {
|
||||||
// 批阅完成
|
// 批阅完成
|
||||||
if (!state.lastQuestionPrompt) {
|
if (!state.lastQuestionPrompt) {
|
||||||
await _showCompletionDialog(context);
|
await _showCompletionDialog(context);
|
||||||
} else {
|
} else {
|
||||||
ToastUtils.showSuccess("批阅已提交", duration: const Duration(milliseconds: 800));
|
ToastUtils.showSuccess("批阅已提交", duration: const Duration(milliseconds: 800));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newParams = DoPaperDetailsParam.fromJson(state.param.value.toJson());
|
var newParams = DoPaperDetailsParam.fromJson(state.param.value.toJson());
|
||||||
if (totalUnAnnotateCount > 0) {
|
if (totalUnAnnotateCount > 0) {
|
||||||
// 当前批阅任务完成 重复提交后 停留在当前数据位置
|
// 当前批阅任务完成 重复提交后 停留在当前数据位置
|
||||||
newParams.templateId = null;
|
newParams.templateId = null;
|
||||||
newParams.studentId = null;
|
newParams.studentId = null;
|
||||||
}
|
}
|
||||||
state.param.value = newParams;
|
state.param.value = newParams;
|
||||||
ToastUtils.showSuccess("批阅已提交");
|
ToastUtils.showSuccess("批阅已提交");
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("批阅提交报错 $e");
|
print("批阅提交报错 $e");
|
||||||
ToastUtils.showError('提交失败,请检查网络连接后重试');
|
ToastUtils.showError('提交失败,请检查网络连接后重试');
|
||||||
|
|
@ -358,7 +365,10 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.check_circle, color: Colors.green, size: 24.sp),
|
Icon(Icons.check_circle, color: Colors.green, size: 24.sp),
|
||||||
SizedBox(width: 8.w),
|
SizedBox(width: 8.w),
|
||||||
Text('批阅已完成', style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w600)),
|
Text(
|
||||||
|
'批阅已完成',
|
||||||
|
style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w600),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
content: Text('暂无更多批阅项,是否继续?', style: TextStyle(fontSize: 14.sp)),
|
content: Text('暂无更多批阅项,是否继续?', style: TextStyle(fontSize: 14.sp)),
|
||||||
|
|
@ -387,13 +397,15 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
|
||||||
try {
|
try {
|
||||||
var data = state.data.value!;
|
var data = state.data.value!;
|
||||||
var param = state.param.value;
|
var param = state.param.value;
|
||||||
await getClient().toFavStudent(FavorParam(
|
await getClient().toFavStudent(
|
||||||
homeworkId: param.homeworkId,
|
FavorParam(
|
||||||
studentId: data.studentId,
|
homeworkId: param.homeworkId,
|
||||||
templateId: data.templateId,
|
studentId: data.studentId,
|
||||||
questionNo: data.studentQuestions[0].questionNo,
|
templateId: data.templateId,
|
||||||
isFav: !data.isFav,
|
questionNo: data.studentQuestions[0].questionNo,
|
||||||
));
|
isFav: !data.isFav,
|
||||||
|
),
|
||||||
|
);
|
||||||
state.favorite.value = !data.isFav;
|
state.favorite.value = !data.isFav;
|
||||||
data.isFav = state.favorite.value;
|
data.isFav = state.favorite.value;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue