1
0
forked from firka/firka

ref: lesson card

This commit is contained in:
checkedear
2026-04-18 13:36:09 +02:00
parent 21845f89a8
commit 251e8de446
4 changed files with 199 additions and 390 deletions

View File

@@ -654,8 +654,6 @@ Future<void> showGradeBottomSheet(
SizedBox(height: 20), SizedBox(height: 20),
LessonWidget( LessonWidget(
data, data,
[],
[],
null, null,
Lesson( Lesson(
uid: "-2", uid: "-2",
@@ -683,8 +681,6 @@ Future<void> showGradeBottomSheet(
lastModifiedAt: timeNow(), lastModifiedAt: timeNow(),
), ),
null, null,
null,
placeholderMode: true,
), ),
FirkaCard( FirkaCard(
left: [ left: [
@@ -827,8 +823,6 @@ Future<void> showHomeworkBottomSheet(
SizedBox(height: 8), SizedBox(height: 8),
LessonWidget( LessonWidget(
data, data,
[],
[],
null, null,
Lesson( Lesson(
uid: "-1", uid: "-1",
@@ -848,8 +842,6 @@ Future<void> showHomeworkBottomSheet(
lastModifiedAt: timeNow(), lastModifiedAt: timeNow(),
), ),
null, null,
null,
placeholderMode: true,
), ),
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,

View File

@@ -3,6 +3,7 @@ import 'package:firka/core/settings.dart';
import 'package:firka/ui/components/firka_card.dart'; import 'package:firka/ui/components/firka_card.dart';
import 'package:firka/app/app_state.dart'; import 'package:firka/app/app_state.dart';
import 'package:firka/ui/theme/style.dart'; import 'package:firka/ui/theme/style.dart';
import 'package:firka_common/ui/components/filled_circle.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:majesticons_flutter/majesticons_flutter.dart'; import 'package:majesticons_flutter/majesticons_flutter.dart';
@@ -16,24 +17,16 @@ import 'bubble_test.dart';
class LessonWidget extends StatelessWidget { class LessonWidget extends StatelessWidget {
final AppInitialization data; final AppInitialization data;
final List<Lesson> week;
final List<Lesson> day;
final int? lessonNo; final int? lessonNo;
final Lesson lesson; final Lesson lesson;
final Test? test; final Test? test;
final Lesson? nextLesson;
final bool? placeholderMode;
const LessonWidget( const LessonWidget(
this.data, this.data,
this.week,
this.day,
this.lessonNo, this.lessonNo,
this.lesson, this.lesson,
this.test, this.test, {
this.nextLesson, {
super.key, super.key,
this.placeholderMode,
}); });
@override @override
@@ -42,26 +35,22 @@ class LessonWidget extends StatelessWidget {
.group("settings") .group("settings")
.subGroup("timetable_toast") .subGroup("timetable_toast")
.boolean("tests_and_homework"); .boolean("tests_and_homework");
final showSubstitutions = data.settings
.group("settings")
.subGroup("timetable_toast")
.boolean("substitution");
final showLessonNos = data.settings
.group("settings")
.subGroup("timetable_toast")
.boolean("lesson_no");
final isSubstituted = lesson.substituteTeacher != null; final isSubstituted = lesson.substituteTeacher != null;
final showSubstitutions =
isSubstituted &&
data.settings
.group("settings")
.subGroup("timetable_toast")
.boolean("substitution");
final showLessonNos =
lessonNo != null &&
data.settings
.group("settings")
.subGroup("timetable_toast")
.boolean("lesson_no");
final isDismissed = lesson.type.name == "UresOra"; final isDismissed = lesson.type.name == "UresOra";
var showBreak = false;
if (week.isNotEmpty) {
showBreak =
timeNow().isAfter(lesson.start) && timeNow().isBefore(lesson.end) ||
timeNow().isAfter(week.last.end) ||
lesson.start.getMidnight() != timeNow().getMidnight() ||
timeNow().isAfter(day.last.end) ||
timeNow().isBefore(day.first.start);
}
var accent = appStyle.colors.accent; var accent = appStyle.colors.accent;
var secondary = appStyle.colors.secondary; var secondary = appStyle.colors.secondary;
var bgColor = appStyle.colors.a15p; var bgColor = appStyle.colors.a15p;
@@ -79,16 +68,9 @@ class LessonWidget extends StatelessWidget {
List<Widget> elements = []; List<Widget> elements = [];
var subjectName = lesson.subject?.name ?? 'N/A'; var subjectName = lesson.subject?.name.firstUpper() ?? 'N/A';
if (subjectName.length >= 19) {
subjectName = "${subjectName.substring(0, 19 - 3)}...";
}
subjectName = subjectName.firstUpper();
var roomName = lesson.roomName ?? 'N/A'; var roomName = lesson.roomName ?? 'N/A';
if (roomName.length >= 11) {
roomName = "${roomName.substring(0, 11 - 3)}...";
}
elements.add( elements.add(
GestureDetector( GestureDetector(
@@ -105,144 +87,143 @@ class LessonWidget extends StatelessWidget {
test, test,
); );
}, },
child: FirkaCard( child: FirkaCard.single(
height: 64,
margin: EdgeInsets.all(0),
padding: EdgeInsets.only(left: 14, right: 16),
color: isDismissed color: isDismissed
? appStyle.colors.cardTranslucent ? appStyle.colors.cardTranslucent
: appStyle.colors.card, : appStyle.colors.card,
attached: showTests && test != null ? Attach.bottom : Attach.none,
shadow: !isDismissed, shadow: !isDismissed,
left: [ child: Row(
showLessonNos == false || lessonNo == null children: [
? SizedBox() !showLessonNos
: SizedBox( ? SizedBox()
width: 18, : SizedBox(
height: 18, width: 18,
child: Stack( height: 18,
alignment: Alignment.center, child: Stack(
children: [ alignment: Alignment.center,
SvgPicture.asset( children: [
"assets/icons/subtract.svg", SvgPicture.asset(
color: bgColor, "assets/icons/subtract.svg",
width: 18, color: bgColor,
height: 18, width: 18,
), height: 18,
Text( ),
lessonNo.toString(), Text(
style: appStyle.fonts.B_12R.apply(color: secondary), lessonNo.toString(),
textAlign: TextAlign.center, style: appStyle.fonts.B_12R.apply(color: secondary),
), textAlign: TextAlign.center,
], ),
), ],
), ),
Transform.translate( ),
offset: Offset(-4, 0), FilledCircle(
child: Card( diameter: 36,
shadowColor: Colors.transparent, color: bgColor,
color: bgColor, child: ClassIconWidget(
shape: RoundedRectangleBorder( uid: lesson.uid,
borderRadius: BorderRadius.circular(16), className: lesson.name,
), category: subjectName,
child: Stack( color: accent,
children: [ size: 24,
Padding( ),
padding: EdgeInsetsGeometry.all(4), ),
child: ClassIconWidget( SizedOverflowBox(
color: accent, size: Size(12, 0),
size: 20, child: !showTests && test != null
uid: lesson.uid, ? Transform.translate(
className: lesson.name, offset: Offset(4, -20),
category: lesson.subject?.name != null child: BubbleTest(),
? lesson.subject!.name.firstUpper() )
: '', : SizedBox(),
),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
spacing: 12,
children: [
LimitedBox(
maxWidth: 150,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
subjectName,
style: appStyle.fonts.B_16SB.apply(
color: !isDismissed
? appStyle.colors.textPrimary
: appStyle.colors.textSecondary,
),
overflow: TextOverflow.ellipsis,
),
showSubstitutions
? Text(
lesson.substituteTeacher!,
style: appStyle.fonts.B_14R.apply(
color: appStyle.colors.textSecondary,
),
)
: SizedBox(),
],
),
),
Flexible(
fit: FlexFit.loose,
child: Card(
shadowColor: Colors.transparent,
color: appStyle.colors.a15p,
margin: EdgeInsets.all(0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 6),
child: Text(
roomName,
style: appStyle.fonts.B_12R.apply(
color: appStyle.colors.textSecondary,
),
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
),
),
), ),
), ),
!showTests && test != null
? Transform.translate(
offset: Offset(26, -18),
child: BubbleTest(),
)
: SizedBox(),
], ],
), ),
), ),
), SizedBox(width: 8),
SizedBox(width: !showTests && test != null ? 16 : 8), Column(
Text( mainAxisSize: MainAxisSize.min,
subjectName, crossAxisAlignment: CrossAxisAlignment.end,
style: appStyle.fonts.B_15SB.apply( children: [
color: appStyle.colors.textPrimary, Text(
), lesson.start.toLocal().format(data.l10n, FormatMode.hmm),
),
],
right: [
placeholderMode == true
? SizedBox()
: Text(
isDismissed
? data.l10n.class_dismissed
: lesson.start.toLocal().format(
data.l10n,
FormatMode.hmm,
),
style: appStyle.fonts.B_14R.apply( style: appStyle.fonts.B_14R.apply(
color: appStyle.colors.textPrimary, color: appStyle.colors.textPrimary,
), ),
), ),
placeholderMode == true Text(
? SizedBox() lesson.end.toLocal().format(data.l10n, FormatMode.hmm),
: isDismissed style: appStyle.fonts.B_14R.apply(
? SizedBox() color: appStyle.colors.textPrimary,
: Card(
shadowColor: Colors.transparent,
color: appStyle.colors.a15p,
child: Padding(
padding: EdgeInsets.all(5),
child: Text(
roomName,
style: appStyle.fonts.B_12R.apply(
color: appStyle.colors.secondary,
),
),
), ),
), ),
], ],
),
],
),
), ),
), ),
); );
if (isSubstituted && showSubstitutions) {
elements.add(
FirkaCard(
left: [
Text(
data.l10n.class_substitution,
style: appStyle.fonts.H_16px.apply(
color: appStyle.colors.textPrimary,
),
),
],
right: [
Text(
lesson.substituteTeacher!,
style: appStyle.fonts.B_16R.apply(
color: appStyle.colors.textSecondary,
),
),
],
),
);
}
if (test != null && showTests) { if (test != null && showTests) {
var theme = test!.theme; var theme = test!.theme.firstUpper();
if (theme.length >= 20) { var method = test!.method.description.firstUpper();
theme = "${theme.substring(0, 20)}...";
}
var method = test!.method.description ?? 'N/A';
if (method.length >= 15) {
method = "${method.substring(0, 15)}...";
}
theme = theme.firstUpper();
method = method.firstUpper();
elements.add( elements.add(
GestureDetector( GestureDetector(
@@ -259,25 +240,28 @@ class LessonWidget extends StatelessWidget {
); );
}, },
child: FirkaCard( child: FirkaCard(
margin: EdgeInsets.only(top: 4),
attached: Attach.top,
left: [ left: [
FirkaIconWidget( FirkaIconWidget(
FirkaIconType.majesticons, FirkaIconType.majesticons,
Majesticon.editPen4Solid, Majesticon.editPen4Solid,
color: appStyle.colors.accent, color: appStyle.colors.accent,
size: 20,
), ),
SizedBox(width: 6), SizedBox(width: 8),
Text( Text(
theme, theme,
style: appStyle.fonts.B_16SB.apply( style: appStyle.fonts.B_16SB.apply(
color: appStyle.colors.textSecondary, color: appStyle.colors.textPrimary,
), ),
), ),
], ],
right: [ right: [
Text( Text(
method, method,
style: appStyle.fonts.B_16R.apply( style: appStyle.fonts.B_14R.apply(
color: appStyle.colors.textTertiary, color: appStyle.colors.textSecondary,
), ),
), ),
], ],
@@ -286,201 +270,6 @@ class LessonWidget extends StatelessWidget {
); );
} }
if (nextLesson != null) {
var breakMins = nextLesson!.start.difference(lesson.end).inMinutes;
var seqSchedule = week.getAllSeqs(lesson);
if (breakMins > 45) {
final breakEnd = lesson.end.add(Duration(minutes: breakMins));
final emptyClass = seqSchedule.firstWhereOrNull(
(lesson2) =>
lesson2.start.isAfter(lesson.end) &&
lesson2.end.isBefore(breakEnd),
);
if (emptyClass != null) {
final preBreak = emptyClass.start.difference(lesson.end).inMinutes;
final postBreak = breakEnd.difference(emptyClass.end).inMinutes;
if (data.settings
.group("settings")
.subGroup("timetable_toast")
.boolean("breaks") &&
showBreak) {
elements.add(
FirkaCard(
color: appStyle.colors.cardTranslucent,
shadow: false,
left: [
Text(
data.l10n.breakTxt,
style: appStyle.fonts.B_16SB.apply(
color: appStyle.colors.textSecondary,
),
),
],
right: [
Text(
"$preBreak ${preBreak == 1 ? data.l10n.starting_min : data.l10n.starting_min_plural}",
style: appStyle.fonts.B_16R.apply(
color: appStyle.colors.textTertiary,
),
),
],
),
);
}
elements.add(
FirkaCard(
left: [
SizedBox(
width: 18,
height: 18,
child: Stack(
children: [
SvgPicture.asset(
"assets/icons/subtract.svg",
color: bgColor,
width: 18,
height: 18,
),
Padding(
padding: EdgeInsets.only(left: 5),
child: Text(
emptyClass.lessonNumber.toString(),
style: appStyle.fonts.B_12R.apply(color: secondary),
),
),
],
),
),
Transform.translate(
offset: Offset(-4, 0),
child: Card(
shadowColor: Colors.transparent,
color: bgColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: EdgeInsetsGeometry.all(4),
child: FirkaIconWidget(
FirkaIconType.majesticonsLocal,
'cupFilled',
color: appStyle.colors.accent,
size: 24,
),
),
),
),
SizedBox(width: 8),
Text(
data.l10n.empty_class,
style: appStyle.fonts.B_16SB.apply(
color: appStyle.colors.textPrimary,
),
),
],
right: [
Text(
isDismissed
? data.l10n.class_dismissed
: "${emptyClass.start.toLocal().format(data.l10n, FormatMode.hmm)} - ${emptyClass.end.toLocal().format(data.l10n, FormatMode.hmm)}",
style: appStyle.fonts.B_16R.apply(
color: appStyle.colors.textPrimary,
),
),
],
),
);
if (data.settings
.group("settings")
.subGroup("timetable_toast")
.boolean("breaks") &&
showBreak) {
elements.add(
FirkaCard(
color: appStyle.colors.cardTranslucent,
shadow: false,
left: [
Text(
data.l10n.breakTxt,
style: appStyle.fonts.B_16SB.apply(
color: appStyle.colors.textSecondary,
),
),
],
right: [
Text(
"$postBreak ${postBreak == 1 ? data.l10n.starting_min : data.l10n.starting_min_plural}",
style: appStyle.fonts.B_16R.apply(
color: appStyle.colors.textTertiary,
),
),
],
),
);
}
} else if (data.settings
.group("settings")
.subGroup("timetable_toast")
.boolean("breaks") &&
showBreak) {
elements.add(
FirkaCard(
color: appStyle.colors.cardTranslucent,
shadow: false,
left: [
Text(
data.l10n.breakTxt,
style: appStyle.fonts.B_16SB.apply(
color: appStyle.colors.textSecondary,
),
),
],
right: [
Text(
"$breakMins ${breakMins == 1 ? data.l10n.starting_min : data.l10n.starting_min_plural}",
style: appStyle.fonts.B_16R.apply(
color: appStyle.colors.textTertiary,
),
),
],
),
);
}
} else if (data.settings
.group("settings")
.subGroup("timetable_toast")
.boolean("breaks") &&
showBreak) {
elements.add(
FirkaCard(
color: appStyle.colors.cardTranslucent,
shadow: false,
left: [
Text(
data.l10n.breakTxt,
style: appStyle.fonts.B_16SB.apply(
color: appStyle.colors.textSecondary,
),
),
],
right: [
Text(
"$breakMins ${breakMins == 1 ? data.l10n.starting_min : data.l10n.starting_min_plural}",
style: appStyle.fonts.B_16R.apply(
color: appStyle.colors.textTertiary,
),
),
],
),
);
}
}
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: elements, children: elements,

View File

@@ -43,9 +43,7 @@ class LessonSmallWidget extends StatelessWidget {
size: 20, size: 20,
uid: lesson.uid, uid: lesson.uid,
className: lesson.name, className: lesson.name,
category: lesson.subject?.name != null category: lesson.subject?.name.firstUpper() ?? '',
? lesson.subject!.name.firstUpper()
: '',
), ),
SizedBox(width: 8), SizedBox(width: 8),
Text( Text(

View File

@@ -1,3 +1,4 @@
import 'package:firka/core/settings.dart';
import 'package:kreta_api/kreta_api.dart'; import 'package:kreta_api/kreta_api.dart';
import 'package:firka/core/extensions.dart'; import 'package:firka/core/extensions.dart';
import 'package:firka/ui/components/firka_card.dart'; import 'package:firka/ui/components/firka_card.dart';
@@ -5,6 +6,7 @@ import 'package:firka/app/app_state.dart';
import 'package:firka/ui/theme/style.dart'; import 'package:firka/ui/theme/style.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:majesticons_flutter/majesticons_flutter.dart';
import 'lesson.dart'; import 'lesson.dart';
@@ -30,11 +32,10 @@ class TimeTableDayWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget noLessonsWidget = SizedBox(); Widget ttBody;
List<Widget> ttBody = List.empty(growable: true);
if (lessons.isEmpty) { if (lessons.isEmpty) {
noLessonsWidget = Column( ttBody = Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
@@ -70,9 +71,10 @@ class TimeTableDayWidget extends StatelessWidget {
], ],
); );
} else { } else {
List<Widget> ttLessons = List.empty(growable: true);
for (var i = 0; i < events.length; i++) { for (var i = 0; i < events.length; i++) {
var event = events[i]; var event = events[i];
ttBody.add( ttLessons.add(
FirkaCard( FirkaCard(
left: [ left: [
Text( Text(
@@ -85,43 +87,71 @@ class TimeTableDayWidget extends StatelessWidget {
), ),
); );
} }
var showBreak = data.settings
.group("settings")
.subGroup("timetable_toast")
.boolean("breaks");
for (var i = 0; i < lessons.length; i++) { for (var i = 0; i < lessons.length; i++) {
var lesson = lessons[i]; var lesson = lessons[i];
Lesson? nextLesson = lessons.length > i + 1 ? lessons[i + 1] : null; var nextLesson = lessons.length > i + 1 ? lessons[i + 1] : null;
ttBody.add( ttLessons.add(
LessonWidget( LessonWidget(
data, data,
week,
day,
lessons.getLessonNo(lesson), lessons.getLessonNo(lesson),
lesson, lesson,
tests.firstWhereOrNull( tests.firstWhereOrNull(
(test) => test.lessonNumber == lesson.lessonNumber, (test) => test.lessonNumber == lesson.lessonNumber,
), ),
nextLesson, ),
);
if (!showBreak || nextLesson == null) {
continue;
}
var breakMins = nextLesson.start.difference(lesson.end).inMinutes;
ttLessons.add(
FirkaCard(
color: appStyle.colors.cardTranslucent,
margin: EdgeInsets.all(0),
padding: EdgeInsets.symmetric(vertical: 11, horizontal: 16),
shadow: false,
left: [
Text(
initData.l10n.breakTxt,
style: appStyle.fonts.B_14SB.copyWith(
color: appStyle.colors.textSecondary,
),
),
],
right: [
Text(
"$breakMins ${breakMins > 1 ? initData.l10n.starting_min_plural : initData.l10n.starting_min}",
style: appStyle.fonts.B_14R.copyWith(
color: appStyle.colors.textSecondary,
),
),
],
), ),
); );
} }
ttBody = Padding(
padding: const EdgeInsets.only(top: 70 + 16 + 20, left: 20, right: 20),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 16,
children: [...ttLessons, SizedBox(height: 55)],
),
),
);
} }
return SizedBox( return ttBody;
width: MediaQuery.of(context).size.width / 1.1,
child: ttBody.isEmpty
? noLessonsWidget
: Padding(
padding: const EdgeInsets.only(
top: 70 + 16 + 20,
left: 4,
right: 4,
),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [...ttBody, SizedBox(height: 24)],
),
),
),
);
} }
} }