diff --git a/filcnaplo/pubspec.yaml b/filcnaplo/pubspec.yaml index d21ace1..6c1db7b 100644 --- a/filcnaplo/pubspec.yaml +++ b/filcnaplo/pubspec.yaml @@ -65,6 +65,7 @@ dependencies: background_fetch: ^1.1.5 flutter_local_notifications: ^14.1.0 package_info_plus: ^4.0.2 + screenshot: ^2.1.0 dev_dependencies: flutter_lints: ^2.0.1 diff --git a/filcnaplo_mobile_ui/lib/common/personality_card/empty_card.dart b/filcnaplo_mobile_ui/lib/common/personality_card/empty_card.dart new file mode 100644 index 0000000..9df12a8 --- /dev/null +++ b/filcnaplo_mobile_ui/lib/common/personality_card/empty_card.dart @@ -0,0 +1,68 @@ +import 'package:dotted_border/dotted_border.dart'; +import 'package:flutter/material.dart'; + +class EmptyCard extends StatefulWidget { + const EmptyCard({ + Key? key, + required this.text, + }) : super(key: key); + + final String text; + + @override + State createState() => _EmptyCardState(); +} + +class _EmptyCardState extends State { + bool hold = false; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onLongPressDown: (_) => setState(() => hold = true), + onLongPressEnd: (_) => setState(() => hold = false), + onLongPressCancel: () => setState(() => hold = false), + child: AnimatedScale( + scale: hold ? 1.018 : 1.0, + curve: Curves.easeInOutBack, + duration: const Duration(milliseconds: 300), + child: Container( + height: 444, + padding: + const EdgeInsets.only(top: 12, bottom: 12, left: 12, right: 12), + decoration: BoxDecoration( + color: const Color(0x280008FF), + borderRadius: const BorderRadius.all(Radius.circular(5)), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.08), + offset: const Offset(0, 5), + blurRadius: 20, + spreadRadius: 10, + ), + ], + ), + child: DottedBorder( + color: Colors.black.withOpacity(0.9), + dashPattern: const [12, 12], + padding: + const EdgeInsets.only(top: 20, bottom: 20, left: 20, right: 20), + child: Center( + child: Text( + widget.text, + style: TextStyle( + color: Colors.white.withOpacity(0.9), + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/filcnaplo_mobile_ui/lib/screens/summary/pages/allsum_page.dart b/filcnaplo_mobile_ui/lib/screens/summary/pages/allsum_page.dart index d7d03b5..f7d9966 100644 --- a/filcnaplo_mobile_ui/lib/screens/summary/pages/allsum_page.dart +++ b/filcnaplo_mobile_ui/lib/screens/summary/pages/allsum_page.dart @@ -27,6 +27,7 @@ class _AllSumBodyState extends State { late List lastSixTiles = []; int avgDropValue = 0; + bool animation = false; List getSubjectGrades(Subject subject, {int days = 0}) => gradeProvider .grades @@ -45,6 +46,12 @@ class _AllSumBodyState extends State { homeworkProvider = Provider.of(context, listen: false); absenceProvider = Provider.of(context, listen: false); //timetableProvider = Provider.of(context, listen: false); + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + setState(() { + animation = true; + }); + }); } void getGrades() { @@ -176,7 +183,11 @@ class _AllSumBodyState extends State { const SizedBox( height: 45, ), - SizedBox( + AnimatedContainer( + curve: Curves.easeInOut, + duration: const Duration(milliseconds: 420), + transform: Matrix4.translationValues( + animation ? 0 : MediaQuery.of(context).size.width, 0, 0), height: 250, child: GridView.count( crossAxisCount: 3, @@ -188,7 +199,11 @@ class _AllSumBodyState extends State { const SizedBox( height: 30, ), - SizedBox( + AnimatedContainer( + curve: Curves.easeInOut, + duration: const Duration(milliseconds: 420), + transform: Matrix4.translationValues( + animation ? 0 : -MediaQuery.of(context).size.width, 0, 0), height: 250, child: GridView.count( crossAxisCount: 3, diff --git a/filcnaplo_mobile_ui/lib/screens/summary/pages/grades_page.dart b/filcnaplo_mobile_ui/lib/screens/summary/pages/grades_page.dart index 9deab81..c84a23c 100644 --- a/filcnaplo_mobile_ui/lib/screens/summary/pages/grades_page.dart +++ b/filcnaplo_mobile_ui/lib/screens/summary/pages/grades_page.dart @@ -45,6 +45,7 @@ class _GradesBodyState extends State { List subjectTiles1 = []; int avgDropValue = 0; + bool animation = false; List getSubjectGrades(Subject subject, {int days = 0}) => gradeProvider .grades @@ -61,6 +62,12 @@ class _GradesBodyState extends State { gradeProvider = Provider.of(context, listen: false); settings = Provider.of(context, listen: false); + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + setState(() { + animation = true; + }); + }); } void generateTiles({required int filter}) { @@ -73,37 +80,46 @@ class _GradesBodyState extends State { Map subjectAvgs = {}; + var count = 1; + for (Subject subject in subjects) { List subjectGrades = getSubjectGrades(subject); double avg = AverageHelper.averageEvals(subjectGrades); if (avg != 0) subjectAvgs[subject] = avg; - Widget widget = Row( - children: [ - GradeValueWidget( - GradeValue(avg.round(), '', '', 100), - fill: true, - size: 28.0, - ), - const SizedBox(width: 8), - Text( - subject.renamedTo ?? subject.name.capital(), - maxLines: 2, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20.0, - color: Colors.white.withOpacity(0.98), - fontStyle: settings.renamedSubjectsItalics && subject.isRenamed - ? FontStyle.italic - : null, + Widget widget = AnimatedContainer( + curve: Curves.easeInOut, + duration: Duration(milliseconds: 300 + (count * 120)), + transform: Matrix4.translationValues( + animation ? 0 : MediaQuery.of(context).size.width, 0, 0), + child: Row( + children: [ + GradeValueWidget( + GradeValue(avg.round(), '', '', 100), + fill: true, + size: 28.0, ), - ) - ], + const SizedBox(width: 8), + Text( + subject.renamedTo ?? subject.name.capital(), + maxLines: 2, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20.0, + color: Colors.white.withOpacity(0.98), + fontStyle: settings.renamedSubjectsItalics && subject.isRenamed + ? FontStyle.italic + : null, + ), + ) + ], + ), ); if (avg.round() == filter) { tiles.add(widget); + count++; } } @@ -175,7 +191,9 @@ class _GradesBodyState extends State { children: [ SizedBox( height: ((100 * subjectTiles5.length) / - (subjectTiles5[0].runtimeType == Row ? 1.95 : 1.2)) + (subjectTiles5[0].runtimeType == AnimatedContainer + ? 1.95 + : 1.2)) .toDouble(), child: ListView.builder( padding: const EdgeInsets.only(left: 5), @@ -186,7 +204,7 @@ class _GradesBodyState extends State { EdgeInsetsGeometry panelPadding = const EdgeInsets.symmetric(horizontal: 24.0); - if (subjectTiles5[index].runtimeType == Row) { + if (subjectTiles5[index].runtimeType == AnimatedContainer) { return Padding( padding: const EdgeInsets.only(top: 8), child: subjectTiles5[index]); @@ -212,7 +230,9 @@ class _GradesBodyState extends State { const SizedBox(height: 12.0), SizedBox( height: ((100 * subjectTiles3.length) / - (subjectTiles3[0].runtimeType == Row ? 1.95 : 1.2)) + (subjectTiles3[0].runtimeType == AnimatedContainer + ? 1.95 + : 1.2)) .toDouble(), child: ListView.builder( padding: const EdgeInsets.only(left: 5), @@ -223,7 +243,7 @@ class _GradesBodyState extends State { EdgeInsetsGeometry panelPadding = const EdgeInsets.symmetric(horizontal: 24.0); - if (subjectTiles3[index].runtimeType == Row) { + if (subjectTiles3[index].runtimeType == AnimatedContainer) { return Padding( padding: const EdgeInsets.only(top: 8), child: subjectTiles3[index]); @@ -249,7 +269,9 @@ class _GradesBodyState extends State { const SizedBox(height: 12.0), SizedBox( height: ((100 * subjectTiles1.length) / - (subjectTiles1[0].runtimeType == Row ? 1.95 : 1.2)) + (subjectTiles1[0].runtimeType == AnimatedContainer + ? 1.95 + : 1.2)) .toDouble(), child: ListView.builder( padding: const EdgeInsets.only(left: 5), @@ -260,7 +282,7 @@ class _GradesBodyState extends State { EdgeInsetsGeometry panelPadding = const EdgeInsets.symmetric(horizontal: 24.0); - if (subjectTiles1[index].runtimeType == Row) { + if (subjectTiles1[index].runtimeType == AnimatedContainer) { return Padding( padding: const EdgeInsets.only(top: 8), child: subjectTiles1[index]); diff --git a/filcnaplo_mobile_ui/lib/screens/summary/pages/personality_page.dart b/filcnaplo_mobile_ui/lib/screens/summary/pages/personality_page.dart index 41d2bda..8782272 100644 --- a/filcnaplo_mobile_ui/lib/screens/summary/pages/personality_page.dart +++ b/filcnaplo_mobile_ui/lib/screens/summary/pages/personality_page.dart @@ -1,7 +1,14 @@ +import 'dart:io'; + import 'package:filcnaplo/api/providers/user_provider.dart'; +import 'package:filcnaplo_mobile_ui/common/personality_card/empty_card.dart'; import 'package:filcnaplo_mobile_ui/common/personality_card/personality_card.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:provider/provider.dart'; +import 'package:screenshot/screenshot.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:path_provider/path_provider.dart'; class PersonalityBody extends StatefulWidget { const PersonalityBody({Key? key}) : super(key: key); @@ -13,6 +20,25 @@ class PersonalityBody extends StatefulWidget { class _PersonalityBodyState extends State { late UserProvider user; + bool isRevealed = false; + + ScreenshotController screenshotController = ScreenshotController(); + + sharePersonality() async { + await screenshotController.capture().then((image) async { + if (image != null) { + final directory = await getApplicationDocumentsDirectory(); + final imagePath = + await File('${directory.path}/refilc_personality.png').create(); + await imagePath.writeAsBytes(image); + + await Share.shareXFiles([XFile(imagePath.path)]); + } + }).catchError((err) { + throw err; + }); + } + @override Widget build(BuildContext context) { user = Provider.of(context); @@ -22,8 +48,39 @@ class _PersonalityBodyState extends State { mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ const SizedBox(height: 60), - PersonalityCard( - user: user, + AnimatedCrossFade( + duration: const Duration(milliseconds: 1000), + sizeCurve: Curves.easeInToLinear, + firstChild: Screenshot( + controller: screenshotController, + child: PersonalityCard(user: user), + ), + secondChild: GestureDetector( + onTap: () => setState(() { + isRevealed = true; + }), + child: const EmptyCard(text: 'Kattints a felfedéshez...'), + ), + crossFadeState: isRevealed + ? CrossFadeState.showFirst + : CrossFadeState.showSecond, + ), + const SizedBox(height: 40), + Center( + child: IconButton( + onPressed: () async { + await sharePersonality(); + }, + icon: const Icon( + FeatherIcons.share2, + color: Colors.white, + size: 20, + ), + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.all(Colors.white.withOpacity(0.5)), + ), + ), ), const SizedBox(height: 60), ]); diff --git a/filcnaplo_mobile_ui/pubspec.yaml b/filcnaplo_mobile_ui/pubspec.yaml index eaf87ed..e518617 100755 --- a/filcnaplo_mobile_ui/pubspec.yaml +++ b/filcnaplo_mobile_ui/pubspec.yaml @@ -41,6 +41,7 @@ dependencies: wtf_sliding_sheet: ^1.0.0 package_info_plus: ^4.0.2 dotted_border: ^2.0.0+3 + screenshot: ^2.1.0 dev_dependencies: flutter_lints: ^1.0.0