diff --git a/lib/Components/ScannerDialog.dart b/lib/Components/ScannerDialog.dart index 2762310..2dff64c 100644 --- a/lib/Components/ScannerDialog.dart +++ b/lib/Components/ScannerDialog.dart @@ -185,7 +185,7 @@ class _ScannerDialogState extends State { context, MaterialPageRoute( builder: (context) { - return SectionPage(visitAppContextIn: visitAppContext, sectionId: section.id!); + return SectionPage(configuration: visitAppContext.configuration!, rawSection: null, visitAppContextIn: visitAppContext, sectionId: section.id!); }, ), ); diff --git a/lib/Components/SlideFromRouteRight.dart b/lib/Components/SlideFromRouteRight.dart new file mode 100644 index 0000000..79b3813 --- /dev/null +++ b/lib/Components/SlideFromRouteRight.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +class SlideFromRightRoute extends PageRouteBuilder { + final Widget page; + + SlideFromRightRoute({required this.page}) + : super( + pageBuilder: (context, animation, secondaryAnimation) => page, + transitionsBuilder: (context, animation, secondaryAnimation, child) { + const begin = Offset(1.0, 0.0); // départ à droite (hors écran) + const end = Offset.zero; // arrivée position normale + const curve = Curves.easeInOut; + + final tween = + Tween(begin: begin, end: end).chain(CurveTween(curve: curve)); + final offsetAnimation = animation.drive(tween); + + return SlideTransition( + position: offsetAnimation, + child: child, + ); + }, + ); +} diff --git a/lib/Components/fade_route.dart b/lib/Components/fade_route.dart deleted file mode 100644 index 7009247..0000000 --- a/lib/Components/fade_route.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/material.dart'; - -class FadeRoute extends PageRouteBuilder { - final Widget page; - - FadeRoute({required this.page}) - : super( - pageBuilder: (context, animation, secondaryAnimation) => page, - transitionsBuilder: (context, animation, secondaryAnimation, child) { - const begin = 0.0; - const end = 1.0; - const curve = Curves.easeInOut; - - final tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve)); - final opacityAnimation = animation.drive(tween); - - return FadeTransition( - opacity: opacityAnimation, - child: child, - ); - }, - ); -} diff --git a/lib/Models/visitContext.dart b/lib/Models/visitContext.dart index ee86e85..0199625 100644 --- a/lib/Models/visitContext.dart +++ b/lib/Models/visitContext.dart @@ -6,7 +6,7 @@ import 'package:mymuseum_visitapp/Models/resourceModel.dart'; import 'package:mymuseum_visitapp/client.dart'; class VisitAppContext with ChangeNotifier { - Client clientAPI = Client("http://192.168.31.228:5000"); // Replace by https://api.mymuseum.be //http://192.168.31.140:8089 // https://api.myinfomate.be + Client clientAPI = Client("https://api.mymuseum.be"); // Replace by https://api.mymuseum.be //http://192.168.31.140:8089 // https://api.myinfomate.be // http://192.168.31.228:5000 String? id = ""; String? language = ""; diff --git a/lib/Screens/Article/article_page.dart b/lib/Screens/Article/article_page.dart index b145392..bb6a040 100644 --- a/lib/Screens/Article/article_page.dart +++ b/lib/Screens/Article/article_page.dart @@ -35,16 +35,20 @@ class ArticlePage extends StatefulWidget { } class _ArticlePageState extends State { - SectionDTO? sectionDTO; - ArticleDTO? articleDTO; ResourceModel? audioResourceModel; final GlobalKey _scaffoldKey = GlobalKey(); File? audioFile; late VisitAppContext visitAppContext; + late List resourcesModelToShow; @override void initState() { widget.visitAppContextIn.isContentCurrentlyShown = true; + + audioResourceModel = widget.resourcesModel.firstWhere((r) => r?.type == ResourceType.Audio, orElse: () => null); + + resourcesModelToShow = widget.resourcesModel.where((r) => r?.type != ResourceType.Audio).toList(); // TODO also handle audio in slider .. must differentiate the main audio and the rest + super.initState(); } @@ -65,24 +69,23 @@ class _ArticlePageState extends State { return Scaffold( key: _scaffoldKey, appBar: CustomAppBar( - title: sectionDTO != null ? TranslationHelper.get(sectionDTO!.title, visitAppContext) : "", + title: TranslationHelper.get(widget.articleDTO.title, visitAppContext), isHomeButton: false, isTextSizeButton: true, ), body: OrientationBuilder( builder: (context, orientation) { - if(articleDTO != null && sectionDTO != null) { if(size.height > size.width) { return Column( children: [ - if(articleDTO!.isContentTop!) + if(widget.articleDTO.isContentTop!) getContent(size, appContext), - if(articleDTO!.isContentTop! && widget.resourcesModel.isNotEmpty) - getImages(size, articleDTO!.isContentTop!), + if(widget.articleDTO.isContentTop! && resourcesModelToShow.isNotEmpty) + getImages(size, widget.articleDTO.isContentTop!), - if(!articleDTO!.isContentTop! && widget.resourcesModel.isNotEmpty) - getImages(size, articleDTO!.isContentTop!), - if(!articleDTO!.isContentTop!) + if(!widget.articleDTO.isContentTop! && resourcesModelToShow.isNotEmpty) + getImages(size, widget.articleDTO.isContentTop!), + if(!widget.articleDTO.isContentTop!) getContent(size, appContext), /*if(audioResourceModel != null) @@ -98,14 +101,14 @@ class _ArticlePageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - if(articleDTO!.isContentTop!) + if(widget.articleDTO.isContentTop!) getContent(size, appContext), - if(articleDTO!.isContentTop! && widget.resourcesModel.isNotEmpty) - getImages(size, articleDTO!.isContentTop!), + if(widget.articleDTO.isContentTop! && resourcesModelToShow.isNotEmpty) + getImages(size, widget.articleDTO.isContentTop!), - if(!articleDTO!.isContentTop! && widget.resourcesModel.isNotEmpty) - getImages(size, articleDTO!.isContentTop!), - if(!articleDTO!.isContentTop!) + if(!widget.articleDTO.isContentTop! && resourcesModelToShow.isNotEmpty) + getImages(size, widget.articleDTO.isContentTop!), + if(!widget.articleDTO.isContentTop!) getContent(size, appContext), ], ), @@ -115,14 +118,11 @@ class _ArticlePageState extends State { ), ); } - } else { - return const LoadingCommon(); - } } ), floatingActionButton: Padding( padding: const EdgeInsets.only(right: 0, top: 0), //size.height*0.1 - child: audioResourceModel != null && audioResourceModel!.source != null ? AudioPlayerFloatingContainer(file: audioFile, resourceURl: audioResourceModel!.source!, isAuto: articleDTO!.isReadAudioAuto!) : null, + child: audioResourceModel != null && audioResourceModel!.source != null ? AudioPlayerFloatingContainer(file: audioFile, resourceURl: audioResourceModel!.source!, isAuto: widget.articleDTO.isReadAudioAuto!) : null, ), floatingActionButtonLocation: FloatingActionButtonLocation.miniEndFloat, //miniEndTop ); @@ -147,9 +147,9 @@ class _ArticlePageState extends State { boxShadow: const [kDefaultShadow], ), child: SliderImagesWidget( - resources: widget.resourcesModel, + resources: resourcesModelToShow, height: size.height * 0.29, - contentsDTO: articleDTO!.contents!, + contentsDTO: widget.articleDTO.contents!, ) ) ) @@ -172,9 +172,9 @@ class _ArticlePageState extends State { boxShadow: const [kDefaultShadow], ), child: SliderImagesWidget( - resources: widget.resourcesModel, + resources: resourcesModelToShow, height: size.height * 0.29, - contentsDTO: articleDTO!.contents!, + contentsDTO: widget.articleDTO.contents!, ) ) ) @@ -206,7 +206,7 @@ class _ArticlePageState extends State { child: Padding( padding: const EdgeInsets.all(8.0), child: HtmlWidget( - TranslationHelper.get(articleDTO!.content, appContext.getContext()), + TranslationHelper.get(widget.articleDTO.content, appContext.getContext()), textStyle: TextStyle(fontSize: (appContext.getContext() as VisitAppContext).isMaximizeTextSize ? kArticleContentBiggerSize : kArticleContentSize), customStylesBuilder: (element) { @@ -243,7 +243,7 @@ class _ArticlePageState extends State { child: Padding( padding: const EdgeInsets.all(8.0), child: HtmlWidget( - TranslationHelper.get(articleDTO!.content, appContext.getContext()), + TranslationHelper.get(widget.articleDTO.content, appContext.getContext()), //textAlign: TextAlign.left, textStyle: TextStyle(fontSize: (appContext.getContext() as VisitAppContext).isMaximizeTextSize ? kArticleContentBiggerSize : kArticleContentSize, fontFamily: "Arial"), ), diff --git a/lib/Screens/Home/home_3.0.dart b/lib/Screens/Home/home_3.0.dart index 9f2c581..178c5d4 100644 --- a/lib/Screens/Home/home_3.0.dart +++ b/lib/Screens/Home/home_3.0.dart @@ -43,14 +43,6 @@ class _HomePage3State extends State with WidgetsBindingObserver { List alreadyDownloaded = []; late VisitAppContext visitAppContext; - final List colors = [ - Colors.blue, - Colors.green, - Colors.orange, - Colors.purple, - Colors.teal, - Colors.red, - ]; @override void initState() { @@ -128,8 +120,9 @@ class _HomePage3State extends State with WidgetsBindingObserver { boxShadow: [ BoxShadow( color: Colors.black26, - blurRadius: 3.5, - offset: Offset(0, -20), + spreadRadius: 0.35, + blurRadius: 2, + offset: Offset(0, -22), ), ], ), @@ -144,7 +137,7 @@ class _HomePage3State extends State with WidgetsBindingObserver { Opacity( opacity: 0.85, child: Image.network( - configurations[1].imageSource!, + configurations[0].imageSource!, fit: BoxFit.cover, ), ), @@ -190,9 +183,6 @@ class _HomePage3State extends State with WidgetsBindingObserver { return InkWell( onTap: () { - // Update context (it's in visit now, hero.. - - Navigator.of(context).push(MaterialPageRoute( builder: (context) => VisitPage(configuration: configurations[index], isAlreadyAllowed: visitAppContext.isScanBeaconAlreadyAllowed), @@ -200,38 +190,58 @@ class _HomePage3State extends State with WidgetsBindingObserver { }, child: Hero( tag: configurations[index].id!, - child: Container( - height: 200 + (index % 3) * 55, - decoration: BoxDecoration( - color: colors[index % colors.length], - borderRadius: BorderRadius.circular(16), - boxShadow: const [ - BoxShadow( - color: kMainGrey, - spreadRadius: 0.5, - blurRadius: 5, - offset: Offset(0, 1), // changes position of shadow - ), - ], - image: configurations[index].imageSource != null ? DecorationImage( - fit: BoxFit.cover, - opacity: 0.5, - image: NetworkImage( - configurations[index].imageSource!, - ), - ): null, - ), - child: Center( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: HtmlWidget( - cleanedTitle, - textStyle: const TextStyle(color: Colors.white, fontFamily: 'Roboto', fontSize: 20), - customStylesBuilder: (element) - { - return {'text-align': 'center', 'font-family': "Roboto", '-webkit-line-clamp': "2"}; - }, - ), + child: Material( + type: MaterialType.transparency, + child: Container( + height: 200 + (index % 3) * 55, + decoration: BoxDecoration( + color: kSecondGrey, + borderRadius: BorderRadius.circular(16), + boxShadow: const [ + BoxShadow( + color: kMainGrey, + spreadRadius: 0.5, + blurRadius: 5, + offset: Offset(0, 1), // changes position of shadow + ), + ], + image: configurations[index].imageSource != null ? DecorationImage( + fit: BoxFit.cover, + opacity: 0.5, + image: NetworkImage( + configurations[index].imageSource!, + ), + ): null, + ), + child: Stack( + children: [ + Center( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: HtmlWidget( + cleanedTitle, + textStyle: const TextStyle(color: Colors.white, fontFamily: 'Roboto', fontSize: 20), + customStylesBuilder: (element) + { + return {'text-align': 'center', 'font-family': "Roboto", '-webkit-line-clamp': "2"}; + }, + ), + ), + ), + Positioned( + bottom: 10, + right: 10, + child: Container( + width: 20, + height: 20, + decoration: const BoxDecoration( + //color: kMainColor, + shape: BoxShape.circle, + ), + child: const Icon(Icons.chevron_right, size: 18, color: Colors.white) + ), + ) + ], ), ), ), diff --git a/lib/Screens/Quizz/questions_list.dart b/lib/Screens/Quizz/questions_list.dart index 2744577..260503f 100644 --- a/lib/Screens/Quizz/questions_list.dart +++ b/lib/Screens/Quizz/questions_list.dart @@ -58,8 +58,8 @@ class _QuestionsListWidget extends State { MediaQuery.of(context).size.height * 0.85 : MediaQuery.of(context).size.height * 0.75 : widget.orientation == Orientation.portrait ? - MediaQuery.of(context).size.height : - MediaQuery.of(context).size.height, + MediaQuery.of(context).size.height * 0.88: + MediaQuery.of(context).size.height * 0.88, width: double.infinity, //color: Colors.orange, child: Stack( diff --git a/lib/Screens/Quizz/quizz_page.dart b/lib/Screens/Quizz/quizz_page.dart index bc33156..2915064 100644 --- a/lib/Screens/Quizz/quizz_page.dart +++ b/lib/Screens/Quizz/quizz_page.dart @@ -4,10 +4,9 @@ import 'dart:typed_data'; //import 'package:confetti/confetti.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; import 'package:manager_api_new/api.dart'; import 'package:mymuseum_visitapp/Components/CustomAppBar.dart'; -import 'package:mymuseum_visitapp/Components/Loading.dart'; -import 'package:mymuseum_visitapp/Components/loading_common.dart'; import 'package:mymuseum_visitapp/Components/rounded_button.dart'; import 'package:mymuseum_visitapp/Helpers/DatabaseHelper.dart'; import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; @@ -35,14 +34,12 @@ class QuizPage extends StatefulWidget { } class _QuizPageState extends State { - SectionDTO? sectionDTO; List resourcesModel = []; ResourceModel? audioResourceModel; final GlobalKey _scaffoldKey = GlobalKey(); late Uint8List audiobytes; late VisitAppContext visitAppContext; - QuizDTO? quizDTO; List _questionsSubDTO = []; //ConfettiController? _controllerCenter; int currentIndex = 1; @@ -59,6 +56,10 @@ class _QuizPageState extends State { //_controllerCenter = ConfettiController(duration: const Duration(seconds: 10)); //_controllerCenter!.play(); + if(widget.quizDTO.questions != null) { + _questionsSubDTO = QuestionSubDTO().fromJSON(widget.quizDTO.questions!); + } + super.initState(); } @@ -67,10 +68,9 @@ class _QuizPageState extends State { visitAppContext.isContentCurrentlyShown = false; currentIndex = 1; //_controllerCenter!.dispose(); - if(quizDTO != null) { - if(quizDTO!.questions != null) { - _questionsSubDTO = QuestionSubDTO().fromJSON(quizDTO!.questions!); - } + + if(widget.quizDTO.questions != null) { + _questionsSubDTO = QuestionSubDTO().fromJSON(widget.quizDTO.questions!); } super.dispose(); @@ -84,136 +84,187 @@ class _QuizPageState extends State { return Scaffold( key: _scaffoldKey, - appBar: CustomAppBar( - title: sectionDTO != null ? TranslationHelper.get(sectionDTO!.title, visitAppContext) : "", + /*appBar: CustomAppBar( + title: TranslationHelper.get(widget.quizDTO.title, visitAppContext), isHomeButton: false, - ), - body: OrientationBuilder( - builder: (context, orientation) { - if(quizDTO != null && sectionDTO != null) { - - if(showResult) { - var goodResponses = 0; - for (var question in _questionsSubDTO) { - if(question.chosen == question.responsesSubDTO!.indexWhere((response) => response.isGood!)) { - goodResponses +=1; - } - } - log("goodResponses =" + goodResponses.toString()); - List levelToShow = []; - var test = goodResponses/quizDTO!.questions!.length; - - if((0 == test || test < 0.25) && quizDTO!.badLevel != null) { - levelToShow = quizDTO!.badLevel!; - } - if((test>=0.25 && test < 0.5) && quizDTO!.mediumLevel != null) { - levelToShow = quizDTO!.mediumLevel!; - } - if((test>=0.5 && test < 0.75) && quizDTO!.goodLevel != null) { - levelToShow = quizDTO!.goodLevel!; - } - if((test>=0.75 && test <= 1) && quizDTO!.greatLevel != null) { - levelToShow = quizDTO!.greatLevel!; - } - - return SizedBox( - width: double.infinity, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - /*Center( - child: SizedBox( - width: 5, - height: 5, - child: ConfettiWidget( - confettiController: _controllerCenter!, - blastDirectionality: BlastDirectionality.explosive, - shouldLoop: false, // start again as soon as the animation is finished - colors: const [ - kMainColor, - kSecondColor, - kConfigurationColor, - kMainColor1 - //Colors.pink, - //Colors.orange, - //Colors.purple - ], // manually specify the colors to be used - createParticlePath: drawPath, // define a custom shape/path. - ), - ), - ),*/ - if (orientation == Orientation.portrait) - Column( - children: [ - if (!showResponses && levelToShow.firstWhere((label) => label.language == visitAppContext.language).resource?.url != null) // TODO SUPPORT OTHER THAN IMAGES - resultImage(visitAppContext, size, levelToShow, orientation), - if(!showResponses) - // TEXT BOX WITH MAIN SCORE - Text('$goodResponses/${quizDTO!.questions!.length}', textAlign: TextAlign.center, style: TextStyle(fontSize: kIsWeb ? (showResponses ? 60 : 100) : 75, color: kBackgroundSecondGrey)), - ], - ), - - if (orientation == Orientation.landscape) - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - if(!showResponses) - // TEXT BOX WITH MAIN SCORE - Text('$goodResponses/${quizDTO!.questions!.length}', textAlign: TextAlign.center, style: TextStyle(fontSize: kIsWeb ? (showResponses ? 60 : 100) : 75, color: kBackgroundSecondGrey)), - if (!showResponses && levelToShow.firstWhere((label) => label.language == visitAppContext.language).resource?.url != null) - resultImage(visitAppContext, size, levelToShow, orientation), - ], - ), - - if(!showResponses) - // TEXT BOX WITH LEVEL TEXT RESULT - resultText(size, levelToShow, appContext), - if(showResponses) - QuestionsListWidget( - questionsSubDTO: _questionsSubDTO, - isShowResponse: true, - onShowResponse: () {}, - orientation: orientation, - ), - // RESPONSE BOX - //ShowReponsesWidget(questionsSubDTO: _questionsSubDTO), - - if(orientation == Orientation.portrait && !showResponses) - // Buttons - Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.center, - children: resultButtons(size, orientation, visitAppContext!), - ), - if(orientation == Orientation.landscape && !showResponses) - // Buttons - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.center, - children: resultButtons(size, orientation, visitAppContext!), - ), + ),*/ + body: Column( + children: [ + Stack( + fit: StackFit.passthrough, + children: [ + Container( + height: size.height * 0.12, + width: size.width, + decoration: BoxDecoration( + boxShadow: const [ + BoxShadow( + color: kMainGrey, + spreadRadius: 0.5, + blurRadius: 2, + offset: Offset(0, 1), // changes position of shadow + ), ], + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + ), + image: widget.quizDTO.imageSource != null ? DecorationImage( + fit: BoxFit.cover, + opacity: 0.65, + image: NetworkImage( + widget.quizDTO.imageSource!, + ), + ): null, ), - ); + ), + Positioned( + top: 35, + left: 10, + child: SizedBox( + width: 50, + height: 50, + child: InkWell( + onTap: () { + setState(() { + Navigator.of(context).pop(); + }); + }, + child: Container( + decoration: const BoxDecoration( + color: kMainColor, + shape: BoxShape.circle, + ), + child: const Icon(Icons.arrow_back, size: 23, color: Colors.white) + ), + ) + ), + ), + ], + ), + OrientationBuilder( + builder: (context, orientation) { + if(showResult) { + var goodResponses = 0; + for (var question in _questionsSubDTO) { + if(question.chosen == question.responsesSubDTO!.indexWhere((response) => response.isGood!)) { + goodResponses +=1; + } + } + log("goodResponses =" + goodResponses.toString()); + List levelToShow = []; + var test = goodResponses/widget.quizDTO.questions!.length; - } else { - return QuestionsListWidget( - isShowResponse: false, - questionsSubDTO: _questionsSubDTO, - onShowResponse: () { - setState(() { - showResult = true; - }); - }, - orientation: orientation, - ); + if((0 == test || test < 0.25) && widget.quizDTO.badLevel != null) { + levelToShow = widget.quizDTO.badLevel!; + } + if((test>=0.25 && test < 0.5) && widget.quizDTO.mediumLevel != null) { + levelToShow = widget.quizDTO.mediumLevel!; + } + if((test>=0.5 && test < 0.75) && widget.quizDTO.goodLevel != null) { + levelToShow = widget.quizDTO.goodLevel!; + } + if((test>=0.75 && test <= 1) && widget.quizDTO.greatLevel != null) { + levelToShow = widget.quizDTO.greatLevel!; + } + + return SizedBox( + width: double.infinity, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + /*Center( + child: SizedBox( + width: 5, + height: 5, + child: ConfettiWidget( + confettiController: _controllerCenter!, + blastDirectionality: BlastDirectionality.explosive, + shouldLoop: false, // start again as soon as the animation is finished + colors: const [ + kMainColor, + kSecondColor, + kConfigurationColor, + kMainColor1 + //Colors.pink, + //Colors.orange, + //Colors.purple + ], // manually specify the colors to be used + createParticlePath: drawPath, // define a custom shape/path. + ), + ), + ),*/ + if (orientation == Orientation.portrait) + Column( + children: [ + if (!showResponses && levelToShow.firstWhere((label) => label.language == visitAppContext.language).resource?.url != null) // TODO SUPPORT OTHER THAN IMAGES + resultImage(visitAppContext, size, levelToShow, orientation), + if(!showResponses) + // TEXT BOX WITH MAIN SCORE + Text('$goodResponses/${widget.quizDTO.questions!.length}', textAlign: TextAlign.center, style: TextStyle(fontSize: kIsWeb ? (showResponses ? 60 : 100) : 75, color: kBackgroundSecondGrey)), + ], + ), + + if (orientation == Orientation.landscape) + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + if(!showResponses) + // TEXT BOX WITH MAIN SCORE + Text('$goodResponses/${widget.quizDTO.questions!.length}', textAlign: TextAlign.center, style: TextStyle(fontSize: kIsWeb ? (showResponses ? 60 : 100) : 75, color: kBackgroundSecondGrey)), + if (!showResponses && levelToShow.firstWhere((label) => label.language == visitAppContext.language).resource?.url != null) + resultImage(visitAppContext, size, levelToShow, orientation), + ], + ), + + if(!showResponses) + // TEXT BOX WITH LEVEL TEXT RESULT + resultText(size, levelToShow, appContext), + if(showResponses) + QuestionsListWidget( + questionsSubDTO: _questionsSubDTO, + isShowResponse: true, + onShowResponse: () {}, + orientation: orientation, + ), + // RESPONSE BOX + //ShowReponsesWidget(questionsSubDTO: _questionsSubDTO), + + if(orientation == Orientation.portrait && !showResponses) + // Buttons + Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, + children: resultButtons(size, orientation, visitAppContext), + ), + if(orientation == Orientation.landscape && !showResponses) + // Buttons + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, + children: resultButtons(size, orientation, visitAppContext), + ), + ], + ), + ); + + } else { + return QuestionsListWidget( + isShowResponse: false, + questionsSubDTO: _questionsSubDTO, + onShowResponse: () { + setState(() { + showResult = true; + }); + }, + orientation: orientation, + ); + } } - } else { - return const LoadingCommon(); - } - } + ), + ], ), floatingActionButton: showResponses ? FloatingActionButton( onPressed: () { @@ -221,7 +272,7 @@ class _QuizPageState extends State { showResult = false; showResponses = false; currentIndex = 1; - _questionsSubDTO = QuestionSubDTO().fromJSON(quizDTO!.questions!); + _questionsSubDTO = QuestionSubDTO().fromJSON(widget.quizDTO.questions!); }); }, backgroundColor: kBackgroundSecondGrey, @@ -378,7 +429,14 @@ class _QuizPageState extends State { child: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(15.0), - child: Text(TranslationHelper.getWithResource(levelToShow, appContext.getContext() as VisitAppContext), textAlign: TextAlign.center, style: TextStyle(fontSize: kIsWeb ? kDescriptionSize : kDescriptionSize)), + child: HtmlWidget( + TranslationHelper.getWithResource(levelToShow, appContext.getContext() as VisitAppContext),textStyle: const TextStyle(fontFamily: 'Roboto', fontSize: 20), + customStylesBuilder: (element) + { + return {'text-align': 'center', 'font-family': "Roboto", '-webkit-line-clamp': "2"}; + } + ) + //Text(, textAlign: TextAlign.center, style: TextStyle(fontSize: kIsWeb ? kDescriptionSize : kDescriptionSize)), ), ), ), @@ -404,7 +462,7 @@ class _QuizPageState extends State { showResult = false; showResponses = false; currentIndex = 1; - _questionsSubDTO = QuestionSubDTO().fromJSON(quizDTO!.questions!); + _questionsSubDTO = QuestionSubDTO().fromJSON(widget.quizDTO.questions!); }); }, fontSize: 18, diff --git a/lib/Screens/Scanner/scanner_old.dart b/lib/Screens/Scanner/scanner_old.dart index f83fa57..a5350a7 100644 --- a/lib/Screens/Scanner/scanner_old.dart +++ b/lib/Screens/Scanner/scanner_old.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:manager_api_new/api.dart'; import 'package:mymuseum_visitapp/Components/CustomAppBar.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; import 'package:mymuseum_visitapp/Screens/Article/article_page.dart'; @@ -181,7 +182,7 @@ class _ScannerPageState extends State { context, MaterialPageRoute( builder: (context) { - return SectionPage(sectionId: code, visitAppContextIn: VisitAppContext()); // will not work.. + return SectionPage(configuration: ConfigurationDTO(), rawSection: null, sectionId: code, visitAppContextIn: VisitAppContext()); // will not work.. }, ), ); diff --git a/lib/Screens/Visit/components/body.dart b/lib/Screens/Visit/components/body.dart index 4f2bdac..6ba867c 100644 --- a/lib/Screens/Visit/components/body.dart +++ b/lib/Screens/Visit/components/body.dart @@ -1,15 +1,15 @@ +import 'dart:convert'; + import 'package:diacritic/diacritic.dart'; import 'package:flutter/material.dart'; import 'package:manager_api_new/api.dart'; +import 'package:mymuseum_visitapp/Components/SlideFromRouteRight.dart'; import 'package:mymuseum_visitapp/Components/loading_common.dart'; import 'package:mymuseum_visitapp/Components/SearchBox.dart'; import 'package:mymuseum_visitapp/Components/SearchNumberBox.dart'; import 'package:mymuseum_visitapp/Helpers/DatabaseHelper.dart'; import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; -import 'package:mymuseum_visitapp/Screens/Article/article_page.dart'; -import 'package:mymuseum_visitapp/Screens/Home/home_3.0.dart'; -import 'package:mymuseum_visitapp/Screens/Quizz/quizz_page.dart'; import 'package:mymuseum_visitapp/Screens/section_page.dart'; import 'package:mymuseum_visitapp/Services/apiService.dart'; import 'package:mymuseum_visitapp/app_context.dart'; @@ -28,11 +28,15 @@ class Body extends StatefulWidget { } class _BodyState extends State { - List sections = []; + late List sections; + late List _allSections; + late List rawSections; List sectionsToDisplay = []; String? searchValue; int? searchNumberValue; + final ValueNotifier> filteredSections = ValueNotifier([]); + @override Widget build(BuildContext context) { final appContext = Provider.of(context); @@ -83,21 +87,21 @@ class _BodyState extends State { height: 50, child: InkWell( onTap: () { - setState(() { + //setState(() { visitAppContext.configuration = null; visitAppContext.isScanningBeacons = false; Navigator.of(context).pop(); /*Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute( builder: (context) => const HomePage3(), ),(route) => false);*/ - }); + //}); }, child: Container( decoration: const BoxDecoration( color: kMainColor, shape: BoxShape.circle, ), - child: const Icon(Icons.chevron_left, size: 28, color: Colors.white) + child: const Icon(Icons.arrow_back, size: 23, color: Colors.white) ), ) ), @@ -108,17 +112,27 @@ class _BodyState extends State { Row( children: [ SearchBox(onChanged: (value) { - setState(() { + searchValue = value?.trim(); + applyFilters(visitAppContext); + /*setState(() { if(value != null && value != "") { searchValue = value; } else { searchValue = null; } - }); + });*/ }), Expanded( child: SearchNumberBox(onChanged: (value) { - setState(() { + if (value != null && value.isNotEmpty) { + searchNumberValue = int.tryParse(value); + } else { + searchNumberValue = null; + } + FocusScope.of(context).unfocus(); + applyFilters(visitAppContext); + } + /*setState(() { if(value != null && value != "") { searchNumberValue = int.parse(value); } else { @@ -126,7 +140,8 @@ class _BodyState extends State { FocusScope.of(context).unfocus(); } }); - }), + }*/ + ), ), ], ), @@ -138,10 +153,18 @@ class _BodyState extends State { Container( margin: const EdgeInsets.only(top: 0), decoration: const BoxDecoration( + boxShadow: [ + BoxShadow( + color: kMainGrey, + spreadRadius: 0.5, + blurRadius: 2, + offset: Offset(0, 1), // changes position of shadow + ), + ], color: kBackgroundColor, borderRadius: BorderRadius.only( - topLeft: Radius.circular(40), - topRight: Radius.circular(40), + topLeft: Radius.circular(30), + topRight: Radius.circular(30), ), ), ), @@ -159,25 +182,31 @@ class _BodyState extends State { // Force refresh if online setState(() {}); } }, - child: ListView.builder( - itemCount: sectionsToDisplay.length, - itemBuilder: (context, index) => SectionCard( - itemCount: sectionsToDisplay.length, - itemIndex: index, - sectionDTO: sectionsToDisplay[index], - press: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => SectionPage( - visitAppContextIn: appContext.getContext(), - sectionId: sectionsToDisplay[index].id!, - ), + child: ValueListenableBuilder>( + valueListenable: filteredSections, + builder: (context, value, child) { + return ListView.builder( + itemCount: value.length, + itemBuilder: (context, index) => SectionCard( + configuration: widget.configuration, + itemCount: value.length, + itemIndex: index, + sectionDTO: value[index], + press: () { + Navigator.push( + context, + SlideFromRightRoute(page: SectionPage( + configuration: widget.configuration, + rawSection: rawSections[index], + visitAppContextIn: appContext.getContext(), + sectionId: value[index].id!, + )), + ); + }, ), ); - }, - ), - ), + } + ) ), ); } else if (snapshot.connectionState == ConnectionState.none) { @@ -212,21 +241,32 @@ class _BodyState extends State { else { // ONLINE - List? sectionsDownloaded = await ApiService.getAllSections(visitAppContext.clientAPI, widget.configuration.id!); + List? sectionsDownloaded = await ApiService.getAllSections(visitAppContext.clientAPI, widget.configuration.id!); + rawSections = jsonDecode(jsonEncode(sectionsDownloaded)); + var rawToSection = jsonDecode(jsonEncode(rawSections)).map((json) => SectionDTO.fromJson(json)).toList(); + List sectionList = rawToSection.whereType().toList(); + //print(sectionsDownloaded); - if(sectionsDownloaded!.isNotEmpty) { - sections = sectionsDownloaded.toList(); + if(sectionList.isNotEmpty) { + sections = sectionList.toList(); //print(sections); } } sections = sections.where((s) => s.configurationId == widget.configuration.id!).toList(); sections.sort((a,b) => a.order!.compareTo(b.order!)); - sectionsToDisplay = sections; + //sectionsToDisplay = sections; + try { + _allSections = sections; - visitAppContext.currentSections = sectionsToDisplay; + //visitAppContext.currentSections = sectionsToDisplay; - if(searchValue != '' && searchValue != null) { + applyFilters(visitAppContext); + } catch(e) { + print(e); + } + + /*if(searchValue != '' && searchValue != null) { sectionsToDisplay = sections.where((s) => removeDiacritics(TranslationHelper.get(s.title, appContext.getContext()).toLowerCase()).contains(removeDiacritics(searchValue.toString().toLowerCase()))).toList(); } else { if(searchNumberValue != null) { @@ -234,6 +274,22 @@ class _BodyState extends State { } else { sectionsToDisplay = sections; } - } + }*/ } + + void applyFilters(VisitAppContext visitAppContext) { + List result = _allSections; + + if (searchValue != null && searchValue!.isNotEmpty) { + result = result.where((s) => + removeDiacritics(TranslationHelper.get(s.title, visitAppContext).toLowerCase()) + .contains(removeDiacritics(searchValue!.toLowerCase())) + ).toList(); + } else if (searchNumberValue != null) { + result = result.where((s) => s.order! + 1 == searchNumberValue).toList(); + } + + filteredSections.value = result; + } + } diff --git a/lib/Screens/Visit/components/section_card.dart b/lib/Screens/Visit/components/section_card.dart index e9f399d..e657f8d 100644 --- a/lib/Screens/Visit/components/section_card.dart +++ b/lib/Screens/Visit/components/section_card.dart @@ -16,12 +16,14 @@ import 'package:provider/provider.dart'; class SectionCard extends StatelessWidget { const SectionCard({ Key? key, + required this.configuration, required this.itemIndex, required this.itemCount, required this.sectionDTO, required this.press, }) : super(key: key); + final ConfigurationDTO configuration; final int itemIndex; final int itemCount; final SectionDTO sectionDTO; @@ -33,7 +35,7 @@ class SectionCard extends StatelessWidget { Size size = MediaQuery.of(context).size; final appContext = Provider.of(context); VisitAppContext visitAppContext = appContext.getContext(); - bool isOffline = (appContext.getContext() as VisitAppContext).configuration?.isOffline! ?? false; + bool isOffline = configuration.isOffline! ?? false; return Container( margin: EdgeInsets.only( @@ -69,7 +71,7 @@ class SectionCard extends StatelessWidget { ), ), ), - if(sectionDTO.imageId != null && (appContext.getContext() as VisitAppContext).configuration != null) + if(sectionDTO.imageId != null ) // && (appContext.getContext() as VisitAppContext).configuration != null // section main image Positioned( top: kDefaultPadding +4.0, @@ -82,7 +84,7 @@ class SectionCard extends StatelessWidget { // image is square but we add extra 20 + 20 padding thats why width is 200 width: size.width*0.5, child: FutureBuilder( - future: ApiService.getResource(appContext, (appContext.getContext() as VisitAppContext).configuration!, sectionDTO.imageId!), + future: ApiService.getResource(appContext, configuration, sectionDTO.imageId!), builder: (context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { return ClipRRect( @@ -142,10 +144,6 @@ class SectionCard extends StatelessWidget { Padding( padding: const EdgeInsets.symmetric(horizontal: kDefaultPadding), child: HtmlWidget(TranslationHelper.get(sectionDTO.title, appContext.getContext())) - /*Text( - TranslationHelper.get(sectionDTO.title, appContext.getContext()), - style: Theme.of(context).textTheme.button, - )*/, ), // it use the available space const Spacer(), @@ -171,6 +169,16 @@ class SectionCard extends StatelessWidget { ), ), ), + const Positioned( + right: -4, + top: (136/2)-18, + bottom: 25, + child: SizedBox( + width: 18, + height: 18, + child: Icon(Icons.chevron_right, size: 15, color: Colors.white) + ), + ) ], ), ), diff --git a/lib/Screens/Visit/visit.dart b/lib/Screens/Visit/visit.dart index 46ef418..e6ed6f4 100644 --- a/lib/Screens/Visit/visit.dart +++ b/lib/Screens/Visit/visit.dart @@ -74,9 +74,12 @@ class _VisitPageState extends State with WidgetsBindingObserver { listeningState(); } - /*WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) { final appContext = Provider.of(context, listen: false); - });*/ + VisitAppContext visitAppContext = appContext.getContext(); + visitAppContext.configuration = widget.configuration; + appContext.setContext(visitAppContext); + }); //listeningState(); } @@ -296,6 +299,8 @@ class _VisitPageState extends State with WidgetsBindingObserver { context, MaterialPageRoute( builder: (context) => SectionPage( + configuration: widget.configuration, + rawSection: null, visitAppContextIn: visitAppContext, sectionId: beaconSection!.sectionId!, ), diff --git a/lib/Screens/section_page.dart b/lib/Screens/section_page.dart index cfc3099..4ec8c3a 100644 --- a/lib/Screens/section_page.dart +++ b/lib/Screens/section_page.dart @@ -27,9 +27,11 @@ import 'package:mymuseum_visitapp/constants.dart'; import 'package:provider/provider.dart'; class SectionPage extends StatefulWidget { - const SectionPage({Key? key, required this.visitAppContextIn, required this.sectionId}) : super(key: key); + const SectionPage({Key? key, required this.rawSection, required this.visitAppContextIn, required this.configuration, required this.sectionId}) : super(key: key); + final Object? rawSection; final String sectionId; + final ConfigurationDTO configuration; final VisitAppContext visitAppContextIn; @override @@ -61,24 +63,27 @@ class _SectionPageState extends State { Size size = MediaQuery.of(context).size; visitAppContext = appContext.getContext(); + var test = SectionDTO.fromJson(jsonDecode(jsonEncode(widget.rawSection))); + return Scaffold( key: _scaffoldKey, - appBar: CustomAppBar( + appBar: test!.type != SectionType.Quiz && test.type != SectionType.Article ? CustomAppBar( title: sectionDTO != null ? TranslationHelper.get(sectionDTO!.title, visitAppContext) : "", isHomeButton: false, - ), + ) : null, body: OrientationBuilder( builder: (context, orientation) { return FutureBuilder( future: getSectionDetail(appContext, visitAppContext.clientAPI, widget.sectionId), builder: (context, AsyncSnapshot snapshot) { - if(sectionDTO != null) { + var sectionResult = snapshot.data; + if(sectionDTO != null && sectionResult != null) { switch(sectionDTO!.type) { case SectionType.Article: - ArticleDTO articleDTO = ArticleDTO.fromJson(rawSectionData)!; + ArticleDTO articleDTO = ArticleDTO.fromJson(sectionResult)!; return ArticlePage(visitAppContextIn: widget.visitAppContextIn, articleDTO: articleDTO, resourcesModel: resourcesModel); case SectionType.Quiz: - QuizDTO quizDTO = QuizDTO.fromJson(rawSectionData)!; + QuizDTO quizDTO = QuizDTO.fromJson(sectionResult)!; return QuizPage(visitAppContextIn: widget.visitAppContextIn, quizDTO: quizDTO, resourcesModel: resourcesModel); default: return const Center(child: Text("Unsupported type")); @@ -95,8 +100,8 @@ class _SectionPageState extends State { Future getSectionDetail(AppContext appContext, Client client, String sectionId) async { try { - if(sectionDTO == null) { - bool isConfigOffline = (appContext.getContext() as VisitAppContext).configuration!.isOffline!; + bool isConfigOffline = widget.configuration.isOffline!; + if(widget.rawSection == null) { if(isConfigOffline) { // OFFLINE @@ -124,48 +129,88 @@ class _SectionPageState extends State { sectionDTO = sectionOnline; } - switch(sectionDTO!.type) - { - case SectionType.Quiz: - QuizDTO? quizDTO = QuizDTO.fromJson(jsonDecode(rawSectionData)); - if(quizDTO != null) { - quizDTO.questions!.sort((a, b) => a.order!.compareTo(b.order!)); - if(quizDTO.questions != null && quizDTO.questions!.isNotEmpty) { - quizDTO.questions!.sort((a, b) => a.order!.compareTo(b.order!)); - for (var question in quizDTO.questions!) { - if(isConfigOffline) - { - // OFFLINE - if(question.imageBackgroundResourceId != null) { - List> ressourceQuizz = await DatabaseHelper.instance.queryWithColumnId(DatabaseTableType.resources, question.imageBackgroundResourceId!); - if(ressourceQuizz.isNotEmpty) { - resourcesModel.add(DatabaseHelper.instance.getResourceFromDB(ressourceQuizz.first)); - } else { - print("EMPTY resourcesModel - second"); - } - } - } - else - { - // ONLINE - if(question.imageBackgroundResourceId != null) { - resourcesModel.add(ResourceModel(id: question.imageBackgroundResourceId, source: question.imageBackgroundResourceUrl, type: ResourceType.Image)); - } - } - } - } - } - break; - default: - break; - } - /*setState(() { //print(sectionDTO!.title); });*/ } else { - return null; // TODO return local list.. + rawSectionData = widget.rawSection; + sectionDTO = SectionDTO.fromJson(jsonDecode(jsonEncode(rawSectionData))); } + + switch(sectionDTO!.type) + { + case SectionType.Quiz: + QuizDTO? quizDTO = QuizDTO.fromJson(rawSectionData); + if(quizDTO != null) { + quizDTO.questions!.sort((a, b) => a.order!.compareTo(b.order!)); + if(quizDTO.questions != null && quizDTO.questions!.isNotEmpty) { + quizDTO.questions!.sort((a, b) => a.order!.compareTo(b.order!)); + for (var question in quizDTO.questions!) { + if(isConfigOffline) + { + // OFFLINE + if(question.imageBackgroundResourceId != null) { + List> ressourceQuizz = await DatabaseHelper.instance.queryWithColumnId(DatabaseTableType.resources, question.imageBackgroundResourceId!); + if(ressourceQuizz.isNotEmpty) { + resourcesModel.add(DatabaseHelper.instance.getResourceFromDB(ressourceQuizz.first)); + } else { + print("EMPTY resourcesModel - second"); + } + } + } + else + { + // ONLINE + if(question.imageBackgroundResourceId != null) { + resourcesModel.add(ResourceModel(id: question.imageBackgroundResourceId, source: question.imageBackgroundResourceUrl, type: ResourceType.Image)); + } + } + } + } + } + break; + case SectionType.Article: + ArticleDTO articleDTO = ArticleDTO.fromJson(rawSectionData)!; + var audioToDownload = articleDTO.audioIds!.firstWhere((a) => a.language == visitAppContext.language!); + if(audioToDownload.value != null) { + if(isConfigOffline) + { + // OFFLINE + List> ressourceArticle = await DatabaseHelper.instance.queryWithColumnId(DatabaseTableType.resources, audioToDownload.value!); + if(ressourceArticle.isNotEmpty) { + resourcesModel.add(DatabaseHelper.instance.getResourceFromDB(ressourceArticle.first)); + } else { + print("EMPTY resourcesModel - second"); + } + } + else + { + // ONLINE + ResourceDTO? resourceDTO = await client.resourceApi!.resourceGetDetail(audioToDownload.value!); + if(resourceDTO != null && resourceDTO.url != null) { + // ONLINE + //ResourceModel? resourceAudioOnline = await ApiService.downloadAudio(client, resourceDTO.url!, resourceDTO.id!); + ResourceModel resourceAudioOnline = ResourceModel(); + resourceAudioOnline.id = resourceDTO.id; + resourceAudioOnline.source = resourceDTO.url; + resourceAudioOnline.type = ResourceType.Audio; + + resourcesModel.add(resourceAudioOnline); + + /*Uint8List base64String = base64Decode(resourceAudioOnline.path!); // GET FROM FILE + audiobytes = base64String;*/ + } else { + print("EMPTY resourcesModel online - audio"); + } + } + } + break; + default: + break; + } + + return rawSectionData; + } catch (e) { print(e); print("IN CATCH"); diff --git a/lib/Services/apiService.dart b/lib/Services/apiService.dart index 610b9cf..de751b8 100644 --- a/lib/Services/apiService.dart +++ b/lib/Services/apiService.dart @@ -40,12 +40,13 @@ class ApiService { } } - static Future?> getAllSections(Client client, String configurationId) async { + static Future?> getAllSections(Client client, String configurationId) async { try { bool isOnline = await hasNetwork(); if(isOnline) { - List? sections = await client.sectionApi!.sectionGetFromConfiguration(configurationId); - return sections; + final rawList = await client.sectionApi!.sectionGetFromConfigurationDetail(configurationId); + var sections = rawList.map((json) => SectionDTO.fromJson(json)).toList(); + return rawList ?? []; } else { return []; // TODO return local list.. } @@ -67,7 +68,7 @@ class ApiService { } } catch (e) { print(e); - print("getAllSections IN CATCH"); + print("getAllBeacons IN CATCH"); return []; } } @@ -162,7 +163,7 @@ class ApiService { } static Future getResource(AppContext appContext, ConfigurationDTO configurationDTO, String imageId) async { - if((appContext.getContext() as VisitAppContext).configuration == null || (appContext.getContext() as VisitAppContext).configuration!.isOffline!) + if(configurationDTO.isOffline!) { Directory? appDocumentsDirectory = Platform.isIOS ? await getApplicationDocumentsDirectory() : await getDownloadsDirectory(); String localPath = appDocumentsDirectory!.path; diff --git a/manager_api_new/lib/api/section_api.dart b/manager_api_new/lib/api/section_api.dart index 47e8877..17e08a2 100644 --- a/manager_api_new/lib/api/section_api.dart +++ b/manager_api_new/lib/api/section_api.dart @@ -590,7 +590,7 @@ class SectionApi { /// Parameters: /// /// * [String] id (required): - Future?> sectionGetFromConfigurationDetail( + Future sectionGetFromConfigurationDetail( String id, ) async { final response = await sectionGetFromConfigurationDetailWithHttpInfo( @@ -604,11 +604,13 @@ class SectionApi { // FormatException when trying to decode an empty string. if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { - final responseBody = await _decodeBodyBytes(response); + /*final responseBody = await _decodeBodyBytes(response); return (await apiClient.deserializeAsync(responseBody, 'List') as List) .cast() - .toList(growable: false); + .toList(growable: false);*/ + final decoded = json.decode(await _decodeBodyBytes(response)); + return decoded; // <- Ce sera un Map ou une List selon le JSON } return null; }