From 546ca32f1f5cd9a6a29ee4b59276e70d046439d3 Mon Sep 17 00:00:00 2001 From: md-y <15069105+md-y@users.noreply.github.com> Date: Tue, 29 Nov 2022 13:26:30 -0600 Subject: [PATCH 1/6] Adjusted text sizing, clipping, and other misc changes --- lib/screens/Homescreen.dart | 53 +++++---- lib/screens/Stats.dart | 139 ++++++++++-------------- lib/screens/qrview.dart | 7 +- lib/widgets/CustomStatsWidget.dart | 169 ++++++++--------------------- 4 files changed, 131 insertions(+), 237 deletions(-) diff --git a/lib/screens/Homescreen.dart b/lib/screens/Homescreen.dart index 1858f99..ffccd3b 100644 --- a/lib/screens/Homescreen.dart +++ b/lib/screens/Homescreen.dart @@ -12,7 +12,6 @@ import 'package:rememberme/services/cardservice.dart'; import 'package:rememberme/services/deckservice.dart'; import 'package:rememberme/widgets/MemoryGameButton.dart'; import 'package:rememberme/widgets/deckcarousel.dart'; -import 'package:rememberme/widgets/memorygameselectdialog.dart'; import 'package:rememberme/widgets/roundedpage.dart'; import 'package:icon_decoration/icon_decoration.dart'; import 'package:rememberme/widgets/useravatar.dart'; @@ -120,38 +119,36 @@ class _HomepageState extends State { MaterialPageRoute(builder: (context) => const Stats()), ); }, - icon: Stack( - alignment: AlignmentDirectional.center, - children: const [ - Icon( - Icons.circle, - size: 70, - color: Color.fromARGB(90, 60, 200, 10), - ), - DecoratedIcon( - icon: Icon(Icons.star, size: 30, color: Colors.yellow), - decoration: IconDecoration( - shadows: [ - Shadow( - blurRadius: 30, - offset: Offset(1, 0), - color: Colors.brown) + icon: Container( + padding: const EdgeInsets.all(12), + decoration: const BoxDecoration( + color: Color.fromARGB(70, 60, 200, 10), + shape: BoxShape.circle, + ), + child: const DecoratedIcon( + icon: Icon(Icons.star, size: 35, color: Colors.yellow), + decoration: IconDecoration( + shadows: [ + Shadow( + blurRadius: 20, + offset: Offset(1, 0), + color: Colors.brown, + ) + ], + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xfffaf8f8), + Color(0xfffae16c), + Color.fromARGB(128, 250, 148, 75), ], - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Color(0xfffaf8f8), - Color(0xfffae16c), - Color.fromARGB(128, 250, 148, 75), - ], - ), ), ), - ], + ), ), label: Text( - '${_masterCardList.length} cards added! ', + ' ${_masterCardList.length} cards added! ', style: const TextStyle( fontSize: 24, color: Colors.black, diff --git a/lib/screens/Stats.dart b/lib/screens/Stats.dart index c1e3397..7432ae9 100644 --- a/lib/screens/Stats.dart +++ b/lib/screens/Stats.dart @@ -1,8 +1,7 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import '../services/userservice.dart'; -import '../widgets/CustomStatsWidget.dart'; +import 'package:rememberme/widgets/roundedpage.dart'; +import 'package:rememberme/services/userservice.dart'; +import 'package:rememberme/widgets/CustomStatsWidget.dart'; import 'package:rememberme/services/deckservice.dart'; class Stats extends StatefulWidget { @@ -19,70 +18,46 @@ class _Stats extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: PreferredSize( - //WITHIN THE APPBAR, FIRSTLY DEALING WITH CHANGED SIZE - preferredSize: Size.fromHeight(100.0), - child: AppBar( - //HERE WE ADD A BACK BUTTON (CONNECT SCREEN LATER) - automaticallyImplyLeading: false, - leadingWidth: 100, - leading: ElevatedButton.icon( - onPressed: () => Navigator.of(context).pop(), - icon: const Icon(Icons.arrow_left, size: 45), - label: const Text(''), - style: ElevatedButton.styleFrom( - elevation: 0, - backgroundColor: Colors.transparent)), - - //THIS ADDS THE TEXT IN THE APPBAR - title: Text('Statistics'), - centerTitle: true, - titleTextStyle: TextStyle( - fontSize: 27, - fontWeight: FontWeight.bold - ), - backgroundColor: Color.fromARGB(255, 253, 142, 84), - ) - ), - - - - - body: ListView( - children: [ - //For achievement heading on the Homepage------------------------- - - FutureBuilder(future: deckFuture, builder: (ctxt, snapshot) - { + return RoundedPage( + title: 'Statistics', + child: Column( + children: [ + FutureBuilder( + future: deckFuture, + builder: (ctxt, snapshot) { if (snapshot.hasData) { return CustomStats( titleText: "Cards Added", achievement: "${snapshot.data!.cards.length} cards added!", ); + } else { + return const Center( + child: CircularProgressIndicator(), + ); } - else { - return CircularProgressIndicator(); - } - }), - - FutureBuilder(future: allDeckFuture, builder: (ctxt, snapshot) - { + }, + ), + FutureBuilder( + future: allDeckFuture, + builder: (ctxt, snapshot) { if (snapshot.hasData) { return CustomStats( titleText: "Number of Decks", achievement: "${snapshot.data!.length} deck(s)", ); + } else { + return const Center( + child: CircularProgressIndicator(), + ); } - else { - return CircularProgressIndicator(); - } - }), - - FutureBuilder(future: allDeckFuture, builder: (ctxt, snapshot) - { + }, + ), + FutureBuilder( + future: allDeckFuture, + builder: (ctxt, snapshot) { if (snapshot.hasData) { - var master = snapshot.data!.firstWhere((element) => element.isMaster); + var master = + snapshot.data!.firstWhere((element) => element.isMaster); Map map = {}; for (var card in master.cards) { final split = card.name.split(' '); @@ -93,44 +68,40 @@ class _Stats extends State { map[firstname] = map[firstname]! + 1; } var sorted = map.entries.toList(); - sorted.sort((a,b)=>a.value.compareTo(b.value)); + sorted.sort((a, b) => a.value.compareTo(b.value)); var commonname = sorted.last; return CustomStats( titleText: "Most Common", achievement: "${commonname.value} ${commonname.key}(s)", ); - } - else { - return CircularProgressIndicator(); - } - }), - - FutureBuilder(future: date, builder: (ctxt, snapshot) - { - if (snapshot.hasData) { - - return CustomStats( - titleText: "Days in a Row", - achievement: "${snapshot.data!}", + } else { + return const Center( + child: CircularProgressIndicator(), ); } - else { - return CircularProgressIndicator(); - } - }), - - CustomStats( - titleText: "High Score Today", - achievement: "Memory Game: 90%" - ), - - ] + }, + ), + FutureBuilder( + future: date, + builder: (ctxt, snapshot) { + if (snapshot.hasData) { + return CustomStats( + titleText: "Days in a Row", + achievement: "${snapshot.data!}", + ); + } else { + return const Center( + child: CircularProgressIndicator(), + ); + } + }), + const CustomStats( + titleText: "High Score Today", + achievement: "Memory Game: 90%", + ), + ], ), ); } } - - - - diff --git a/lib/screens/qrview.dart b/lib/screens/qrview.dart index d296522..55a621c 100644 --- a/lib/screens/qrview.dart +++ b/lib/screens/qrview.dart @@ -1,3 +1,4 @@ +import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:rememberme/screens/modifycard.dart'; @@ -34,12 +35,14 @@ class _QRViewState extends State { ), ), Flexible( - child: Text( + child: AutoSizeText( 'Nice to Meet You! I\'m ${AuthService.getUser()?.displayName}.', + maxLines: 3, + maxFontSize: 28, style: const TextStyle( - fontSize: 36, fontWeight: FontWeight.bold, color: Colors.white, + fontSize: 28, ), ), ), diff --git a/lib/widgets/CustomStatsWidget.dart b/lib/widgets/CustomStatsWidget.dart index 7f51e0a..a8f64d7 100644 --- a/lib/widgets/CustomStatsWidget.dart +++ b/lib/widgets/CustomStatsWidget.dart @@ -1,141 +1,64 @@ - -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:icon_decoration/icon_decoration.dart'; - - class CustomStats extends StatelessWidget { final String titleText; final String achievement; - const CustomStats({required this.titleText, required this.achievement}); - - + const CustomStats({ + super.key, + required this.titleText, + required this.achievement, + }); @override Widget build(BuildContext context) { - return Row( - children: [ - //Space between left edge and star - Container( - margin: EdgeInsets.fromLTRB(50, 90, 0, 0) - ), - - Container( - margin: EdgeInsets.fromLTRB(0, 30, 0, 0), - child: Stack( - alignment: AlignmentDirectional.center, - children: [ - - - Icon( - Icons.circle, - size: 70, - color: Color.fromARGB(70,60,200,10), - ), - - DecoratedIcon( - icon: Icon( - Icons.star, - size: 30, - color: Colors.yellow - ), - decoration: IconDecoration( - shadows: [Shadow(blurRadius: 20, offset: Offset(1, 0), color: Colors.brown)], - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Color(0xfffaf8f8), - Color(0xfffae16c), - Color.fromARGB(128, 250, 148, 75), - ], - ) - ) - ), - - ], + return Container( + margin: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 16, + ), + child: ListTile( + title: Text( + titleText, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 23, + ), ), + subtitle: Text( + achievement, + style: const TextStyle(fontSize: 20), ), - //Space between star and text - Container( - margin: EdgeInsets.fromLTRB(50, 90, 0, 0) - ), - - //Space above text - Column( - children: [ - Column( - children: [ - Text( - ' ' - ), - ], - ), - //More space above text - Column( - children: [ - Text( - ' ' - ), - ], - ), - - //More space above text - Column( - children: [ - Text( - ' ' - ), - ], - ), - - //Bolded text - Column( - children: [ - SizedBox( - width: 225, - child: Text( - titleText, - style: TextStyle( - fontSize: 23, - fontWeight: FontWeight.bold - ) - ), - ), - ], - ), - - //Space between bolded and unbolded text - Column( - children: [ - Text( - ' ' - ), - ], - ), - -//Unbolded text - Column( - children: [ - SizedBox( - width: 225, - child: Text( - achievement, - style: TextStyle( - fontSize: 23 - ) - ), - ), + leading: Container( + padding: const EdgeInsets.all(16), + decoration: const BoxDecoration( + color: Color.fromARGB(70, 60, 200, 10), + shape: BoxShape.circle, + ), + child: const DecoratedIcon( + icon: Icon(Icons.star, size: 35, color: Colors.yellow), + decoration: IconDecoration( + shadows: [ + Shadow( + blurRadius: 20, + offset: Offset(1, 0), + color: Colors.brown, + ) + ], + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xfffaf8f8), + Color(0xfffae16c), + Color.fromARGB(128, 250, 148, 75), ], ), - ] + ), + ), ), - ], + ), ); } } - - - From 653c26ccba224a83db10b8b2745b121588d0c481 Mon Sep 17 00:00:00 2001 From: md-y <15069105+md-y@users.noreply.github.com> Date: Tue, 29 Nov 2022 16:13:01 -0600 Subject: [PATCH 2/6] Fixed end screen, memory game stat and select --- lib/screens/EndScreen.dart | 4 ++-- lib/screens/Stats.dart | 2 +- lib/widgets/memorygameselectdialog.dart | 4 +--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/screens/EndScreen.dart b/lib/screens/EndScreen.dart index 4ad6808..510cfd8 100644 --- a/lib/screens/EndScreen.dart +++ b/lib/screens/EndScreen.dart @@ -19,7 +19,7 @@ class _EndPageState extends State { Widget build(BuildContext context) { int correct = widget.m.correctGuesses; int wrong = widget.m.wrongGuesses; - int accuracy = (((wrong - correct).abs() / correct) * 100).round(); + int accuracy = (correct / (correct + wrong) * 100).round(); return Scaffold( body: Padding( @@ -75,7 +75,7 @@ class _EndPageState extends State { Padding( padding: const EdgeInsets.fromLTRB(20, 30, 20, 10), child: Text( - 'Accuarcy: ${accuracy}%', + 'Accuracy: ${accuracy}%', style: TextStyle( color: Colors.black, fontSize: 25, diff --git a/lib/screens/Stats.dart b/lib/screens/Stats.dart index 7432ae9..9e3b5ef 100644 --- a/lib/screens/Stats.dart +++ b/lib/screens/Stats.dart @@ -98,7 +98,7 @@ class _Stats extends State { }), const CustomStats( titleText: "High Score Today", - achievement: "Memory Game: 90%", + achievement: "Memory Game: 100%", ), ], ), diff --git a/lib/widgets/memorygameselectdialog.dart b/lib/widgets/memorygameselectdialog.dart index 6d22d9c..f5d450a 100644 --- a/lib/widgets/memorygameselectdialog.dart +++ b/lib/widgets/memorygameselectdialog.dart @@ -48,13 +48,11 @@ class MemoryGameSelectDialog extends StatefulWidget { } class _MemoryGameSelectDialogState extends State { - late Deck _selectedDeck; List _sortedDecks = []; @override void initState() { super.initState(); - _selectedDeck = widget.decks.firstWhere((deck) => deck.isMaster); _sortedDecks = widget.decks.sublist(0); _sortedDecks.sort( (a, b) => _getQuestionCount(b).compareTo(_getQuestionCount(a)), @@ -93,7 +91,7 @@ class _MemoryGameSelectDialogState extends State { Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (context) => MemoryGame( - deck: _selectedDeck, + deck: deck, ), ), ); From 46011d978a94bf0c703e7ae4cfcde20a3e579f2c Mon Sep 17 00:00:00 2001 From: md-y <15069105+md-y@users.noreply.github.com> Date: Tue, 29 Nov 2022 16:18:13 -0600 Subject: [PATCH 3/6] Remove name from answer tiles --- lib/widgets/MemoryGameTile.dart | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/widgets/MemoryGameTile.dart b/lib/widgets/MemoryGameTile.dart index 21a3799..e1eff3e 100644 --- a/lib/widgets/MemoryGameTile.dart +++ b/lib/widgets/MemoryGameTile.dart @@ -149,12 +149,14 @@ class _MemoryGameTileState extends State child: Padding( padding: const EdgeInsets.all(16), child: GridTile( - header: Text( - widget.card.name, - textAlign: TextAlign.center, - ), + header: widget.isQuestion + ? Text( + widget.card.name, + textAlign: TextAlign.center, + ) + : null, child: Padding( - padding: const EdgeInsets.only(top: 16), + padding: EdgeInsets.only(top: (widget.isQuestion ? 16 : 0)), child: Center( child: AutoSizeText( widget.data, @@ -162,6 +164,7 @@ class _MemoryGameTileState extends State fontWeight: FontWeight.bold, fontSize: 30, ), + wrapWords: false, textAlign: TextAlign.center, ), ), From 15bde818d5c8ab53ee5746be7d2cee6bc8f8a164 Mon Sep 17 00:00:00 2001 From: md-y <15069105+md-y@users.noreply.github.com> Date: Tue, 29 Nov 2022 16:21:06 -0600 Subject: [PATCH 4/6] FIx not disposing animations bug --- lib/widgets/MemoryGameTile.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/widgets/MemoryGameTile.dart b/lib/widgets/MemoryGameTile.dart index e1eff3e..5acf34b 100644 --- a/lib/widgets/MemoryGameTile.dart +++ b/lib/widgets/MemoryGameTile.dart @@ -179,6 +179,8 @@ class _MemoryGameTileState extends State @override void dispose() { _selectAnimationController.dispose(); + _colorAnimationController.dispose(); + _fadeAnimationController.dispose(); super.dispose(); } } From 1460d26f7fc2f188c20998c77ca8094844663799 Mon Sep 17 00:00:00 2001 From: md-y <15069105+md-y@users.noreply.github.com> Date: Sat, 3 Dec 2022 22:07:34 -0600 Subject: [PATCH 5/6] Data syncing and optimizations --- assets/human-brain.png | Bin 17923 -> 0 bytes lib/main.dart | 9 ++-- lib/screens/EndScreen.dart | 18 ++++++-- lib/screens/Homescreen.dart | 76 ++++++++++++++++++++++------------ lib/screens/Stats.dart | 19 +++++++-- lib/services/userservice.dart | 15 +++++++ lib/widgets/deckcarousel.dart | 21 ++++------ pubspec.lock | 9 +--- pubspec.yaml | 3 -- 9 files changed, 107 insertions(+), 63 deletions(-) delete mode 100644 assets/human-brain.png diff --git a/assets/human-brain.png b/assets/human-brain.png deleted file mode 100644 index e96e2967377a82c5ea286117dbd8604f3c371414..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17923 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 a<#pa@%+WVEItzGU3xlVtpUXO@geCw2HYSh& diff --git a/lib/main.dart b/lib/main.dart index c6861a8..fee997f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,13 +1,10 @@ import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; -import 'package:rememberme/screens/Stats.dart'; import 'package:rememberme/screens/homescreen.dart'; import 'package:rememberme/screens/login-signup.dart'; -import 'package:rememberme/screens/profile.dart'; import 'package:rememberme/services/authservice.dart'; +import 'package:rememberme/services/userservice.dart'; import 'firebase_options.dart'; -import 'package:rememberme/screens/modifycard.dart'; -import 'Screens/MemorySelect.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -15,6 +12,8 @@ void main() async { options: DefaultFirebaseOptions.currentPlatform, ); + UserService.updateUserLoginDate(); + runApp(MyApp()); } @@ -53,4 +52,4 @@ class MyApp extends StatelessWidget { home: AuthService.isUserSignedIn() ? Homepage() : LoginOptions(), ); } -} \ No newline at end of file +} diff --git a/lib/screens/EndScreen.dart b/lib/screens/EndScreen.dart index 510cfd8..e78fd11 100644 --- a/lib/screens/EndScreen.dart +++ b/lib/screens/EndScreen.dart @@ -3,6 +3,7 @@ import 'package:rememberme/controllers/MemoryGameController.dart'; import 'package:rememberme/screens/Homescreen.dart'; import 'package:rememberme/screens/MemoryGame.dart'; import 'package:rememberme/controllers/MemoryGameController.dart'; +import 'package:rememberme/services/userservice.dart'; import '../widgets/memorygameselectdialog.dart'; class EndPage extends StatefulWidget { @@ -15,12 +16,21 @@ class EndPage extends StatefulWidget { } class _EndPageState extends State { + late int correct; + late int wrong; + late int accuracy; + @override - Widget build(BuildContext context) { - int correct = widget.m.correctGuesses; - int wrong = widget.m.wrongGuesses; - int accuracy = (correct / (correct + wrong) * 100).round(); + void initState() { + super.initState(); + correct = widget.m.correctGuesses; + wrong = widget.m.wrongGuesses; + accuracy = (correct / (correct + wrong) * 100).round(); + UserService.setMemoryGameHighScore(accuracy); + } + @override + Widget build(BuildContext context) { return Scaffold( body: Padding( padding: const EdgeInsets.all(40), diff --git a/lib/screens/Homescreen.dart b/lib/screens/Homescreen.dart index ffccd3b..653cb0a 100644 --- a/lib/screens/Homescreen.dart +++ b/lib/screens/Homescreen.dart @@ -25,15 +25,12 @@ class Homepage extends StatefulWidget { class _HomepageState extends State { List _masterCardList = []; + List _decks = []; @override void initState() { super.initState(); - DeckService.getMasterDeck().then((value) { - setState(() { - _masterCardList = value.cards; - }); - }); + _update(); } @override @@ -52,43 +49,63 @@ class _HomepageState extends State { labelStyle: TextStyle(fontSize: 22), child: const Icon(Icons.note_add, size: 30, color: Color.fromRGBO(239, 119, 55, 1.0)), - onTap: () => Navigator.of(context).push( - MaterialPageRoute(builder: (_) => const ModifyCard()), - ), + onTap: () async { + await Navigator.of(context).push( + MaterialPageRoute(builder: (_) => const ModifyCard()), + ); + await _update(); + }, ), SpeedDialChild( label: 'New Deck', labelStyle: TextStyle(fontSize: 22), child: const Icon(Icons.collections_bookmark, size: 30, color: Color.fromRGBO(239, 119, 55, 1.0)), - onTap: () => Navigator.of(context).push( - MaterialPageRoute(builder: (_) => const ModifyDeck()), - ), + onTap: () async { + await Navigator.of(context).push( + MaterialPageRoute(builder: (_) => const ModifyDeck()), + ); + await _update(); + }, ), SpeedDialChild( label: 'QR Code', labelStyle: TextStyle(fontSize: 22), child: const Icon(Icons.qr_code_outlined, size: 30, color: Color.fromRGBO(239, 119, 55, 1.0)), - onTap: () => Navigator.of(context).push( - MaterialPageRoute(builder: (_) => const QRScan()), - ), - onLongPress: () => Navigator.of(context).push( - MaterialPageRoute(builder: (_) => const QRView()), - ), + onTap: () async { + await Navigator.of(context).push( + MaterialPageRoute(builder: (_) => const QRScan()), + ); + await _update(); + }, + onLongPress: () async { + await Navigator.of(context).push( + MaterialPageRoute(builder: (_) => const QRView()), + ); + await _update(); + }, ), ], ), appBarActions: [ IconButton( - onPressed: () { + onPressed: () async { // method to show the search bar - showSearch( + var res = await showSearch( context: context, // delegate to customize the search bar delegate: _CustomSearchDelegate( cards: _masterCardList, )); + if (res != null && mounted) { + await Navigator.of(context).push(MaterialPageRoute( + builder: (context) => ModifyCard( + existingCard: res, + ), + )); + } + await _update(); }, icon: const Icon(Icons.search), ) @@ -173,7 +190,10 @@ class _HomepageState extends State { ), //THIS IS TO SET UP THE CAROUSEL - const DeckCarousel(), + DeckCarousel( + decks: _decks, + onChange: () => _update(), + ), Container( margin: const EdgeInsets.only(bottom: 60), @@ -184,9 +204,17 @@ class _HomepageState extends State { ), ); } + + _update() async { + var decks = await DeckService.getAllDecks(); + setState(() { + _decks = decks; + _masterCardList = decks.firstWhere((i) => i.isMaster).cards; + }); + } } -class _CustomSearchDelegate extends SearchDelegate { +class _CustomSearchDelegate extends SearchDelegate { final List cards; _CustomSearchDelegate({required this.cards}); @@ -243,11 +271,7 @@ class _CustomSearchDelegate extends SearchDelegate { var result = matchQuery[index]; return ListTile( title: Text(result.name), - onTap: () => Navigator.of(context).pushReplacement( - MaterialPageRoute( - builder: (context) => CardView(initialCard: result), - ), - ), + onTap: () => Navigator.of(context).pop(result), ); }, ); diff --git a/lib/screens/Stats.dart b/lib/screens/Stats.dart index 9e3b5ef..c8e42c9 100644 --- a/lib/screens/Stats.dart +++ b/lib/screens/Stats.dart @@ -15,6 +15,7 @@ class _Stats extends State { final Future deckFuture = DeckService.getMasterDeck(); final Future> allDeckFuture = DeckService.getAllDecks(); final Future date = UserService.getDaysLogged(); + final Future highScore = UserService.getMemoryGameHighScore(); @override Widget build(BuildContext context) { @@ -96,10 +97,20 @@ class _Stats extends State { ); } }), - const CustomStats( - titleText: "High Score Today", - achievement: "Memory Game: 100%", - ), + FutureBuilder( + future: highScore, + builder: (ctxt, snapshot) { + if (snapshot.hasData) { + return CustomStats( + titleText: "High Score", + achievement: "${snapshot.data}%", + ); + } else { + return const Center( + child: CircularProgressIndicator(), + ); + } + }), ], ), ); diff --git a/lib/services/userservice.dart b/lib/services/userservice.dart index bf82f10..c159726 100644 --- a/lib/services/userservice.dart +++ b/lib/services/userservice.dart @@ -89,4 +89,19 @@ class UserService { return null; } } + + static Future setMemoryGameHighScore(int score) async { + var ref = await getUserDocRef(); + await ref.set({'memorygame_highscore': score}); + } + + static Future getMemoryGameHighScore() async { + var res = await getUserDocRef().get(); + var data = res.data(); + if (data == null || !data.containsKey('memorygame_highscore')) { + return 0; + } else { + return data['memorygame_highscore'] as int; + } + } } diff --git a/lib/widgets/deckcarousel.dart b/lib/widgets/deckcarousel.dart index 39c7049..f210729 100644 --- a/lib/widgets/deckcarousel.dart +++ b/lib/widgets/deckcarousel.dart @@ -7,7 +7,10 @@ import 'package:rememberme/services/deckservice.dart'; import 'package:rememberme/widgets/cardavatar.dart'; class DeckCarousel extends StatefulWidget { - const DeckCarousel({super.key}); + const DeckCarousel({super.key, required this.decks, this.onChange}); + + final List decks; + final void Function()? onChange; @override State createState() => _DeckCarouselState(); @@ -17,12 +20,10 @@ class _DeckCarouselState extends State { double _scrollValue = -1; int _lastFullScroll = -1; int _currentPage = 0; - List _decks = []; @override void initState() { super.initState(); - _updateDecks(); } @override @@ -60,7 +61,7 @@ class _DeckCarouselState extends State { List _getCarouselItems() { double percent = sin(pi * (_scrollValue - _lastFullScroll)).abs(); - return _decks + return widget.decks .map( (deck) => _CarouselItem( percent: percent, @@ -71,20 +72,14 @@ class _DeckCarouselState extends State { builder: (context) => DeckView(initialDeck: deck), ), ); - _updateDecks(); + if (widget.onChange != null) { + widget.onChange!(); + } }, ), ) .toList(); } - - _updateDecks() { - DeckService.getAllDecks().then((value) { - setState(() { - _decks = value; - }); - }); - } } class _CarouselItem extends StatelessWidget { diff --git a/pubspec.lock b/pubspec.lock index 3d2f567..839ec27 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -15,13 +15,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.0.2" - animations: - dependency: "direct main" - description: - name: animations - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.7" async: dependency: transitive description: @@ -603,7 +596,7 @@ packages: source: hosted version: "3.0.6" vector_math: - dependency: "direct main" + dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" diff --git a/pubspec.yaml b/pubspec.yaml index 10265ba..023f448 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -46,7 +46,6 @@ dependencies: firebase_storage: ^10.3.11 image_picker: ^0.8.6 cached_network_image: ^3.2.2 - animations: ^2.0.7 page_transition: ^2.0.9 auto_size_text: ^3.0.0 flutter_staggered_grid_view: ^0.6.2 @@ -56,7 +55,6 @@ dependencies: crypto: ^3.0.2 file: ^6.1.4 animate_gradient: ^0.0.2 - vector_math: ^2.1.2 font_awesome_flutter: ^10.2.1 dev_dependencies: @@ -86,7 +84,6 @@ flutter: - assets/OrangeFlower.jpg - assets/avatar.webp - assets/logo.png - - assets/human-brain.png # An image asset can refer to one or more resolution-specific "variants", see From a1752d6585b605ef41e5dd97d5ccf9ddb92595ee Mon Sep 17 00:00:00 2001 From: md-y <15069105+md-y@users.noreply.github.com> Date: Sat, 3 Dec 2022 22:14:03 -0600 Subject: [PATCH 6/6] Fix stats overwriting other stats --- lib/services/userservice.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/services/userservice.dart b/lib/services/userservice.dart index c159726..9a950a9 100644 --- a/lib/services/userservice.dart +++ b/lib/services/userservice.dart @@ -47,7 +47,7 @@ class UserService { } await getUserDocRef() - .set({'lastlogin': currenttime, 'dayslogged': dayslogged}); + .update({'lastlogin': currenttime, 'dayslogged': dayslogged}); return dayslogged; } @@ -92,7 +92,7 @@ class UserService { static Future setMemoryGameHighScore(int score) async { var ref = await getUserDocRef(); - await ref.set({'memorygame_highscore': score}); + await ref.update({'memorygame_highscore': score}); } static Future getMemoryGameHighScore() async {