diff --git a/firka/lib/helpers/ui/stateless_async_widget.dart b/firka/lib/helpers/ui/stateless_async_widget.dart deleted file mode 100644 index f48818f6..00000000 --- a/firka/lib/helpers/ui/stateless_async_widget.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:firka/ui/widget/delayed_spinner.dart'; -import 'package:flutter/material.dart'; - -abstract class StatelessAsyncWidget extends StatelessWidget { - const StatelessAsyncWidget({super.key}); - - Future buildAsync(BuildContext context); - - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: buildAsync(context), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return Center(child: DelayedSpinnerWidget()); - } else if (snapshot.hasError) { - return Center(child: Text('Error: ${snapshot.error}')); - } else { - return snapshot.data!; - } - }); - } -} diff --git a/firka/lib/ui/phone/pages/home/home_grades.dart b/firka/lib/ui/phone/pages/home/home_grades.dart index 7d77ffc3..a921ce40 100644 --- a/firka/lib/ui/phone/pages/home/home_grades.dart +++ b/firka/lib/ui/phone/pages/home/home_grades.dart @@ -1,167 +1,204 @@ +import 'package:firka/helpers/api/client/kreta_client.dart'; import 'package:firka/helpers/api/model/generic.dart'; import 'package:firka/helpers/ui/firka_card.dart'; import 'package:firka/helpers/ui/grade_helpers.dart'; -import 'package:firka/helpers/ui/stateless_async_widget.dart'; import 'package:firka/ui/phone/screens/home/home_screen.dart'; import 'package:firka/ui/widget/grade_small_card.dart'; import 'package:flutter/material.dart'; +import '../../../../helpers/api/model/grade.dart'; import '../../../../helpers/api/model/subject.dart'; +import '../../../../helpers/api/model/timetable.dart'; import '../../../../helpers/debug_helper.dart'; import '../../../../main.dart'; import '../../../model/style.dart'; +import '../../../widget/delayed_spinner.dart'; -class HomeGradesScreen extends StatelessAsyncWidget { +class HomeGradesScreen extends StatefulWidget { final AppInitialization data; final void Function(ActiveHomePage, bool) cb; const HomeGradesScreen(this.data, this.cb, {super.key}); @override - Future buildAsync(BuildContext context) async { - var now = timeNow(); - var start = now.subtract(Duration(days: now.weekday - 1)); - var end = start.add(Duration(days: 6)); + State createState() => _HomeGradesScreen(); +} - var grades = await data.client.getGrades(); - var subjectAvg = 0.00; - var week = await data.client.getTimeTable(start, end); - final List subjects = List.empty(growable: true); - final List gradeCards = []; +class _HomeGradesScreen extends State { + ApiResponse>? grades; + ApiResponse>? week; - for (var grade in grades.response!) { - if (subjects.where((s) => s.uid == grade.subject.uid).isEmpty) { - subjects.add(grade.subject); - } - } + @override + void initState() { + super.initState(); - subjects.sort((s1, s2) => s1.name.compareTo(s2.name)); + (() async { + var now = timeNow(); + var start = now.subtract(Duration(days: now.weekday - 1)); + var end = start.add(Duration(days: 6)); - for (var subject in subjects) { - for (var grade in grades.response!) { - if (grade.subject.uid != subject.uid) continue; + grades = await widget.data.client.getGrades(); + week = await widget.data.client.getTimeTable(start, end); - if (grade.valueType.name == "Szazalekos") { - grade.valueType = NameUidDesc( - uid: "1,Osztalyzat", name: "Osztalyzat", description: ""); - if (grade.numericValue != null) { - grade.numericValue = percentageToGrade(grade.numericValue!); - } - } - } - var avg = grades.response!.getAverageBySubject(subject); + if (mounted) setState(() {}); + })(); + } - if (avg.isNaN) { - gradeCards.add(GradeSmallCard(grades.response!, subject)); - } else { - gradeCards.add(GestureDetector( - child: GradeSmallCard(grades.response!, subject), - onTap: () { - cb(ActiveHomePage(HomePages.grades, subPageUid: subject.uid), true); - }, - )); - } - - subjectAvg += roundGrade(avg); - } - - subjectAvg /= subjects.length; - - var subjectAvgColor = getGradeColor(subjectAvg); - - return Flexible( - child: Padding( - padding: const EdgeInsets.only( - left: 16.0, - right: 16.0, - ), + @override + Widget build(BuildContext context) { + if (grades == null || week == null) { + return SizedBox( + height: MediaQuery.of(context).size.height / 1.35, child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - children: [ - Text( - data.l10n.subjects, - style: appStyle.fonts.H_H2 - .apply(color: appStyle.colors.textSecondary), - ) - ], - ), - SizedBox(height: 16), // TODO: Add graphs here - // ...gradeCards, - SizedBox( - height: MediaQuery.of(context).size.height - - MediaQuery.of(context).padding.top - - 230, - child: ListView( - children: [ - Text( - data.l10n.your_subjects, - style: appStyle.fonts.H_14px - .apply(color: appStyle.colors.textSecondary), - ), - SizedBox(height: 16), - ...gradeCards, - SizedBox(height: 16), - Text( - data.l10n.data, - style: appStyle.fonts.B_16SB - .apply(color: appStyle.colors.textSecondary), - ), - SizedBox(height: 16), - FirkaCard( - left: [ - Text( - data.l10n.subject_avg, - style: appStyle.fonts.B_16SB - .apply(color: appStyle.colors.textPrimary), - ), - ], - right: [ - Card( - shadowColor: Colors.transparent, - color: subjectAvgColor.withAlpha(38), - child: Padding( - padding: EdgeInsets.only( - left: 8, right: 8, top: 4, bottom: 4), - child: Text( - subjectAvg.toStringAsFixed(2), - style: appStyle.fonts.B_16SB - .apply(color: subjectAvgColor), - ), - ), - ), - ], - ), - FirkaCard(left: [ - Text( - data.l10n.class_avg, - style: appStyle.fonts.B_16SB - .apply(color: appStyle.colors.textPrimary), - ), - ]), - FirkaCard( - left: [ - Text( - data.l10n.class_n, - style: appStyle.fonts.B_16SB - .apply(color: appStyle.colors.textPrimary), - ), - ], - right: [ - Text( - week.response!.length.toString(), - style: appStyle.fonts.B_14SB - .apply(color: appStyle.colors.textPrimary), - ), - ], - ), - ], - ), - ), + SizedBox(), + DelayedSpinnerWidget(), + SizedBox(), ], ), - ), - ); + ); + } else { + var subjectAvg = 0.00; + final List subjects = List.empty(growable: true); + final List gradeCards = []; + + for (var grade in grades!.response!) { + if (subjects.where((s) => s.uid == grade.subject.uid).isEmpty) { + subjects.add(grade.subject); + } + } + + subjects.sort((s1, s2) => s1.name.compareTo(s2.name)); + + for (var subject in subjects) { + for (var grade in grades!.response!) { + if (grade.subject.uid != subject.uid) continue; + + if (grade.valueType.name == "Szazalekos") { + grade.valueType = NameUidDesc( + uid: "1,Osztalyzat", name: "Osztalyzat", description: ""); + if (grade.numericValue != null) { + grade.numericValue = percentageToGrade(grade.numericValue!); + } + } + } + var avg = grades!.response!.getAverageBySubject(subject); + + if (avg.isNaN) { + gradeCards.add(GradeSmallCard(grades!.response!, subject)); + } else { + gradeCards.add(GestureDetector( + child: GradeSmallCard(grades!.response!, subject), + onTap: () { + widget.cb( + ActiveHomePage(HomePages.grades, subPageUid: subject.uid), + true); + }, + )); + } + + subjectAvg += roundGrade(avg); + } + + subjectAvg /= subjects.length; + + var subjectAvgColor = getGradeColor(subjectAvg); + + return Flexible( + child: Padding( + padding: const EdgeInsets.only( + left: 16.0, + right: 16.0, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + widget.data.l10n.subjects, + style: appStyle.fonts.H_H2 + .apply(color: appStyle.colors.textSecondary), + ) + ], + ), + SizedBox(height: 16), // TODO: Add graphs here + // ...gradeCards, + SizedBox( + height: MediaQuery.of(context).size.height - + MediaQuery.of(context).padding.top - + 230, + child: ListView( + children: [ + Text( + widget.data.l10n.your_subjects, + style: appStyle.fonts.H_14px + .apply(color: appStyle.colors.textSecondary), + ), + SizedBox(height: 16), + ...gradeCards, + SizedBox(height: 16), + Text( + widget.data.l10n.data, + style: appStyle.fonts.B_16SB + .apply(color: appStyle.colors.textSecondary), + ), + SizedBox(height: 16), + FirkaCard( + left: [ + Text( + widget.data.l10n.subject_avg, + style: appStyle.fonts.B_16SB + .apply(color: appStyle.colors.textPrimary), + ), + ], + right: [ + Card( + shadowColor: Colors.transparent, + color: subjectAvgColor.withAlpha(38), + child: Padding( + padding: EdgeInsets.only( + left: 8, right: 8, top: 4, bottom: 4), + child: Text( + subjectAvg.toStringAsFixed(2), + style: appStyle.fonts.B_16SB + .apply(color: subjectAvgColor), + ), + ), + ), + ], + ), + FirkaCard(left: [ + Text( + widget.data.l10n.class_avg, + style: appStyle.fonts.B_16SB + .apply(color: appStyle.colors.textPrimary), + ), + ]), + FirkaCard( + left: [ + Text( + widget.data.l10n.class_n, + style: appStyle.fonts.B_16SB + .apply(color: appStyle.colors.textPrimary), + ), + ], + right: [ + Text( + week!.response!.length.toString(), + style: appStyle.fonts.B_14SB + .apply(color: appStyle.colors.textPrimary), + ), + ], + ), + ], + ), + ), + ], + ), + ), + ); + } } } diff --git a/firka/lib/ui/phone/pages/home/home_grades_subject.dart b/firka/lib/ui/phone/pages/home/home_grades_subject.dart index 1bc9d2e9..8675ed9a 100644 --- a/firka/lib/ui/phone/pages/home/home_grades_subject.dart +++ b/firka/lib/ui/phone/pages/home/home_grades_subject.dart @@ -1,129 +1,161 @@ +import 'package:firka/helpers/api/model/grade.dart'; import 'package:firka/helpers/extensions.dart'; import 'package:firka/helpers/ui/firka_card.dart'; import 'package:firka/helpers/ui/grade.dart'; -import 'package:firka/helpers/ui/stateless_async_widget.dart'; import 'package:flutter/material.dart'; import '../../../../main.dart'; import '../../../model/style.dart'; +import '../../../widget/delayed_spinner.dart'; -class HomeGradesSubjectScreen extends StatelessAsyncWidget { +class HomeGradesSubjectScreen extends StatefulWidget { final AppInitialization data; final String subPageUid; const HomeGradesSubjectScreen(this.data, this.subPageUid, {super.key}); @override - Future buildAsync(BuildContext context) async { - var grades = (await data.client.getGrades()) - .response! - .where((grade) => grade.subject.uid == subPageUid) - .where((grade) => grade.type.name != "felevi_jegy_ertekeles"); - var aGrade = grades.first; - var groups = grades.groupList((grade) => grade.recordDate); + State createState() => _HomeGradesSubjectScreen(); +} - var gradeWidgets = List.empty(growable: true); +class _HomeGradesSubjectScreen extends State { + Iterable? grades; - for (var group in groups.entries) { - gradeWidgets.add(SizedBox( - height: 8, - )); - gradeWidgets.add(Text( - group.key.format(data.l10n, FormatMode.grades), - style: appStyle.fonts.H_14px, - )); - gradeWidgets.add(SizedBox( - height: 8, - )); - for (var grade in group.value) { - gradeWidgets.add(FirkaCard( - left: [ - Row( - children: [ - GradeWidget(grade), - SizedBox(width: 8), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - grade.topic ?? grade.type.description!, - style: appStyle.fonts.B_14SB, - ), - Text( - grade.mode?.description ?? "", - style: appStyle.fonts.B_14R, - ) - ], - ) - ], - ) - ], + @override + void initState() { + super.initState(); + + (() async { + grades = (await widget.data.client.getGrades()) + .response! + .where((grade) => grade.subject.uid == widget.subPageUid) + .where((grade) => grade.type.name != "felevi_jegy_ertekeles"); + + if (mounted) setState(() {}); + })(); + } + + @override + Widget build(BuildContext context) { + if (grades != null) { + var aGrade = grades!.first; + var groups = grades!.groupList((grade) => grade.recordDate); + + var gradeWidgets = List.empty(growable: true); + + for (var group in groups.entries) { + gradeWidgets.add(SizedBox( + height: 8, )); - } - } - - return Flexible( - child: Padding( - padding: const EdgeInsets.only( - left: 16.0, - right: 16.0, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - data.l10n.subjects, - style: appStyle - .fonts.H_16px // TODO: Replace this with the proper font - .apply(color: appStyle.colors.textPrimary), - ) - ], - ), - SizedBox(height: 16), - SizedBox( - height: MediaQuery.of(context).size.height - - MediaQuery.of(context).padding.top - - 230, - child: ListView( + gradeWidgets.add(Text( + group.key.format(widget.data.l10n, FormatMode.grades), + style: appStyle.fonts.H_14px, + )); + gradeWidgets.add(SizedBox( + height: 8, + )); + for (var grade in group.value) { + gradeWidgets.add(FirkaCard( + left: [ + Row( children: [ - FirkaCard( - left: [ - Padding( - padding: EdgeInsets.only(left: 4), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - aGrade.subject.name, - style: appStyle.fonts.H_H2, - ), - Text( - aGrade.teacher, // For some reason the teacher's - // name isn't stored in the subject, so we need - // to get *a* grade, and then get the teacher's - // name from there :3 - style: appStyle.fonts.B_14R, - ) - ], - ), + GradeWidget(grade), + SizedBox(width: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + grade.topic ?? grade.type.description!, + style: appStyle.fonts.B_14SB, + ), + Text( + grade.mode?.description ?? "", + style: appStyle.fonts.B_14R, ) ], - ), - Padding( - padding: EdgeInsets.only(left: 4), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: gradeWidgets, - ), + ) + ], + ) + ], + )); + } + } + + return Flexible( + child: Padding( + padding: const EdgeInsets.only( + left: 16.0, + right: 16.0, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + widget.data.l10n.subjects, + style: appStyle + .fonts.H_16px // TODO: Replace this with the proper font + .apply(color: appStyle.colors.textPrimary), ) ], ), - ), + SizedBox(height: 16), + SizedBox( + height: MediaQuery.of(context).size.height - + MediaQuery.of(context).padding.top - + 230, + child: ListView( + children: [ + FirkaCard( + left: [ + Padding( + padding: EdgeInsets.only(left: 4), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + aGrade.subject.name, + style: appStyle.fonts.H_H2, + ), + Text( + aGrade.teacher, // For some reason the teacher's + // name isn't stored in the subject, so we need + // to get *a* grade, and then get the teacher's + // name from there :3 + style: appStyle.fonts.B_14R, + ) + ], + ), + ) + ], + ), + Padding( + padding: EdgeInsets.only(left: 4), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: gradeWidgets, + ), + ) + ], + ), + ), + ], + ), + ), + ); + } else { + return SizedBox( + height: MediaQuery.of(context).size.height / 1.35, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox(), + DelayedSpinnerWidget(), + SizedBox(), ], ), - ), - ); + ); + } } } diff --git a/firka/lib/ui/phone/screens/home/home_screen.dart b/firka/lib/ui/phone/screens/home/home_screen.dart index 05a21717..ab38dd71 100644 --- a/firka/lib/ui/phone/screens/home/home_screen.dart +++ b/firka/lib/ui/phone/screens/home/home_screen.dart @@ -18,6 +18,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:home_widget/home_widget.dart'; import 'package:majesticons_flutter/majesticons_flutter.dart'; +import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart'; import '../../../../helpers/db/widget.dart'; import '../../../../helpers/debug_helper.dart'; @@ -70,6 +71,8 @@ class _HomeScreenState extends State { Widget? toast; bool pairingDone = false; bool _disposed = false; + final RefreshController _refreshController = + RefreshController(initialRefresh: false); ActiveToastType activeToast = ActiveToastType.none; @@ -262,6 +265,17 @@ class _HomeScreenState extends State { )); } + void _onRefresh() async { + await Future.delayed(Duration(milliseconds: 1000)); + + _refreshController.refreshCompleted(); + } + + void _onLoading() async { + if (mounted) setState(() {}); + _refreshController.loadComplete(); + } + @override Widget build(BuildContext context) { _updateSystemUI(); // Update system UI on every build, to compensate for the android system being dumb @@ -327,138 +341,149 @@ class _HomeScreenState extends State { } SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - return PopScope( - canPop: canPop, - child: Scaffold( - backgroundColor: appStyle.colors.background, - body: SafeArea( - child: SizedBox( - height: MediaQuery.of(context).size.height, - child: Stack( - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return SmartRefresher( + controller: _refreshController, + onLoading: _onLoading, + onRefresh: _onRefresh, + header: MaterialClassicHeader( + color: appStyle.colors.accent, + backgroundColor: appStyle.colors.background, + offset: 24, + ), + child: PopScope( + canPop: canPop, + child: Scaffold( + backgroundColor: appStyle.colors.background, + body: SafeArea( + child: SizedBox( + height: MediaQuery.of(context).size.height, + child: Stack( children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [HomeSubPage(page, setPageCB, widget.data)], - ), - Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.bottomCenter, - end: Alignment.topCenter, - colors: [ - appStyle.colors.background, - appStyle.colors.background.withValues(alpha: 0.0), - ], - stops: const [0.0, 1.0], + Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [HomeSubPage(page, setPageCB, widget.data)], ), - ), - width: MediaQuery.of(context).size.width, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 55, vertical: 15), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - // Home Button - BottomNavIconWidget(() { - if (page.page != HomePages.home) { - HapticFeedback.lightImpact(); - - setState(() { - previousPages.add(page); - canPop = false; - page = ActiveHomePage(HomePages.home); - }); - } - }, - page.page == HomePages.home, - page.page == HomePages.home - ? Majesticon.homeSolid - : Majesticon.homeLine, - widget.data.l10n.home, - page.page == HomePages.home - ? appStyle.colors.accent - : appStyle.colors.secondary, - appStyle.colors.textPrimary), - // Grades Button - BottomNavIconWidget(() { - if (page.page != HomePages.grades) { - HapticFeedback.lightImpact(); - - setState(() { - previousPages.add(page); - canPop = false; - page = ActiveHomePage(HomePages.grades); - }); - } - }, - page.page == HomePages.grades, - page.page == HomePages.grades - ? Majesticon.bookmarkSolid - : Majesticon.bookmarkLine, - widget.data.l10n.grades, - page.page == HomePages.grades - ? appStyle.colors.accent - : appStyle.colors.secondary, - appStyle.colors.textPrimary), - // Timetable Button - BottomNavIconWidget(() { - if (page.page != HomePages.timetable) { - HapticFeedback.lightImpact(); - - setState(() { - previousPages.add(page); - canPop = false; - page = ActiveHomePage(HomePages.timetable); - }); - } - }, - page.page == HomePages.timetable, - page.page == HomePages.timetable - ? Majesticon.calendarSolid - : Majesticon.calendarLine, - widget.data.l10n.timetable, - page.page == HomePages.timetable - ? appStyle.colors.accent - : appStyle.colors.secondary, - appStyle.colors.textPrimary), - // More Button - BottomNavIconWidget( - () { - HapticFeedback.lightImpact(); - showExtrasBottomSheet(context, widget.data); - }, - false, - Majesticon.globeEarthLine, - widget.data.l10n.other, - appStyle.colors.secondary, - appStyle.colors.textPrimary, + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + appStyle.colors.background, + appStyle.colors.background + .withValues(alpha: 0.0), + ], + stops: const [0.0, 1.0], ), - ], + ), + width: MediaQuery.of(context).size.width, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 55, vertical: 15), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // Home Button + BottomNavIconWidget(() { + if (page.page != HomePages.home) { + HapticFeedback.lightImpact(); + + setState(() { + previousPages.add(page); + canPop = false; + page = ActiveHomePage(HomePages.home); + }); + } + }, + page.page == HomePages.home, + page.page == HomePages.home + ? Majesticon.homeSolid + : Majesticon.homeLine, + widget.data.l10n.home, + page.page == HomePages.home + ? appStyle.colors.accent + : appStyle.colors.secondary, + appStyle.colors.textPrimary), + // Grades Button + BottomNavIconWidget(() { + if (page.page != HomePages.grades) { + HapticFeedback.lightImpact(); + + setState(() { + previousPages.add(page); + canPop = false; + page = ActiveHomePage(HomePages.grades); + }); + } + }, + page.page == HomePages.grades, + page.page == HomePages.grades + ? Majesticon.bookmarkSolid + : Majesticon.bookmarkLine, + widget.data.l10n.grades, + page.page == HomePages.grades + ? appStyle.colors.accent + : appStyle.colors.secondary, + appStyle.colors.textPrimary), + // Timetable Button + BottomNavIconWidget(() { + if (page.page != HomePages.timetable) { + HapticFeedback.lightImpact(); + + setState(() { + previousPages.add(page); + canPop = false; + page = + ActiveHomePage(HomePages.timetable); + }); + } + }, + page.page == HomePages.timetable, + page.page == HomePages.timetable + ? Majesticon.calendarSolid + : Majesticon.calendarLine, + widget.data.l10n.timetable, + page.page == HomePages.timetable + ? appStyle.colors.accent + : appStyle.colors.secondary, + appStyle.colors.textPrimary), + // More Button + BottomNavIconWidget( + () { + HapticFeedback.lightImpact(); + showExtrasBottomSheet(context, widget.data); + }, + false, + Majesticon.globeEarthLine, + widget.data.l10n.other, + appStyle.colors.secondary, + appStyle.colors.textPrimary, + ), + ], + ), + ), ), - ), + ], ), + toast ?? SizedBox(), ], ), - toast ?? SizedBox(), - ], + ), ), ), - ), - ), - onPopInvokedWithResult: (_, __) => { - if (previousPages.isNotEmpty && page != previousPages.last) - { - setState(() { - page = previousPages.removeLast(); - canPop = previousPages.isEmpty; - }) - } - }, - ); + onPopInvokedWithResult: (_, __) => { + if (previousPages.isNotEmpty && page != previousPages.last) + { + setState(() { + page = previousPages.removeLast(); + canPop = previousPages.isEmpty; + }) + } + }, + )); } @override