471 lines
15 KiB
Dart
471 lines
15 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||
import 'package:marking_app/common/model/marking/marking_text_question.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';
|
||
|
||
// 输入型打分键盘
|
||
class InputKeyboard extends StatefulHookConsumerWidget {
|
||
BoxDecoration? dexs;
|
||
BoxDecoration? notDexs;
|
||
TextStyle? theSty;
|
||
|
||
QuestionTer? qter;
|
||
final MarkingTextQuestion data;
|
||
String questScore; // 当前分值
|
||
String totalScore; // 总分
|
||
bool hasSubtopic = false; // 是否有小题
|
||
int subtopicIndex; // 小题下标
|
||
SynchroScoreCallback synchroScore;
|
||
GestureTapCallback submitCall;
|
||
GestureTapCallback closeKeyboard;
|
||
|
||
InputKeyboard({
|
||
required this.data,
|
||
required this.questScore,
|
||
required this.totalScore,
|
||
required this.synchroScore,
|
||
required this.subtopicIndex,
|
||
required this.submitCall,
|
||
required this.closeKeyboard,
|
||
this.dexs,
|
||
this.notDexs,
|
||
this.theSty,
|
||
Key? key,
|
||
}) : super(key: key) {
|
||
hasSubtopic = data.subQuestionDetailList.isNotEmpty; // 是否有小题
|
||
dexs = dexs ??
|
||
BoxDecoration(
|
||
color: const Color.fromRGBO(249, 250, 254, 1),
|
||
borderRadius: BorderRadius.all(Radius.circular(4.w)),
|
||
border: Border.all(
|
||
width: 0.5.w,
|
||
color: const Color.fromRGBO(224, 230, 255, 1),
|
||
),
|
||
);
|
||
notDexs = notDexs ??
|
||
BoxDecoration(
|
||
color: Colors.grey,
|
||
borderRadius: BorderRadius.all(Radius.circular(4.w)),
|
||
border: Border.all(
|
||
width: 0.5.w,
|
||
color: const Color.fromRGBO(224, 230, 255, 1),
|
||
),
|
||
);
|
||
|
||
theSty = theSty ??
|
||
TextStyle(
|
||
fontSize: 14.sp,
|
||
fontWeight: FontWeight.w400,
|
||
color: const Color.fromRGBO(80, 87, 103, 1),
|
||
);
|
||
|
||
qter = QuestionTer(item: data);
|
||
}
|
||
|
||
@override
|
||
_InputTypeKeyboardState createState() => _InputTypeKeyboardState();
|
||
}
|
||
|
||
class _InputTypeKeyboardState extends ConsumerState<InputKeyboard> {
|
||
String questScore = ''; // 已打分值
|
||
bool hasZeroPointFive = false;
|
||
late Offset keyboardOrigin; // 键盘滑动起始坐标
|
||
late Offset keyboarddestination; // 键盘滑动截止坐标
|
||
late RemoveListener _markingSubtopicSwitchingListener;
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
widget.qter?.addListener(() {
|
||
hasZeroPointFive = false;
|
||
});
|
||
|
||
_markingSubtopicSwitchingListener = ref.read(markingSubtopicSwitchingProvider.notifier).addListener((state) {
|
||
if (widget.subtopicIndex != state) widget.subtopicIndex = state;
|
||
if (widget.subtopicIndex > -1 && widget.hasSubtopic) {
|
||
toUpState(setState, () {
|
||
questScore = (widget.data.subQuestionDetailList[state].subQuestionGotScore ?? '').toString();
|
||
}, mounted);
|
||
}
|
||
}, fireImmediately: false);
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
super.dispose();
|
||
_markingSubtopicSwitchingListener();
|
||
widget.qter?.dispose();
|
||
}
|
||
|
||
// 清空得分
|
||
void cleanCurrentScore() {
|
||
toUpState(setState, () {
|
||
hasZeroPointFive = false;
|
||
questScore = '';
|
||
}, mounted);
|
||
|
||
widget.synchroScore(
|
||
score: 0,
|
||
continueScoring: false,
|
||
hasSubtopic: widget.hasSubtopic,
|
||
cleanScore: true,
|
||
);
|
||
}
|
||
|
||
// 同步小题得分
|
||
void synchroScore(String score, {bool full = false, bool zeroScore = false}) {
|
||
MarkingTextQuestion currentQuestion = widget.data;
|
||
bool hasSubtopic = widget.hasSubtopic; // 是否有小题
|
||
|
||
int subtopicIndex = widget.subtopicIndex; // 小题下标
|
||
List<SubQuestions> subQuestionDetailList = currentQuestion.subQuestionDetailList; // 小题集合
|
||
double totalScore =
|
||
hasSubtopic ? subQuestionDetailList[subtopicIndex].subQuestionScore : currentQuestion.totalScore; // 总分
|
||
|
||
double? doubleScore;
|
||
if (zeroScore) {
|
||
doubleScore = 0;
|
||
} else if (full) {
|
||
doubleScore = totalScore;
|
||
} else {
|
||
String newquestScore = questScore;
|
||
|
||
/** 这是拼接打分 如:1+1 = 11
|
||
if (questScore != '') {
|
||
List<String> theScores = questScore.split('.');
|
||
if (theScores.length > 1) {
|
||
if (theScores[1] == '0') {
|
||
newquestScore = theScores[0];
|
||
} else {
|
||
newquestScore = '';
|
||
}
|
||
// newquestScore
|
||
}
|
||
}
|
||
*/
|
||
|
||
// doubleScore = double.parse(newquestScore + score); // 当前得分
|
||
|
||
doubleScore = double.parse(newquestScore == '' ? '0' : newquestScore) + double.parse(score);
|
||
print('最终得分111:${doubleScore}');
|
||
}
|
||
|
||
if (!full) {
|
||
// 非满分
|
||
if (doubleScore > totalScore) {
|
||
// ToastUtils.showError('打分不得高于本${hasSubtopic ? '小' : ''}题总分,$totalScore 分');
|
||
// score = '';
|
||
// doubleScore = null;
|
||
// return;
|
||
|
||
// 打分超过满分就定义为满分
|
||
score = totalScore.toString();
|
||
} else {
|
||
score = doubleScore.toString();
|
||
}
|
||
} else {
|
||
score = totalScore.toString();
|
||
}
|
||
|
||
// 处理得分小数点 小数点后为0 取整
|
||
List scoreStrList = score.toString().split('.');
|
||
if (scoreStrList.length > 1) {
|
||
if (double.parse(scoreStrList[1]) == 0) {
|
||
score = scoreStrList[0];
|
||
doubleScore = double.parse(score);
|
||
}
|
||
}
|
||
|
||
toUpState(setState, () {
|
||
hasZeroPointFive = full || questScore.split('.').length > 1; // 包含了小数0.5
|
||
questScore = score;
|
||
}, mounted);
|
||
widget.synchroScore(score: doubleScore, continueScoring: hasZeroPointFive, hasSubtopic: hasSubtopic);
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
BoxDecoration _notDexs = widget.notDexs!;
|
||
BoxDecoration _dexs = widget.dexs!;
|
||
|
||
SubQuestions? subQuest; // 小题
|
||
List<SubQuestions>? subQuestionDetailList; // 小题集合
|
||
if (widget.hasSubtopic) {
|
||
subQuestionDetailList = widget.data.subQuestionDetailList;
|
||
subQuest = subQuestionDetailList[widget.subtopicIndex];
|
||
}
|
||
|
||
return Container(
|
||
width: 94.w,
|
||
height: ScreenUtil().screenHeight - 60.h,
|
||
padding: EdgeInsets.all(6.w),
|
||
margin: EdgeInsets.symmetric(vertical: 10.h),
|
||
decoration: BoxDecoration(
|
||
color: Colors.white,
|
||
borderRadius: BorderRadius.circular(4.w),
|
||
boxShadow: [
|
||
BoxShadow(
|
||
color: const Color.fromRGBO(46, 91, 255, 0.2),
|
||
offset: Offset(2.w, 2.h), //阴影y轴偏移量
|
||
blurRadius: 14, //阴影模糊程度
|
||
spreadRadius: 0.5, //阴影扩散程度
|
||
)
|
||
],
|
||
),
|
||
child: GestureDetector(
|
||
behavior: HitTestBehavior.translucent,
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
// 当前分值
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Container(
|
||
width: 53.w,
|
||
height: 20.w,
|
||
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
||
alignment: Alignment.centerLeft,
|
||
decoration: widget.dexs,
|
||
child: Text(widget.questScore, style: widget.theSty),
|
||
),
|
||
InkWell(
|
||
onTap: cleanCurrentScore,
|
||
child: Container(
|
||
width: 24.w,
|
||
height: 20.w,
|
||
alignment: Alignment.center,
|
||
decoration: widget.dexs,
|
||
child: Icon(
|
||
Icons.clear_sharp,
|
||
size: 24.sp,
|
||
color: const Color.fromRGBO(80, 87, 103, 1),
|
||
),
|
||
),
|
||
)
|
||
],
|
||
),
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
ScoreButton(
|
||
'满分',
|
||
widget.theSty!,
|
||
widget.dexs!,
|
||
(val) {
|
||
synchroScore(widget.hasSubtopic ? (subQuest!.subQuestionScore.toString()) : widget.totalScore,
|
||
full: true);
|
||
},
|
||
),
|
||
ScoreButton(
|
||
'零分',
|
||
widget.theSty!,
|
||
widget.dexs!,
|
||
(val) => synchroScore('0', zeroScore: true),
|
||
),
|
||
ScoreButton(
|
||
'.5',
|
||
widget.theSty!,
|
||
hasZeroPointFive ? _notDexs : _dexs,
|
||
(val) {
|
||
if (!hasZeroPointFive) {
|
||
synchroScore(val);
|
||
}
|
||
},
|
||
),
|
||
],
|
||
),
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
ScoreButton(
|
||
'7',
|
||
widget.theSty!,
|
||
hasZeroPointFive ? _notDexs : _dexs,
|
||
(val) {
|
||
if (!hasZeroPointFive) {
|
||
synchroScore(val);
|
||
}
|
||
},
|
||
),
|
||
ScoreButton(
|
||
'8',
|
||
widget.theSty!,
|
||
hasZeroPointFive ? _notDexs : _dexs,
|
||
(val) {
|
||
if (!hasZeroPointFive) {
|
||
synchroScore(val);
|
||
}
|
||
},
|
||
),
|
||
ScoreButton(
|
||
'9',
|
||
widget.theSty!,
|
||
hasZeroPointFive ? _notDexs : _dexs,
|
||
(val) {
|
||
if (!hasZeroPointFive) {
|
||
synchroScore(val);
|
||
}
|
||
},
|
||
),
|
||
],
|
||
),
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
ScoreButton(
|
||
'4',
|
||
widget.theSty!,
|
||
hasZeroPointFive ? _notDexs : _dexs,
|
||
(val) {
|
||
if (!hasZeroPointFive) {
|
||
synchroScore(val);
|
||
}
|
||
},
|
||
),
|
||
ScoreButton(
|
||
'5',
|
||
widget.theSty!,
|
||
hasZeroPointFive ? _notDexs : _dexs,
|
||
(val) {
|
||
if (!hasZeroPointFive) {
|
||
synchroScore(val);
|
||
}
|
||
},
|
||
),
|
||
ScoreButton(
|
||
'6',
|
||
widget.theSty!,
|
||
hasZeroPointFive ? _notDexs : _dexs,
|
||
(val) {
|
||
if (!hasZeroPointFive) {
|
||
synchroScore(val);
|
||
}
|
||
},
|
||
),
|
||
],
|
||
),
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
ScoreButton(
|
||
'1',
|
||
widget.theSty!,
|
||
hasZeroPointFive ? _notDexs : _dexs,
|
||
(val) {
|
||
if (!hasZeroPointFive) {
|
||
synchroScore(val);
|
||
}
|
||
},
|
||
),
|
||
ScoreButton(
|
||
'2',
|
||
widget.theSty!,
|
||
hasZeroPointFive ? _notDexs : _dexs,
|
||
(val) {
|
||
if (!hasZeroPointFive) {
|
||
synchroScore(val);
|
||
}
|
||
},
|
||
),
|
||
ScoreButton(
|
||
'3',
|
||
widget.theSty!,
|
||
hasZeroPointFive ? _notDexs : _dexs,
|
||
(val) {
|
||
if (!hasZeroPointFive) {
|
||
synchroScore(val);
|
||
}
|
||
},
|
||
),
|
||
],
|
||
),
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
ScoreButton(
|
||
'0',
|
||
widget.theSty!,
|
||
hasZeroPointFive ? _notDexs : _dexs,
|
||
(val) {
|
||
if (!hasZeroPointFive) {
|
||
synchroScore(val);
|
||
}
|
||
},
|
||
),
|
||
InkWell(
|
||
onTap: () {
|
||
easyThrottle('toMarkingVal', () => widget.submitCall(), duration: Duration(seconds: 1));
|
||
},
|
||
child: Container(
|
||
width: 53.w,
|
||
height: 20.w,
|
||
alignment: Alignment.center,
|
||
decoration: BoxDecoration(
|
||
color: const Color.fromRGBO(54, 97, 255, 1),
|
||
borderRadius: BorderRadius.all(Radius.circular(4.w)),
|
||
border: Border.all(width: 0.5.w, color: const Color.fromRGBO(224, 230, 255, 1)),
|
||
),
|
||
child: Text(
|
||
'提 交',
|
||
style: TextStyle(
|
||
fontSize: 14.sp,
|
||
fontWeight: FontWeight.w500,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
onPanStart: (DragStartDetails detail) => keyboardOrigin = detail.localPosition,
|
||
onPanUpdate: (DragUpdateDetails detail) => keyboarddestination = detail.localPosition,
|
||
onPanEnd: (DragEndDetails details) {
|
||
bool flag = (keyboardOrigin.dy - keyboarddestination.dy).abs() > 50.0 ||
|
||
(keyboardOrigin.dx - keyboarddestination.dx).abs() > 40.0;
|
||
if (!flag) return;
|
||
widget.closeKeyboard();
|
||
},
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
// 打分按钮
|
||
// ignore: must_be_immutable
|
||
class ScoreButton extends StatelessWidget {
|
||
String scoreVal;
|
||
Function call;
|
||
final TextStyle _theSty;
|
||
final BoxDecoration dexs;
|
||
ScoreButton(this.scoreVal, this._theSty, this.dexs, this.call, {Key? key}) : super(key: key);
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return InkWell(
|
||
onTap: () => call(scoreVal),
|
||
child: Container(
|
||
width: 24.w,
|
||
height: 20.w,
|
||
alignment: Alignment.center,
|
||
decoration: dexs,
|
||
child: Text(scoreVal, style: _theSty),
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
// 监听试题数据变化
|
||
class QuestionTer extends ChangeNotifier {
|
||
MarkingTextQuestion item;
|
||
|
||
QuestionTer({required this.item}) {
|
||
notifyListeners();
|
||
}
|
||
}
|