tt: add animations to switching between pages

closes #45
This commit is contained in:
2025-09-10 20:26:29 +02:00
parent e53df719c7
commit 359129c0dd

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:firka/helpers/api/model/test.dart';
import 'package:firka/helpers/api/model/timetable.dart';
@@ -34,7 +36,8 @@ class HomeTimetableScreen extends StatefulWidget {
State<HomeTimetableScreen> createState() => _HomeTimetableScreen();
}
class _HomeTimetableScreen extends FirkaState<HomeTimetableScreen> {
class _HomeTimetableScreen extends FirkaState<HomeTimetableScreen>
with TickerProviderStateMixin {
List<Lesson>? lessons;
List<Lesson>? events;
List<Test>? tests;
@@ -43,8 +46,28 @@ class _HomeTimetableScreen extends FirkaState<HomeTimetableScreen> {
int active = 0;
final CarouselSliderController _controller = CarouselSliderController();
AnimationController? _cardAnimationController;
Animation<Offset>? _cardOffsetAnimation;
bool _showAnimatedCard = false;
_HomeTimetableScreen();
@override
void initState() {
super.initState();
widget.updateNotifier.addListener(updateListener);
widget.data.settingsUpdateNotifier.addListener(settingsUpdateListener);
now = timeNow();
initForWeek(now!);
_cardAnimationController = AnimationController(
duration: Duration(milliseconds: 300),
vsync: this,
);
}
void setActiveToToday() {
final todayMid = now!.getMidnight();
int idx = dates!.indexWhere((d) =>
@@ -132,13 +155,13 @@ class _HomeTimetableScreen extends FirkaState<HomeTimetableScreen> {
void updateListener() async {
if (now != null) {
await initForWeek(now!, forceCache: false);
setState(() {});
if (mounted) setState(() {});
}
widget.finishNotifier.update();
}
void settingsUpdateListener() {
setState(() {});
if (mounted) setState(() {});
}
@override
@@ -152,23 +175,51 @@ class _HomeTimetableScreen extends FirkaState<HomeTimetableScreen> {
widget.data.settingsUpdateNotifier.addListener(settingsUpdateListener);
}
@override
void initState() {
super.initState();
bool animating = false;
widget.updateNotifier.addListener(updateListener);
widget.data.settingsUpdateNotifier.addListener(settingsUpdateListener);
void _handleNavTap(int oldIndex, int targetIndex) async {
if (animating) return;
now = timeNow();
initForWeek(now!);
}
active = -1;
@override
void dispose() {
super.dispose();
const double cardWidth = 40.0;
const double spacing = 8.0;
final double totalCardWidth = cardWidth + spacing;
widget.updateNotifier.removeListener(updateListener);
widget.data.settingsUpdateNotifier.removeListener(settingsUpdateListener);
final double start = oldIndex * totalCardWidth;
final double end = targetIndex * totalCardWidth;
_cardAnimationController!.reset();
_cardOffsetAnimation = Tween<Offset>(
begin: Offset(start, 0),
end: Offset(end, 0),
).animate(CurvedAnimation(
parent: _cardAnimationController!,
curve: Curves.easeInOut,
));
setState(() {
_showAnimatedCard = true;
});
_cardAnimationController!.forward();
animating = true;
await _controller.animateToPage(
targetIndex,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
animating = false;
if (!mounted) return;
setState(() {
active = targetIndex;
_showAnimatedCard = false;
});
_cardAnimationController!.reset();
}
@override
@@ -197,14 +248,10 @@ class _HomeTimetableScreen extends FirkaState<HomeTimetableScreen> {
.toList();
if (testsOnDate.isNotEmpty) {
debugPrint(testsOnDate.toString());
ttWidgets.add(Stack(
children: [
BottomTimeTableNavIconWidget(widget.data.l10n, () {
setState(() {
_controller.jumpToPage(i);
active = i;
});
_handleNavTap(active, i);
}, active == i, date),
Transform.translate(
offset: Offset(38, -10),
@@ -214,10 +261,7 @@ class _HomeTimetableScreen extends FirkaState<HomeTimetableScreen> {
));
} else {
ttWidgets.add(BottomTimeTableNavIconWidget(widget.data.l10n, () {
setState(() {
_controller.jumpToPage(i);
active = i;
});
_handleNavTap(active, i);
}, active == i, date));
}
@@ -225,6 +269,45 @@ class _HomeTimetableScreen extends FirkaState<HomeTimetableScreen> {
lessonsOnDate, eventsOnDate, testsOnDate));
}
List<Widget> ttEmptyCards = List.empty(growable: true);
if (animating || _showAnimatedCard) {
for (var i = 0; i < ttDays.length; i++) {
if (i == 0) {
Widget cardWidget = Card(
color: appStyle.colors.buttonSecondaryFill,
shadowColor: Colors.transparent,
child: SizedBox(width: 40, height: 54),
);
if (_showAnimatedCard && _cardOffsetAnimation != null) {
ttEmptyCards.add(AnimatedBuilder(
animation: _cardOffsetAnimation!,
builder: (context, child) {
return Transform.translate(
offset: _cardOffsetAnimation!.value,
child: cardWidget,
);
},
));
} else {
ttEmptyCards.add(Transform.translate(
offset: Offset(0, 0),
child: cardWidget,
));
}
} else {
ttEmptyCards.add(Card(
color: Colors.transparent,
shadowColor: Colors.transparent,
child: SizedBox(width: 40, height: 54),
));
}
}
} else {
ttEmptyCards.clear();
}
return Scaffold(
backgroundColor: appStyle.colors.background,
body: Stack(children: [
@@ -322,6 +405,7 @@ class _HomeTimetableScreen extends FirkaState<HomeTimetableScreen> {
),
onTap: () async {
var newNow = now!.subtract(Duration(days: 7));
if (!mounted) return;
setState(() {
now = newNow;
lessons = null;
@@ -375,6 +459,7 @@ class _HomeTimetableScreen extends FirkaState<HomeTimetableScreen> {
now = timeNow();
}
await initForWeek(newNow);
if (!mounted) return;
setState(() {
now = newNow;
});
@@ -401,16 +486,25 @@ class _HomeTimetableScreen extends FirkaState<HomeTimetableScreen> {
enableInfiniteScroll: false,
initialPage: active,
onPageChanged: (i, _) {
if (animating || !mounted) return;
setState(() {
active = i;
});
}),
))),
TransparentPointer(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: ttWidgets,
)),
Stack(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: ttEmptyCards,
),
TransparentPointer(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: ttWidgets,
)),
],
),
],
)
]));
@@ -429,4 +523,12 @@ class _HomeTimetableScreen extends FirkaState<HomeTimetableScreen> {
);
}
}
@override
void dispose() {
super.dispose();
widget.updateNotifier.removeListener(updateListener);
widget.data.settingsUpdateNotifier.removeListener(settingsUpdateListener);
}
}