ref: lesson widget

This commit is contained in:
checkedear
2026-04-29 21:21:36 +02:00
parent 1807d96895
commit 613b66bc22
3 changed files with 215 additions and 578 deletions

View File

@@ -307,13 +307,12 @@ class _HomeMainScreen extends FirkaState<HomeMainScreen> {
...lessonTestMap.entries.map(
(entry) => Padding(
padding: EdgeInsets.symmetric(horizontal: 5),
child: LessonWidget(
child: LessonBigWidget(
widget.data,
lessons!.getLessonNo(entry.key),
entry.key,
entry.value,
active: currentLesson == entry.key,
expanded: true,
),
),
),

View File

@@ -21,7 +21,6 @@ class LessonWidget extends StatelessWidget {
final AppInitialization data;
final int? lessonNo;
final Lesson lesson;
final bool expanded;
final bool active;
final Test? test;
@@ -31,7 +30,6 @@ class LessonWidget extends StatelessWidget {
this.lesson,
this.test, {
this.active = false,
this.expanded = false,
super.key,
});
@@ -78,8 +76,6 @@ class LessonWidget extends StatelessWidget {
var roomName = lesson.roomName ?? 'N/A';
final spacing = expanded ? 8.0 : 12.0;
elements.add(
GestureDetector(
onTap: () {
@@ -96,16 +92,14 @@ class LessonWidget extends StatelessWidget {
);
},
child: FirkaCard.single(
height: expanded ? 104 : 64,
height: 64,
borderColor: active ? appStyle.colors.accent : null,
margin: EdgeInsets.all(0),
padding: EdgeInsets.only(left: expanded ? 16 : 14, right: 16),
padding: EdgeInsets.only(left: 14, right: 16),
color: isDismissed
? appStyle.colors.cardTranslucent
: appStyle.colors.card,
attached: !expanded && showTests && test != null
? Attach.bottom
: Attach.none,
attached: showTests && test != null ? Attach.bottom : Attach.none,
shadow: !isDismissed,
child: Column(
spacing: 12,
@@ -138,19 +132,19 @@ class LessonWidget extends StatelessWidget {
),
),
FilledCircle(
diameter: expanded ? 32 : 36,
diameter: 36,
color: bgColor,
child: ClassIconWidget(
uid: lesson.uid,
className: lesson.name,
category: subjectName,
color: accent,
size: expanded ? 20 : 24,
size: 24,
),
),
SizedOverflowBox(
size: Size(spacing, 0),
child: !expanded && !showTests && test != null
size: Size(12, 0),
child: !showTests && test != null
? Transform.translate(
offset: Offset(4, -20),
child: BubbleTest(),
@@ -160,7 +154,7 @@ class LessonWidget extends StatelessWidget {
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
spacing: spacing,
spacing: 12,
children: [
LimitedBox(
maxWidth: 155,
@@ -227,92 +221,23 @@ class LessonWidget extends StatelessWidget {
color: appStyle.colors.textPrimary,
),
),
if (!expanded)
Text(
lesson.end.toLocal().format(
data.l10n,
FormatMode.hmm,
),
style: appStyle.fonts.B_14R.apply(
color: appStyle.colors.textPrimary,
),
),
],
),
],
),
if (expanded && test == null)
Column(
spacing: 4,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
lesson.end
.difference(timeNow().max(lesson.start))
.timeLeft(initData.l10n) ??
"",
style: appStyle.fonts.B_14R.apply(
color: appStyle.colors.textSecondary,
),
),
Text(
lesson.end.format(initData.l10n, FormatMode.hmm),
style: appStyle.fonts.B_14R.apply(
color: appStyle.colors.textSecondary,
),
),
],
),
ClipRRect(
borderRadius: BorderRadius.circular(16),
child: LinearProgressIndicator(
value:
timeNow().difference(lesson.start).inMilliseconds /
lesson.end.difference(lesson.start).inMilliseconds,
backgroundColor: appStyle.colors.a15p,
color: appStyle.colors.accent,
minHeight: 8,
),
),
],
),
if (expanded && test != null)
Container(
height: 28,
padding: EdgeInsets.symmetric(horizontal: 6),
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
color: appStyle.colors.background,
),
child: Row(
children: [
FirkaIconWidget(
FirkaIconType.majesticons,
Majesticon.editPen4Solid,
size: 12,
color: appStyle.colors.accent,
),
SizedBox(width: 8),
Text(
test!.theme,
style: appStyle.fonts.B_16R.apply(
lesson.end.toLocal().format(data.l10n, FormatMode.hmm),
style: appStyle.fonts.B_14R.apply(
color: appStyle.colors.textPrimary,
),
),
],
),
),
],
),
],
),
),
),
);
if (!expanded && test != null && showTests) {
if (test != null && showTests) {
var theme = test!.theme.firstUpper();
var method = test!.method.description.firstUpper();

View File

@@ -1,554 +1,267 @@
import 'package:kreta_api/kreta_api.dart';
import 'package:firka/core/extensions.dart';
import 'package:firka/core/settings.dart';
import 'package:firka/ui/components/firka_card.dart';
import 'package:firka/l10n/app_localizations.dart';
import 'package:firka/app/app_state.dart';
import 'package:firka/ui/theme/style.dart';
import 'package:firka/ui/shared/firka_icon.dart';
import 'package:firka_common/ui/components/filled_circle.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:majesticons_flutter/majesticons_flutter.dart';
import 'package:kreta_api/kreta_api.dart';
import 'package:firka/core/debug_helper.dart';
import 'package:firka/ui/components/common_bottom_sheets.dart';
import 'package:firka/ui/shared/class_icon.dart';
import 'package:firka/ui/shared/firka_icon.dart';
import 'bubble_test.dart';
class LessonBigWidget extends StatelessWidget {
final AppLocalizations l10n;
final DateTime now;
final int? lessonNo;
final Lesson? lesson;
final Lesson? prevLesson;
final Lesson? nextLesson;
final List<Lesson> lessons;
final List<Test> tests;
final AppInitialization data;
final int lessonNo;
final Lesson lesson;
final Test? test;
final bool active;
const LessonBigWidget(
this.l10n,
this.now,
this.data,
this.lessonNo,
this.lesson,
this.prevLesson,
this.nextLesson,
this.lessons,
this.tests, {
this.test, {
this.active = false,
super.key,
});
@override
Widget build(BuildContext context) {
var hasLesson = lesson != null;
var lessonsLeft = lessons.where((lesson) => lesson.end.isAfter(now)).length;
var hasPrevLesson = prevLesson != null;
var hasNextLesson = nextLesson != null;
// TODO: holnapi órák száma kiszámolás
var lessonsTomorrow = 0;
final isSubstituted = lesson.substituteTeacher != null;
final isDismissed = lesson.type.name == "UresOra";
var testsTomorrow = tests
.where(
(test) =>
test.date.isAfter(now) &&
test.date.isBefore(DateTime(now.year, now.month, now.day + 2)),
)
.length;
var accent = appStyle.colors.accent;
var secondary = appStyle.colors.secondary;
var bgColor = appStyle.colors.a15p;
if (lessonsLeft < 1) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
FirkaCard(
left: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
if (isSubstituted) {
accent = appStyle.colors.warningAccent;
secondary = appStyle.colors.warningText;
bgColor = appStyle.colors.warning15p;
}
if (isDismissed) {
accent = appStyle.colors.errorAccent;
secondary = appStyle.colors.errorText;
bgColor = appStyle.colors.error15p;
}
var subjectName = lesson.subject?.name.firstUpper() ?? 'N/A';
var roomName = lesson.roomName ?? 'N/A';
Widget extra = test == null
? Column(
spacing: 4,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
SizedBox(
width: 40,
height: 40,
child: Stack(
children: [
Card(
shadowColor: Colors.transparent,
color: appStyle.colors.a15p,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
child: Padding(
padding: EdgeInsets.all(6),
child: FirkaIconWidget(
FirkaIconType.majesticons,
Majesticon.moonSolid,
size: 32.0,
color: appStyle.colors.accent,
),
),
),
],
),
),
Text(
testsTomorrow == 0
? l10n.tt_no_classes_l2
: l10n.get_ready,
style: appStyle.fonts.B_16R.apply(
color: appStyle.colors.textPrimary,
),
),
],
Text(
(timeNow().isAfter(lesson.start)
? lesson.end
.difference(timeNow())
.timeLeft(initData.l10n)
: null) ??
lesson.start.format(data.l10n, FormatMode.hmm),
style: appStyle.fonts.B_14R.apply(
color: appStyle.colors.textSecondary,
),
),
Text(
lesson.end.format(initData.l10n, FormatMode.hmm),
style: appStyle.fonts.B_14R.apply(
color: appStyle.colors.textSecondary,
),
),
],
),
ClipRRect(
borderRadius: BorderRadius.circular(16),
child: LinearProgressIndicator(
value:
timeNow().difference(lesson.start).inMilliseconds /
lesson.end.difference(lesson.start).inMilliseconds,
backgroundColor: appStyle.colors.a15p,
color: appStyle.colors.accent,
minHeight: 8,
),
),
],
extra: Column(
)
: Container(
height: 28,
padding: EdgeInsets.symmetric(horizontal: 6),
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
color: appStyle.colors.background,
),
child: Row(
children: [
SizedBox(height: 4),
ClipRRect(
borderRadius: BorderRadius.circular(360),
child: Container(
width: double.infinity,
color: appStyle.colors.background,
padding: EdgeInsets.symmetric(vertical: 6, horizontal: 12),
child: Row(
children: [
SizedBox(
width: 20,
height: 20,
child: Stack(
children: [
Padding(
padding: EdgeInsets.all(2),
child: FirkaIconWidget(
FirkaIconType.majesticons,
Majesticon.editPen4Solid,
size: 32.0,
color: appStyle.colors.accent,
),
),
],
),
),
SizedBox(width: 8),
Text(
(lessonsTomorrow == 0 && testsTomorrow == 0)
? l10n.no_tests_tomorrow
: (testsTomorrow > 1)
? l10n.tests_tomorrow(testsTomorrow.toString())
: (testsTomorrow < 1 && lessonsTomorrow > 0)
? l10n.lessons_tomorrow(
lessonsTomorrow.toString(),
)
: l10n.tests_tomorrow(testsTomorrow.toString()),
textAlign: TextAlign.left,
style: appStyle.fonts.B_16R.apply(
color: appStyle.colors.textPrimary,
),
),
],
),
FirkaIconWidget(
FirkaIconType.majesticons,
Majesticon.editPen4Solid,
size: 12,
color: appStyle.colors.accent,
),
SizedBox(width: 8),
Text(
test!.theme,
style: appStyle.fonts.B_16R.apply(
color: appStyle.colors.textPrimary,
),
overflow: TextOverflow.ellipsis,
),
],
),
),
],
);
}
if (!hasLesson && (!hasPrevLesson || !hasNextLesson)) {
if (!hasPrevLesson && !hasNextLesson) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
FirkaCard(
left: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Card(
shadowColor: Colors.transparent,
color: appStyle.colors.a15p,
child: Padding(
padding: EdgeInsets.all(4),
child: FirkaIconWidget(
FirkaIconType.majesticons,
'cupFilled',
color: appStyle.colors.accent,
size: 24,
),
),
),
Text(
l10n.breakTxt,
style: appStyle.fonts.B_16SB.apply(
color: appStyle.colors.textPrimary,
),
),
],
),
],
),
],
right: [
Column(
children: [
Row(
children: [
Text(
'-',
style: appStyle.fonts.B_16R.apply(
color: appStyle.colors.textPrimary,
),
),
],
),
Row(
children: [
Text(
'-',
style: appStyle.fonts.B_16R.apply(
color: appStyle.colors.textPrimary,
),
),
],
),
],
),
],
extra: SizedBox.shrink(),
),
],
);
return GestureDetector(
onTap: () {
showLessonBottomSheet(
context,
data,
lesson,
lessonNo,
accent,
secondary,
bgColor,
test,
);
}
// Before the first lesson: prev missing but next present. Show countdown
// to the next lesson using nextLesson data.
if (!hasPrevLesson && hasNextLesson) {
var timeLeft = nextLesson!.start.difference(now);
var timeLeftStr = l10n.timeLeft(timeLeft.inMinutes + 1);
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
},
child: FirkaCard.single(
height: 104,
borderColor: active ? appStyle.colors.accent : null,
margin: EdgeInsets.all(0),
padding: EdgeInsets.only(left: 16, right: 16),
color: isDismissed
? appStyle.colors.cardTranslucent
: appStyle.colors.card,
shadow: !isDismissed,
child: Column(
spacing: 12,
mainAxisAlignment: MainAxisAlignment.center,
children: [
FirkaCard(
left: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Card(
shadowColor: Colors.transparent,
color: appStyle.colors.a15p,
child: Padding(
padding: EdgeInsets.all(4),
child: FirkaIconWidget(
FirkaIconType.majesticonsLocal,
'cupFilled',
color: appStyle.colors.accent,
size: 24,
),
),
),
Text(
l10n.breakTxt,
style: appStyle.fonts.B_16SB.apply(
color: appStyle.colors.textPrimary,
),
),
],
),
Row(
children: [
Text(
timeLeftStr,
style: appStyle.fonts.B_12R.apply(
color: appStyle.colors.textSecondary,
),
),
],
),
],
),
],
right: [
Column(
children: [
Row(
children: [
Text(
'-',
style: appStyle.fonts.B_16R.apply(
color: appStyle.colors.textPrimary,
),
),
],
),
Row(
children: [
Text(
nextLesson!.start.toLocal().format(
l10n,
FormatMode.hmm,
),
style: appStyle.fonts.B_16R.apply(
color: appStyle.colors.textPrimary,
),
),
],
),
],
),
],
extra: SizedBox.shrink(),
),
],
);
}
// After the last lesson: next missing but prev present. Show a simple
// "no more lessons" style card with the previous lesson end time.
if (hasPrevLesson && !hasNextLesson) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// TODO: implement home/today afternoon
],
);
}
}
if (hasLesson) {
var timeLeft = lesson!.end.difference(now);
var duration = lesson!.end.difference(lesson!.start).inMilliseconds;
var progress = now.difference(lesson!.start).inMilliseconds;
var minsLeft = timeLeft.inMinutes;
var secsLeft = timeLeft.inSeconds;
var timeLeftStr =
"$minsLeft ${minsLeft == 1 ? l10n.starting_min : l10n.starting_min_plural}";
if (minsLeft < 1) {
timeLeftStr =
"$secsLeft ${secsLeft == 1 ? l10n.starting_sec : l10n.starting_sec_plural}";
}
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
FirkaCard(
left: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
Row(
children: [
SizedBox(
width: 18,
height: 18,
child: Stack(
alignment: Alignment.center,
children: [
SizedBox(
SvgPicture.asset(
"assets/icons/subtract.svg",
color: bgColor,
width: 18,
height: 18,
child: Stack(
),
Text(
lessonNo.toString(),
style: appStyle.fonts.B_12R.apply(color: secondary),
textAlign: TextAlign.center,
),
],
),
),
FilledCircle(
diameter: 32,
color: bgColor,
child: ClassIconWidget(
uid: lesson.uid,
className: lesson.name,
category: subjectName,
color: accent,
size: 20,
),
),
SizedBox(width: 8),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
spacing: 8,
children: [
LimitedBox(
maxWidth: 155,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
"assets/icons/subtract.svg",
color: appStyle.colors.a15p,
width: 18,
height: 18,
),
Padding(
padding: EdgeInsets.only(left: 5),
child: Text(
lessonNo.toString(),
style: appStyle.fonts.B_12R.apply(
color: appStyle.colors.secondary,
),
Text(
subjectName,
style: appStyle.fonts.B_16SB.apply(
color: !isDismissed
? appStyle.colors.textPrimary
: appStyle.colors.textSecondary,
),
overflow: TextOverflow.ellipsis,
),
if (isSubstituted)
Text(
lesson.substituteTeacher!,
style: appStyle.fonts.B_14R.apply(
color: appStyle.colors.textSecondary,
),
overflow: TextOverflow.ellipsis,
),
],
),
),
Transform.translate(
offset: Offset(-4, 0),
Flexible(
fit: FlexFit.loose,
child: Card(
shadowColor: Colors.transparent,
color: appStyle.colors.a15p,
margin: EdgeInsets.all(0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
borderRadius: BorderRadius.circular(10),
),
child: Padding(
padding: EdgeInsets.all(4),
child: ClassIconWidget(
color: appStyle.colors.accent,
size: 24,
uid: lesson!.uid,
className: lesson!.name,
category: lesson!.subject?.name ?? '',
),
),
),
),
Text(
lesson!.subject?.name ?? 'N/A',
style: appStyle.fonts.B_16SB.apply(
color: appStyle.colors.textPrimary,
),
),
],
),
Row(
children: [
Text(
timeLeftStr,
style: appStyle.fonts.B_12R.apply(
color: appStyle.colors.textSecondary,
),
),
],
),
],
),
],
right: [
Column(
children: [
Row(
children: [
Text(
lesson!.start.toLocal().format(l10n, FormatMode.hmm),
style: appStyle.fonts.B_16R.apply(
color: appStyle.colors.textPrimary,
),
),
Card(
shadowColor: Colors.transparent,
color: appStyle.colors.a15p,
child: Padding(
padding: EdgeInsets.all(4),
child: Text(
lesson!.roomName ?? '?',
style: appStyle.fonts.B_12R.apply(
color: appStyle.colors.secondary,
padding: EdgeInsets.symmetric(horizontal: 6),
child: Text(
roomName,
style: appStyle.fonts.B_12R.apply(
color: appStyle.colors.textSecondary,
),
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
),
),
),
),
],
),
Row(
children: [
SizedBox(width: 18),
Text(
lesson!.end.toLocal().format(l10n, FormatMode.hmm),
style: appStyle.fonts.B_12R.apply(
color: appStyle.colors.textSecondary,
),
),
],
),
],
),
],
extra: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: LinearProgressIndicator(
value: progress / duration,
backgroundColor: appStyle.colors.a15p,
color: appStyle.colors.accent,
minHeight: 8,
),
),
),
],
);
} else {
var duration = nextLesson!.start
.difference(prevLesson!.end)
.inMilliseconds;
var progress =
duration - nextLesson!.start.difference(now).inMilliseconds;
var timeLeft = nextLesson!.start.difference(now);
var timeLeftStr = l10n.timeLeft(timeLeft.inMinutes + 1);
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
FirkaCard(
left: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Card(
shadowColor: Colors.transparent,
color: appStyle.colors.a15p,
child: Padding(
padding: EdgeInsets.all(4),
child: FirkaIconWidget(
FirkaIconType.majesticonsLocal,
'cupFilled',
color: appStyle.colors.accent,
size: 24,
),
),
),
Text(
l10n.breakTxt,
style: appStyle.fonts.B_16SB.apply(
color: appStyle.colors.textPrimary,
),
),
],
),
Row(
),
if (test != null) SizedBox(width: 8),
if (test != null)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
timeLeftStr,
style: appStyle.fonts.B_12R.apply(
color: appStyle.colors.textSecondary,
),
),
],
),
],
),
],
right: [
Column(
children: [
Row(
children: [
Text(
prevLesson!.end.toLocal().format(l10n, FormatMode.hmm),
style: appStyle.fonts.B_16R.apply(
color: appStyle.colors.textPrimary,
),
),
],
),
Row(
children: [
Text(
nextLesson!.start.toLocal().format(
l10n,
lesson.start.toLocal().format(
data.l10n,
FormatMode.hmm,
),
style: appStyle.fonts.B_16R.apply(
style: appStyle.fonts.B_14R.apply(
color: appStyle.colors.textPrimary,
),
),
],
),
],
),
],
extra: LinearProgressIndicator(
// TODO: Make this rounded
value: progress / duration,
backgroundColor: appStyle.colors.a15p,
color: appStyle.colors.accent,
],
),
),
],
);
}
extra,
],
),
),
);
}
}