Marking.Client.Moblie/marking_app/lib/components/marking/selectable_keyboard_bottom....

426 lines
16 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:marking_app/common/model/enum/KeyboardType.dart';
import 'package:marking_app/common/model/marking/do_marking_keyboard_model.dart';
import 'package:marking_app/common/model/marking/keyboard_assist_event.dart';
import 'package:marking_app/common/model/marking/marking_common_score_items.dart';
import 'package:marking_app/common/model/marking/marking_keyboard_sliding_position.dart';
import 'package:marking_app/common/model/marking/marking_text_question.dart';
import 'package:marking_app/pages/common/event_bus_mixin.dart';
import 'package:marking_app/provider/do_marking_provider.dart';
import 'package:marking_app/utils/index.dart';
import 'package:marking_app/utils/marking_utils/index.dart';
import 'package:marking_app/utils/my_text.dart';
// 选择型键盘
class SelectableKeyboardBottom extends StatefulHookConsumerWidget {
MarkingTextQuestion data;
int markingUserId;
String questScore; // 当前分值
double totalScore; // 总分
bool hasSubtopic = false; // 是否有小题
int subtopicIndex; // 小题下标
SynchroScoreCallback synchroScore;
GestureTapCallback submitCall;
SelectableKeyboardBottom({
required this.data,
required this.markingUserId,
required this.questScore,
required this.totalScore,
required this.synchroScore,
required this.subtopicIndex,
required this.submitCall,
Key? key,
}) : hasSubtopic = data.subQuestionDetailList.isNotEmpty,
super(key: key);
@override
_SelectableKeyboardState createState() => _SelectableKeyboardState();
}
class _SelectableKeyboardState extends ConsumerState<SelectableKeyboardBottom>
with SingleTickerProviderStateMixin, EventBusMixin<KeyboardAssistEvent> {
late AnimationController _animationController;
late ScrollController _scrollController;
bool isRight = true; // 是否是右侧打分键盘
String? _smallScore; // 小分栏打分
late List nums; // 个位数集合
late List tens = []; // 十位数集合
late RemoveListener _markingKeyboardListener;
late RemoveListener _markingSubtopicSwitchingListener;
num? _tensVal; // 当前选中十位分值
late Duration easyThrottleTime;
@override
void initState() {
super.initState();
easyThrottleTime = Duration(milliseconds: widget.hasSubtopic ? 400 : 1000);
// 初始化不执行
_scrollController = ScrollController()..addListener(scrollListener);
_markingSubtopicSwitchingListener = ref.read(markingSubtopicSwitchingProvider.notifier).addListener((state) {
if (widget.subtopicIndex != state) widget.subtopicIndex = state;
DoMarkingKeyboardModel preference = ref.read(markingKeyboardProvider);
initKeyboardSuper(
preference.getScoreStepSize(widget.markingUserId, widget.data.questionNum, widget.data.scoreInterval),
preference.sort!);
toUp();
}, fireImmediately: false);
_markingKeyboardListener = ref.read(markingKeyboardProvider.notifier).addListener((state) {
isRight = state.keyboard == KeyboardType.RIGHT_SELECTION;
initKeyboardSuper(
state.getScoreStepSize(widget.markingUserId, widget.data.questionNum, widget.data.scoreInterval),
state.sort!);
toUp();
});
_animationController = AnimationController(
duration: const Duration(milliseconds: 400),
lowerBound: 72,
upperBound: 144,
vsync: this,
)..addListener(toUp);
// 事件总线监听
// eventOn(callback: (KeyboardAssistEvent item) {
// bool openAuxiliary = item.openAuxiliary;
// openAuxiliary ? _animationController.forward() : _animationController.reverse();
// ref.read(markingKeyboardProvider.notifier).changeOpenAuxiliary(openAuxiliary);
// });
}
void toUp() {
toUpState(setState, () {}, mounted);
}
/** 键盘滚动位置监听 */
void scrollListener() {
// 保存当前滑动位置这里假设使用SharedPreferences存储位置为"scroll_position" data
FastData.getInstance().setMarkingKeyboardSlidingPosition(MarkingKeyboardSlidingPosition(
id: widget.markingUserId,
questionNum: widget.data.questionNum,
position: _scrollController.position.pixels,
isVertical: true,
));
}
@override
void dispose() {
super.dispose();
_markingKeyboardListener();
_markingSubtopicSwitchingListener();
_animationController
..removeListener(toUp)
..dispose();
eventCancel();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// 获取历史滑动位置这里假设使用SharedPreferences存储位置为"scroll_position"
FastData.getInstance().getMarkingKeyboardSlidingPosition().then((historyPosition) {
print('键盘滑动位置:${historyPosition?.toJson()}');
// 检查是否有历史滑动位置并且ListView已经完成渲染
bool flag = historyPosition != null &&
historyPosition.isVertical &&
historyPosition.position >= 0 &&
_scrollController.hasClients &&
historyPosition.questionNum == widget.data.questionNum &&
historyPosition.id == widget.markingUserId;
if (flag) {
// 使用JumpTo或AnimateTo方法滚动到历史位置
_scrollController.jumpTo(historyPosition.position);
// _scrollController.animateTo(
// historyPosition.position,
// duration: Duration(milliseconds: 350),
// curve: Curves.easeInOut,
// );
}
});
}
/// 键盘计算数据
/// @params localScoreInterval 本地设置分值步长
/// @params sort 键盘排序方式
void initKeyboardSuper(double? localScoreInterval, SortKeyboard sort) {
double scoreInterval = widget.data.scoreInterval; // 服务器针对当前实体设置的分值步长
if (localScoreInterval != null && localScoreInterval != 0 && localScoreInterval != scoreInterval) {
scoreInterval = localScoreInterval;
}
print(widget.data.subQuestionDetailList.map((e) => e.toJson()));
print(widget.subtopicIndex);
double totalScore = widget.hasSubtopic
? widget.data.subQuestionDetailList[widget.subtopicIndex].subQuestionScore
: widget.totalScore;
initKeyboard(totalScore, scoreInterval, sort);
}
/// 初始化键盘
/// @params {double} totalScore 总分
/// @params {double} scoreInterval 小分步长
void initKeyboard(double totalScore, double scoreInterval, SortKeyboard sort) {
// tens.clear();
// int floorScore = totalScore.floor(); // 总分向下取整
// int singleDigit = floorScore; // 个位小数
// int numsLength = singleDigit;
// if (floorScore > 10) {
// singleDigit = floorScore;
// for (var i = 10; i < floorScore; i += 10) {
// tens.add(i);
// }
// numsLength = singleDigit;
// if (scoreInterval == 0.5) {
// numsLength = singleDigit * 2;
// }
// } else {
// if (scoreInterval == 0.5) {
// numsLength = (singleDigit) * 2;
// }
// }
// nums = List.generate(numsLength, (e) => e * scoreInterval); // 小分键盘
// if (sort != SortKeyboard.FULL_AND_AERO_TOP) {
// nums = nums.reversed.toList();
// }
tens.clear();
int floorScore = totalScore.floor(); // 总分向下取整
int singleDigit = floorScore; // 个位小数
int numsLength = singleDigit;
if (floorScore > 10) {
singleDigit = 9;
for (var i = 10; i < floorScore; i += 10) {
tens.add(i);
}
}
numsLength = ((numsLength - (numsLength % scoreInterval)) / scoreInterval).floor();
/// +1 添加满分
nums = List.generate(numsLength + 1, (e) => e * scoreInterval); // 小分键盘
// 加一个总分
if (numsLength * scoreInterval < totalScore) nums.add(totalScore);
if (sort != SortKeyboard.FULL_AND_AERO_TOP) {
nums = nums.reversed.toList();
}
}
/// 打分
/// @params {num} fraction 分数
/// @params {bool} isTens 十位分数
/// @params {bool} fullScore 满分
Future<void> marking({required num fraction, String? strScore, bool isTens = false, bool fullScore = false}) async {
// 零分的处理
bool allWrong = fraction == 0 && fullScore;
if (allWrong) {
fullScore = false;
}
// 十位
if (!fullScore && isTens) {
_tensVal = fraction;
return await widget.synchroScore(
score: fraction.toDouble(),
continueScoring: true,
hasSubtopic: widget.hasSubtopic,
);
}
EasyLoading.show(status: 'loading...');
try {
double theScore = fraction.toDouble();
if (fullScore) {
// 满分
// theScore = widget.hasSubtopic
// ? widget.data.subQuestionDetailList[widget.subtopicIndex].subQuestionScore
// : fraction.toDouble();
theScore = widget.data.totalScore;
} else if (_tensVal != null) {
// 有十位数
theScore = (_tensVal! + fraction).toDouble();
}
if (theScore > widget.totalScore) {
return ToastUtils.showInfo('评分已大于总分,请重新评分');
}
await widget
.synchroScore(
score: theScore,
continueScoring: false,
allWrong: allWrong,
hasSubtopic: widget.hasSubtopic,
)
.then((_) {
if (widget.hasSubtopic) {
// 是否有小题
List<SubQuestions> subQuestionDetailList = widget.data.subQuestionDetailList;
if (subQuestionDetailList.map((e) => e.completeRating).contains(false)) {
return;
}
}
widget.submitCall(); // 提交
});
} catch (e) {
toPrint(val: e.toString());
} finally {
EasyLoading.dismiss();
}
}
@override
Widget build(BuildContext context) {
DoMarkingKeyboardModel model = ref.watch(markingKeyboardProvider);
SortKeyboard sort = model.sort!;
bool isBroadwise = model.screenDirection == ScreenDirection.HORIZONTAL_SCREEN; // 是否横向
MarkingCommonScoreItems? commonScores = model.commonScores;
List<double> theCommonScores = [];
if (commonScores != null &&
commonScores.questionNum == widget.data.questionNum &&
commonScores.id == widget.markingUserId) {
theCommonScores = commonScores.score;
}
double totalScore = widget.hasSubtopic
? widget.data.subQuestionDetailList[widget.subtopicIndex].subQuestionScore
: widget.totalScore;
return Container(
height: 62.h,
width: double.infinity,
padding: EdgeInsets.only(left: 4.h, right: 4.h),
decoration: BoxDecoration(
// boxShadow: [
// BoxShadow(
// color: const Color.fromRGBO(46, 91, 255, 0.2),
// offset: Offset(26.h, 0), //阴影y轴偏移量
// blurRadius: 1, //阴影模糊程度
// spreadRadius: 0.4, //阴影扩散程度
// )
// ],
// color: Colors.white,
),
child: Row(
children: [
Expanded(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 4.h, vertical: 4.h),
color: Colors.transparent,
child: ListView(
controller: _scrollController,
scrollDirection: Axis.horizontal,
children: [
InkWell(
onTap: () {
easyThrottle(
'toMarkingVal',
() => marking(
fraction: widget.totalScore, fullScore: true, strScore: widget.totalScore.toString()),
duration: easyThrottleTime);
},
child: Container(
width: isBroadwise ? 28.w : 52.w,
height: double.infinity,
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(
width: 1.h,
color: const Color.fromRGBO(224, 230, 255, 1),
),
color: const Color.fromRGBO(249, 250, 254, 1),
borderRadius: BorderRadius.all(Radius.circular(2.w)),
),
child: quickText(widget.hasSubtopic ? '全对' : '满分',
size: 15.sp, color: const Color.fromRGBO(148, 163, 182, 1)),
),
),
InkWell(
onTap: () {
easyThrottle('toMarkingVal', () => marking(fraction: 0, fullScore: true, strScore: '0'),
duration: easyThrottleTime);
},
child: Container(
width: isBroadwise ? 28.w : 52.w,
height: double.infinity,
alignment: Alignment.center,
margin: EdgeInsets.only(left: 3.h),
decoration: BoxDecoration(
border: Border.all(width: 1.h, color: const Color.fromRGBO(224, 230, 255, 1)),
color: const Color.fromRGBO(249, 250, 254, 1),
borderRadius: BorderRadius.all(Radius.circular(2.w)),
),
child: quickText(widget.hasSubtopic ? '全错' : '零分',
size: 15.sp, color: const Color.fromRGBO(148, 163, 182, 1)),
),
),
...[...theCommonScores, ...nums].map((e) {
String strScore = getDoubleRemoveZero(e);
bool isCurrentSelect = _smallScore == strScore; // 当前选中
bool effective = e <= totalScore;
return InkWell(
onTap: effective
? () {
easyThrottle('toMarkingVal', () => marking(fraction: e, strScore: strScore),
duration: easyThrottleTime);
}
: null,
child: Container(
width: isBroadwise ? 28.w : 52.w,
height: double.infinity,
margin: EdgeInsets.only(left: 3.h),
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(width: 1.h, color: const Color.fromRGBO(224, 230, 255, 1)),
color: isCurrentSelect
? Theme.of(context).primaryColor
: effective
? const Color.fromRGBO(249, 250, 254, 1)
: Colors.grey[300],
borderRadius: BorderRadius.all(Radius.circular(2.w)),
),
child: quickText(
strScore,
size: 15.sp,
color: isCurrentSelect ? Colors.white : const Color.fromRGBO(148, 163, 182, 1),
),
),
);
}).toList(),
// if (sort != SortKeyboard.FULL_AND_AERO_TOP)
// InkWell(
// onTap: () {
// easyThrottle(
// 'toMarkingVal',
// () => marking(
// fraction: widget.totalScore, fullScore: true, strScore: widget.totalScore.toString()),
// duration: easyThrottleTime);
// },
// child: Container(
// width: isBroadwise ? 28.w : 52.w,
// height: double.infinity,
// margin: EdgeInsets.only(left: 3.h),
// alignment: Alignment.center,
// decoration: BoxDecoration(
// border: Border.all(width: 1.h, color: const Color.fromRGBO(224, 230, 255, 1)),
// color: const Color.fromRGBO(249, 250, 254, 1),
// borderRadius: BorderRadius.all(Radius.circular(2.w)),
// ),
// child: quickText('满分', size: 15.sp, color: const Color.fromRGBO(148, 163, 182, 1)),
// ),
// ),
],
),
),
)
],
),
);
}
}