2025-01-31 10:24:37 +01:00

369 lines
12 KiB
Dart

import 'package:refilc/models/settings.dart';
import 'package:refilc/theme/colors/colors.dart';
import 'package:refilc_kreta_api/models/grade.dart';
import 'package:refilc/helpers/subject.dart';
import 'package:refilc/utils/format.dart';
import 'package:refilc_mobile_ui/pages/grades/calculator/grade_calculator_provider.dart';
import 'package:refilc_mobile_ui/pages/grades/subject_grades_container.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
class GradeTile extends StatelessWidget {
const GradeTile(
this.grade, {
super.key,
this.onTap,
this.padding,
this.censored = false,
this.viewOverride = false,
});
final Grade grade;
final void Function()? onTap;
final EdgeInsetsGeometry? padding;
final bool censored;
final bool viewOverride;
@override
Widget build(BuildContext context) {
String title;
String subtitle;
bool isTitleItalic = false;
bool isSubtitleItalic = false;
// EdgeInsets leadingPadding = EdgeInsets.zero;
bool isSubjectView =
SubjectGradesContainer.of(context) != null || viewOverride;
String subjectName =
grade.subject.renamedTo ?? grade.subject.name.escapeHtml().capital();
String modeDescription = grade.mode.description.escapeHtml().capital();
String description = grade.description.escapeHtml().capital();
GradeCalculatorProvider calculatorProvider =
Provider.of<GradeCalculatorProvider>(context, listen: false);
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
// Test order:
// description
// mode
// value name
if (grade.type == GradeType.midYear || grade.type == GradeType.ghost) {
if (grade.description != "") {
title = description;
} else {
title = modeDescription != ""
? modeDescription
: grade.value.valueName.split("(")[0];
}
} else {
title = subjectName;
isTitleItalic =
grade.subject.isRenamed && settingsProvider.renamedSubjectsItalics;
}
// Test order:
// subject name
// mode + weight != 100
if (grade.type == GradeType.midYear) {
subtitle = isSubjectView
? description != ""
? modeDescription
: ""
: subjectName;
isSubtitleItalic = isSubjectView
? false
: grade.subject.isRenamed && settingsProvider.renamedSubjectsItalics;
} else {
subtitle = grade.value.valueName.split("(")[0];
}
// if (subtitle != "") leadingPadding = const EdgeInsets.only(top: 2.0);
return Material(
type: MaterialType.transparency,
child: Padding(
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
child: ListTile(
visualDensity: VisualDensity.compact,
contentPadding: isSubjectView
? grade.type != GradeType.ghost
? const EdgeInsets.symmetric(horizontal: 8.0)
: const EdgeInsets.only(left: 8.0, right: 0.0)
: const EdgeInsets.only(left: 8.0, right: 12.0),
onTap: onTap,
// onLongPress: kDebugMode ? () => log(jsonEncode(grade.json)) : null,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
leading: isSubjectView
? GradeValueWidget(
grade.value,
fill: true,
size: 27.5,
)
: GradeValueWidget(
grade.value,
fill: true,
size: 27.5,
),
// leading: isSubjectView
// ? GradeValueWidget(grade.value)
// : SizedBox(
// width: 44,
// height: 44,
// child: censored
// ? Container(
// decoration: BoxDecoration(
// color: AppColors.of(context).text,
// borderRadius: BorderRadius.circular(60.0),
// ),
// )
// : Center(
// child: Padding(
// padding: leadingPadding,
// child: Icon(
// SubjectIcon.resolveVariant(
// subject: grade.subject, context: context),
// size: 28.0,
// color: AppColors.of(context).text,
// ),
// ),
// ),
// ),
title: censored
? Wrap(
children: [
Container(
width: 110,
height: 15,
decoration: BoxDecoration(
color: AppColors.of(context).text,
borderRadius: BorderRadius.circular(8.0),
),
),
],
)
: Text(
title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.w600,
fontStyle: isTitleItalic ? FontStyle.italic : null),
),
subtitle: subtitle != ""
? censored
? Wrap(
children: [
Container(
width: 50,
height: 10,
decoration: BoxDecoration(
color: AppColors.of(context).text,
borderRadius: BorderRadius.circular(8.0),
),
),
],
)
: Text(
subtitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.w500,
fontStyle:
isSubtitleItalic ? FontStyle.italic : null),
)
: null,
trailing: isSubjectView
? grade.type != GradeType.ghost
? Text(grade.date.format(context),
style: const TextStyle(fontWeight: FontWeight.w500))
: IconButton(
splashRadius: 24.0,
icon: Icon(FeatherIcons.trash2,
color: AppColors.of(context).red),
onPressed: () {
calculatorProvider.removeGrade(grade);
},
)
: censored
? Container(
width: 15,
height: 15,
decoration: BoxDecoration(
color: AppColors.of(context).text,
borderRadius: BorderRadius.circular(8.0),
),
)
// : GradeValueWidget(grade.value),
: Icon(
SubjectIcon.resolveVariant(
context: context, subject: grade.subject),
color: AppColors.of(context).text.withValues(alpha: .5),
),
minLeadingWidth: isSubjectView ? 32.0 : 0,
),
),
);
}
}
class GradeValueWidget extends StatelessWidget {
const GradeValueWidget(
this.value, {
super.key,
this.size = 38.0,
this.fill = false,
this.contrast = false,
this.shadow = false,
this.outline = false,
this.complemented = false,
this.nocolor = false,
this.color,
});
final GradeValue value;
final double size;
final bool fill;
final bool contrast;
final bool shadow;
final bool outline;
final bool complemented;
final bool nocolor;
final Color? color;
@override
Widget build(BuildContext context) {
GradeValue value = this.value;
bool isSubjectView = SubjectGradesContainer.of(context) != null;
Color color = this.color ??
gradeColor(context: context, value: value.value, nocolor: nocolor);
Widget valueText;
final percentage = value.percentage;
if (percentage) {
double multiplier = 1.0;
if (isSubjectView) multiplier = 0.75;
valueText = Text.rich(
TextSpan(
text: value.value.toString(),
children: [
TextSpan(
text: "\n%",
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: size / 2.5 * multiplier,
height: 0.7),
),
],
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: size / (isSubjectView ? 1 : 1.5) * multiplier,
height: 1),
),
textAlign: TextAlign.center,
);
} else if (value.valueName.toLowerCase().specialChars() == 'nem irt') {
valueText = Icon(
Icons.edit_off_rounded,
size: size / 1.5,
);
color = AppColors.of(context).text;
} else {
valueText = Stack(alignment: Alignment.topRight, children: [
Transform.translate(
offset: (value.weight >= 200) ? const Offset(1.0, 0.2) : Offset.zero,
child: Text(
value.value.toString(),
textAlign: TextAlign.center,
style: TextStyle(
fontWeight:
value.weight == 50 ? FontWeight.w500 : FontWeight.bold,
fontSize: size,
color: contrast ? Colors.white : color,
shadows: [
if (value.weight >= 200)
Shadow(
color:
(contrast ? Colors.white : color).withValues(alpha: .4),
offset: const Offset(-4, -3),
)
],
),
),
),
if (complemented)
Transform.translate(
offset: const Offset(9, 1),
child: Text(
"*",
style:
TextStyle(fontSize: size / 1.6, fontWeight: FontWeight.bold),
),
),
]);
}
return fill
? Container(
width: size * 1.4,
height: size * 1.4,
decoration: BoxDecoration(
color: color.withValues(alpha: contrast ? 1.0 : .25),
shape: BoxShape.circle,
boxShadow: [
if (shadow &&
Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)
BoxShadow(
color: color,
blurRadius: 62.0,
)
],
),
child: Center(child: valueText),
)
: valueText;
}
}
Color gradeColor(
{required BuildContext context, required num value, bool nocolor = false}) {
int valueInt = 0;
var settings = Provider.of<SettingsProvider>(context, listen: false);
try {
if (value < 2.0) {
valueInt = 1;
} else {
if (value >= value.floor() + settings.rounding / 10) {
valueInt = value.ceil();
} else {
valueInt = value.floor();
}
}
} catch (_) {}
if (nocolor) return AppColors.of(context).text;
switch (valueInt) {
case 5:
return settings.gradeColors[4];
case 4:
return settings.gradeColors[3];
case 3:
return settings.gradeColors[2];
case 2:
return settings.gradeColors[1];
case 1:
return settings.gradeColors[0];
default:
return AppColors.of(context).text;
}
}