From c2c0293ace8812b23489bbb1a6e9f273dc326cb6 Mon Sep 17 00:00:00 2001 From: Fransolet Thomas Date: Fri, 24 Feb 2023 18:00:56 +0100 Subject: [PATCH] Handle Quizz (Quizz page + questions list + show response etc) + other small fixes --- lib/Components/ScannerDialog.dart | 33 +- lib/Components/rounded_button.dart | 74 ++++ lib/Models/ResponseSubDTO.dart | 54 +++ lib/Screens/Quizz/drawPath.dart | 25 ++ lib/Screens/Quizz/questions_list.dart | 277 +++++++++++++++ lib/Screens/Quizz/quizz_page.dart | 475 +++++++++++++++++++++++++- lib/Screens/Visit/visit.dart | 2 +- lib/constants.dart | 2 + 8 files changed, 924 insertions(+), 18 deletions(-) create mode 100644 lib/Components/rounded_button.dart create mode 100644 lib/Models/ResponseSubDTO.dart create mode 100644 lib/Screens/Quizz/drawPath.dart create mode 100644 lib/Screens/Quizz/questions_list.dart diff --git a/lib/Components/ScannerDialog.dart b/lib/Components/ScannerDialog.dart index a37215c..ee15ff6 100644 --- a/lib/Components/ScannerDialog.dart +++ b/lib/Components/ScannerDialog.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; import 'dart:io'; import 'package:flutter/foundation.dart'; +import 'package:manager_api/api.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/Quizz/quizz_page.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/constants.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; @@ -152,14 +154,29 @@ class _ScannerDialogState extends State { Navigator.of(context).pop(); } else { - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) { - return ArticlePage(visitAppContextIn: visitAppContext, articleId: code); - }, - ), - ); + SectionDTO section = visitAppContext.currentSections!.firstWhere((cs) => cs!.id == code)!; + switch(section.type) { + case SectionType.Article: + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) { + return ArticlePage(visitAppContextIn: visitAppContext, articleId: code); + }, + ), + ); + break; + case SectionType.Quizz: + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) { + return QuizzPage(visitAppContextIn: visitAppContext, sectionId: code); + }, + ), + ); + break; + } } } }); diff --git a/lib/Components/rounded_button.dart b/lib/Components/rounded_button.dart new file mode 100644 index 0000000..f27f8c2 --- /dev/null +++ b/lib/Components/rounded_button.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:mymuseum_visitapp/constants.dart'; + +class RoundedButton extends StatelessWidget { + final String? text; + final Function? press; + final IconData? icon; + final Color? color, textColor; + final double? fontSize; + final double? vertical; + final double? horizontal; + + const RoundedButton({ + Key? key, + this.text, + this.press, + this.icon, + this.color = kMainColor, + this.textColor = Colors.white, + this.fontSize, + this.vertical, + this.horizontal + }) : super(key: key); + + @override + Widget build(BuildContext context) { + //Size size = MediaQuery.of(context).size; + return TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.resolveWith((states) => EdgeInsets.symmetric(vertical: this.vertical != null ? this.vertical! : 25, horizontal: this.horizontal != null ? this.horizontal! : (icon == null ? 85 : 30))), + backgroundColor: MaterialStateColor.resolveWith((states) => color!), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30.0), + ) + ) + ), + + onPressed: () => { + press!() + }, + child: getValue(icon) + ); + } + + getValue(icon) { + if (icon != null) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Center( + child: Text( + text!, + style: new TextStyle(color: textColor, fontSize: fontSize, fontWeight: FontWeight.w400), + ), + ), + SizedBox( + width: 10 + ), + Icon( + icon, + color: textColor, + size: fontSize! + 8, + ) + ], + ); + } else { + return Text( + text!, + style: new TextStyle(color: textColor, fontSize: fontSize, fontWeight: FontWeight.w400), + ); + } + } +} \ No newline at end of file diff --git a/lib/Models/ResponseSubDTO.dart b/lib/Models/ResponseSubDTO.dart new file mode 100644 index 0000000..4f92d59 --- /dev/null +++ b/lib/Models/ResponseSubDTO.dart @@ -0,0 +1,54 @@ +import 'package:manager_api/api.dart'; + +class ResponseSubDTO { + List? label; + bool? isGood; + int? order; + + ResponseSubDTO({this.label, this.isGood, this.order}); + + + //ResponseSubDTO(List label, bool isGood, int order) : super(); + + List fromJSON(List responsesDTO) { + List responsesSubDTO = []; + for(ResponseDTO responseDTO in responsesDTO) + { + responsesSubDTO.add(ResponseSubDTO( + label: responseDTO.label, + isGood: responseDTO.isGood, + order: responseDTO.order, + )); + } + return responsesSubDTO; + } +} + +class QuestionSubDTO { + List? label; + List? responsesSubDTO; + int? chosen; + String? resourceId; + String? source_; + int? order; + + QuestionSubDTO({this.label, this.responsesSubDTO, this.chosen, this.resourceId, this.source_, this.order}); + + List fromJSON(List questionsDTO) { + List questionSubDTO = []; + for(QuestionDTO questionDTO in questionsDTO) + { + questionSubDTO.add(QuestionSubDTO( + chosen: null, + label: questionDTO.label, + responsesSubDTO: ResponseSubDTO().fromJSON(questionDTO.responses!), + resourceId: questionDTO.resourceId, + source_: questionDTO.source_, + order: questionDTO.order, + )); + } + return questionSubDTO; + } +} + + diff --git a/lib/Screens/Quizz/drawPath.dart b/lib/Screens/Quizz/drawPath.dart new file mode 100644 index 0000000..b567560 --- /dev/null +++ b/lib/Screens/Quizz/drawPath.dart @@ -0,0 +1,25 @@ +import 'dart:math'; +import 'dart:ui'; + +Path drawPath(Size size) { + // Method to convert degree to radians + double degToRad(double deg) => deg * (pi / 180.0); + + const numberOfPoints = 5; + final halfWidth = size.width / 2; + final externalRadius = halfWidth; + final internalRadius = halfWidth / 2.5; + final degreesPerStep = degToRad(360 / numberOfPoints); + final halfDegreesPerStep = degreesPerStep / 2; + final path = Path(); + final fullAngle = degToRad(360); + path.moveTo(size.width, halfWidth); + + for (double step = 0; step < fullAngle; step += degreesPerStep) { + path.lineTo(halfWidth + externalRadius * cos(step), + halfWidth + externalRadius * sin(step)); + path.lineTo(halfWidth + internalRadius * cos(step + halfDegreesPerStep), + halfWidth + internalRadius * sin(step + halfDegreesPerStep)); + } + return path; +} \ No newline at end of file diff --git a/lib/Screens/Quizz/questions_list.dart b/lib/Screens/Quizz/questions_list.dart new file mode 100644 index 0000000..230fb2b --- /dev/null +++ b/lib/Screens/Quizz/questions_list.dart @@ -0,0 +1,277 @@ +import 'package:carousel_slider/carousel_slider.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; +import 'package:mymuseum_visitapp/Models/ResponseSubDTO.dart'; +import 'package:mymuseum_visitapp/Models/visitContext.dart'; +import 'package:mymuseum_visitapp/app_context.dart'; +import 'package:mymuseum_visitapp/constants.dart'; +import 'package:provider/provider.dart'; + + +class QuestionsListWidget extends StatefulWidget { + List? questionsSubDTO; + bool isShowResponse = false; + final Function onShowResponse; + QuestionsListWidget({Key? key, this.questionsSubDTO, required this.isShowResponse, required this.onShowResponse}) : super(key: key); + + @override + _QuestionsListWidget createState() => _QuestionsListWidget(); +} + +class _QuestionsListWidget extends State { + List _questionsSubDTO = []; + CarouselController? sliderController; + int currentIndex = 1; + + bool kIsWeb = false; + + @override + void initState() { + super.initState(); + sliderController = CarouselController(); + _questionsSubDTO = widget.questionsSubDTO!; + } + + @override + void dispose() { + sliderController = null; + currentIndex = 1; + super.dispose(); + } + + @override + Widget build(BuildContext context) { + //Size sizeAll = MediaQuery.of(context).size; + + //Size size = Size(sizeAll.width * 0.65, kIsWeb ? sizeAll.height * 0.5 : sizeAll.height * 0.32); + final appContext = Provider.of(context); + + return SizedBox( + height: widget.isShowResponse ? MediaQuery.of(context).size.height * 0.825 : MediaQuery.of(context).size.height, + width: double.infinity, + //color: Colors.orange, + child: Stack( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: widget.isShowResponse ? MainAxisAlignment.start : MainAxisAlignment.start, + children: [ + if(_questionsSubDTO.isNotEmpty) + CarouselSlider( + carouselController: sliderController, + options: CarouselOptions( + onPageChanged: (int index, CarouselPageChangedReason reason) { + setState(() { + currentIndex = index + 1; + }); + }, + height: widget.isShowResponse ? MediaQuery.of(context).size.height * 0.79 : MediaQuery.of(context).size.height * 0.85, + enlargeCenterPage: true, + scrollPhysics: const NeverScrollableScrollPhysics(), + reverse: false, + ), + items: _questionsSubDTO.map((i) { + return Builder( + builder: (BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 7.5, right: 7.5, bottom: 10.0, top: 8.0), + child: Container( + width: MediaQuery.of(context).size.width, + // height: widget.isShowResponse ? MediaQuery.of(context).size.height *0.55 : MediaQuery.of(context).size.height, + margin: const EdgeInsets.symmetric(horizontal: 1.5), + decoration: BoxDecoration( + //color: Colors.orange, //kBackgroundLight + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(20.0), + image: i.source_ != null ? DecorationImage( + fit: BoxFit.cover, + opacity: 0.35, + image: NetworkImage( + i.source_!, + ), + ): null, + boxShadow: const [ + BoxShadow( + color: kBackgroundSecondGrey, + spreadRadius: 0.5, + blurRadius: 5, + offset: Offset(0, 1.5), // changes position of shadow + ), + ], + ), + child: Column( + //crossAxisAlignment: CrossAxisAlignment.center, + //mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + //color: Colors.red, + //width: MediaQuery.of(context).size.width *0.65, + height: MediaQuery.of(context).size.height *0.25, + child: Stack( + children: [ + Center( + child: Container( + decoration: BoxDecoration( + color: kBackgroundLight, //kBackgroundLight + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(10.0), + boxShadow: const [ + BoxShadow( + color: kBackgroundSecondGrey, + spreadRadius: 0.3, + blurRadius: 4, + offset: Offset(0, 2), // changes position of shadow + ), + ], + ), + width: MediaQuery.of(context).size.width *0.7, + height: MediaQuery.of(context).size.height *0.2, + child: Center( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(2.5), + child: Text(TranslationHelper.get(i.label, appContext.getContext() as VisitAppContext), textAlign: TextAlign.center, style: const TextStyle(fontSize: kDescriptionSize)), + ), + ), + ), + ) + ), + ] + ), + ), + /*SizedBox( + height: MediaQuery.of(context).size.height * 0.01, + ),*/ + Expanded( + child: Padding( + padding: const EdgeInsets.all(5.0), + child: SizedBox( + //height: MediaQuery.of(context).size.height * 0.75, + width: MediaQuery.of(context).size.width * 0.72, + //color: Colors.green, + child: Padding( + padding: const EdgeInsets.all(10.0), + child: GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 1, // TODO HERE IS TABLET + mainAxisExtent: widget.isShowResponse ? 70 : 75, // TODO depends on percentage + mainAxisSpacing: widget.isShowResponse ? 35 : 42.5, // TODO depends on percentage + crossAxisSpacing: widget.isShowResponse ? 0 : 0, // TODO depends on percentage + ), + itemCount: i.responsesSubDTO!.length, + itemBuilder: (BuildContext ctx, index) { + return InkWell( + onTap: () { + if(!widget.isShowResponse) { + setState(() { + i.chosen = index; + if(currentIndex == _questionsSubDTO.length && i.chosen == index) + { + widget.onShowResponse(); + //showResult = true; + //_controllerCenter!.play(); // TODO Maybe show only confetti on super score .. + } else { + sliderController!.nextPage(duration: const Duration(milliseconds: 650), curve: Curves.fastOutSlowIn); + } + }); + } + }, + child: Padding( + padding: const EdgeInsets.all(2.5), + child: Container( + alignment: Alignment.center, + child: Text(TranslationHelper.get(i.responsesSubDTO![index].label, appContext.getContext() as VisitAppContext), textAlign: TextAlign.center, style: TextStyle(fontSize: kDescriptionSize, color: i.chosen == index ? Colors.white : Colors.black)), + decoration: BoxDecoration( + color: !widget.isShowResponse ? i.chosen == index ? kTestSecondColor : kBackgroundLight : i.responsesSubDTO![index].isGood! ? kGreen : i.chosen == index ? kTextRed : kBackgroundLight, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(10.0), + boxShadow: const [ + BoxShadow( + color: kBackgroundSecondGrey, + spreadRadius: 0.3, + blurRadius: 4, + offset: Offset(0, 2), // changes position of shadow + ), + ], + ), + ), + ), + ); + }), + ), + ), + ), + ), + ], + ) + ), + ); + }, + ); + }).toList(), + ), + ], + ), + if(widget.questionsSubDTO!.isNotEmpty && _questionsSubDTO.length > 1 && currentIndex != _questionsSubDTO.length && _questionsSubDTO[currentIndex-1].chosen != null) + Positioned( + top: MediaQuery.of(context).size.height * 0.38, + right: -9, + child: InkWell( + onTap: () { + if( _questionsSubDTO.isNotEmpty && _questionsSubDTO[currentIndex-1].chosen != null) { + sliderController!.nextPage(duration: const Duration(milliseconds: 500), curve: Curves.fastOutSlowIn); + } + }, + child: const Icon( + Icons.chevron_right, + size: 75, + color: kMainColor, + ), + ) + ), + if(_questionsSubDTO.length > 1 && currentIndex != 1) + Positioned( + top: MediaQuery.of(context).size.height * 0.38, + left: -9, + child: InkWell( + onTap: () { + if(currentIndex > 1) { + sliderController!.previousPage(duration: const Duration(milliseconds: 500), curve: Curves.fastOutSlowIn); + } + }, + child: const Icon( + Icons.chevron_left, + size: 75, + color: kMainColor, + ), + ) + ), + if(_questionsSubDTO.isNotEmpty) + Positioned.fill( + child: Padding( + padding: EdgeInsets.only(bottom: widget.isShowResponse ? 0 : 0), + child: Align( + alignment: Alignment.bottomCenter, + child: InkWell( + child: Text( + currentIndex.toString()+'/'+widget.questionsSubDTO!.length.toString(), + style: const TextStyle(fontSize: 30, fontWeight: FontWeight.w500), + ), + ) + ), + ), + ), + if(_questionsSubDTO.isEmpty) + const Center( + child: Text( + "Aucune question à afficher", + style: TextStyle(fontSize: kNoneInfoOrIncorrect), + textAlign: TextAlign.center + ), + ) + ] + ), + ); + } +} \ No newline at end of file diff --git a/lib/Screens/Quizz/quizz_page.dart b/lib/Screens/Quizz/quizz_page.dart index 5d6c88b..2e570ed 100644 --- a/lib/Screens/Quizz/quizz_page.dart +++ b/lib/Screens/Quizz/quizz_page.dart @@ -1,18 +1,23 @@ import 'dart:convert'; +import 'dart:developer'; import 'dart:typed_data'; +import 'package:carousel_slider/carousel_slider.dart'; +import 'package:confetti/confetti.dart'; import 'package:flutter/material.dart'; import 'package:manager_api/api.dart'; import 'package:mymuseum_visitapp/Components/CustomAppBar.dart'; import 'package:mymuseum_visitapp/Components/Loading.dart'; -import 'package:mymuseum_visitapp/Components/SliderImages.dart'; +import 'package:mymuseum_visitapp/Components/rounded_button.dart'; import 'package:mymuseum_visitapp/Helpers/DatabaseHelper.dart'; import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; +import 'package:mymuseum_visitapp/Models/ResponseSubDTO.dart'; import 'package:mymuseum_visitapp/Models/articleRead.dart'; import 'package:mymuseum_visitapp/Models/resourceModel.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; -import 'package:mymuseum_visitapp/Screens/Article/audio_player.dart'; -import 'package:mymuseum_visitapp/Services/apiService.dart'; +import 'package:mymuseum_visitapp/Screens/Quizz/drawPath.dart'; +import 'package:mymuseum_visitapp/Screens/Quizz/questions_list.dart'; +import 'package:mymuseum_visitapp/Screens/Quizz/showResponses.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/client.dart'; import 'package:mymuseum_visitapp/constants.dart'; @@ -30,22 +35,45 @@ class QuizzPage extends StatefulWidget { class _QuizzPageState extends State { SectionDTO? sectionDTO; - QuizzDTO? quizzDTO; List resourcesModel = []; ResourceModel? audioResourceModel; final GlobalKey _scaffoldKey = GlobalKey(); late Uint8List audiobytes; VisitAppContext? visitAppContext; + QuizzDTO? quizzDTO; + List _questionsSubDTO = []; + //ConfettiController? _controllerCenter; + CarouselController? sliderController; + int currentIndex = 1; + bool showResult = false; + bool showResponses = false; + + bool kIsWeb = false; + @override void initState() { widget.visitAppContextIn.isContentCurrentlyShown = true; + + //_controllerCenter = ConfettiController(duration: const Duration(seconds: 10)); + //_controllerCenter!.play(); + + sliderController = CarouselController(); + super.initState(); } @override void dispose() { visitAppContext!.isContentCurrentlyShown = false; + currentIndex = 1; + //_controllerCenter!.dispose(); + if(quizzDTO != null) { + if(quizzDTO!.questions != null) { + _questionsSubDTO = QuestionSubDTO().fromJSON(quizzDTO!.questions!); + } + } + super.dispose(); } @@ -53,8 +81,6 @@ class _QuizzPageState extends State { Widget build(BuildContext context) { final appContext = Provider.of(context); Size size = MediaQuery.of(context).size; - //final notchInset = MediaQuery.of(context).padding; - visitAppContext = appContext.getContext(); return Scaffold( @@ -64,10 +90,439 @@ class _QuizzPageState extends State { isHomeButton: false, ), body: FutureBuilder( - future: getQuizz(appContext, appContext.clientAPI, widget.sectionId), + future: getQuizz(appContext, appContext.clientAPI, widget.sectionId), // MAYBE MOVE THAT TO PARENT .. builder: (context, AsyncSnapshot snapshot) { if(quizzDTO != null && sectionDTO != null) { - if(size.height > size.width) { + + 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()); + QuizzDTOBadLevel levelToShow = QuizzDTOBadLevel(); + var test = goodResponses/quizzDTO!.questions!.length; + + if((0 == test || test < 0.25) && quizzDTO!.badLevel != null) { + levelToShow = quizzDTO!.badLevel!; + } + if((test>=0.25 && test < 0.5) && quizzDTO!.mediumLevel != null) { + levelToShow = quizzDTO!.mediumLevel!; + } + if((test>=0.5 && test < 0.75) && quizzDTO!.goodLevel != null) { + levelToShow = quizzDTO!.goodLevel!; + } + if((test>=0.75 && test <= 1) && quizzDTO!.greatLevel != null) { + levelToShow = quizzDTO!.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, + kTestSecondColor + //Colors.pink, + //Colors.orange, + //Colors.purple + ], // manually specify the colors to be used + createParticlePath: drawPath, // define a custom shape/path. + ), + ), + ),*/ + if (!showResponses && levelToShow.source_ != null) + Container( + //height: size.height * 0.2, + //width: size.width * 0.25, + constraints: BoxConstraints( + maxHeight: size.height * 0.25, + maxWidth: kIsWeb ? size.width * 0.20 : size.width * 0.85, + ), + alignment: Alignment.center, + decoration: BoxDecoration( + image: levelToShow.source_ != null ? DecorationImage( + fit: BoxFit.contain, + opacity: 0.85, + image: NetworkImage( + levelToShow.source_!, + ), + ): null, + borderRadius: const BorderRadius.all( Radius.circular(50.0)), + border: Border.all( + color: kBackgroundGrey, + width: 1.0, + ), + ), + child: Container( + //borderRadius: BorderRadius.all(Radius.circular(25.0)), + decoration: BoxDecoration( + color: const Color(0xff7c94b6), + image: DecorationImage( + image: NetworkImage( + levelToShow.source_!, + ), + fit: BoxFit.cover, + ), + borderRadius: const BorderRadius.all( Radius.circular(50.0)), + border: Border.all( + color: kBackgroundGrey, + width: 1.0, + ), + ), + ), + ), + if(!showResponses) + // TEXT BOX WITH MAIN SCORE + Text('$goodResponses/${quizzDTO!.questions!.length}', textAlign: TextAlign.center, style: TextStyle(fontSize: kIsWeb ? (showResponses ? 60 : 100) : 75, color: kBackgroundSecondGrey)), + if(!showResponses) + // TEXT BOX WITH LEVEL TEXT RESULT + Padding( + padding: const EdgeInsets.only(bottom: 10), + child: Container( + width: MediaQuery.of(context).size.width *0.75, + height: kIsWeb ? (showResponses ? MediaQuery.of(context).size.height *0.10 : MediaQuery.of(context).size.height *0.20) : MediaQuery.of(context).size.height *0.25, + decoration: BoxDecoration( + color: kBackgroundLight, //kBackgroundLight + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(10.0), + boxShadow: const [ + BoxShadow( + color: kBackgroundSecondGrey, + spreadRadius: 0.3, + blurRadius: 4, + offset: Offset(0, 2), // changes position of shadow + ), + ], + ), + child: Center( + child: SizedBox( + width: double.infinity, + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(15.0), + child: Text(TranslationHelper.get(levelToShow.label, appContext.getContext() as VisitAppContext), textAlign: TextAlign.center, style: TextStyle(fontSize: kIsWeb ? kDescriptionSize : kDescriptionSize)), + ), + ), + ), + ), + ), + ), + if(showResponses) + QuestionsListWidget( + questionsSubDTO: _questionsSubDTO, + isShowResponse: true, + onShowResponse: () {} + ), + // RESPONSE BOX + //ShowReponsesWidget(questionsSubDTO: _questionsSubDTO), + // Buttons + Column( // TABLET IS ROW + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(4), + child: SizedBox( + height: kIsWeb ? 50 : 50, + width: size.width * 0.6, + child: RoundedButton( + text: "Recommencer", + color: kBackgroundSecondGrey, + textColor: kBackgroundLight, + icon: Icons.undo, + press: () { + setState(() { + showResult = false; + showResponses = false; + currentIndex = 1; + _questionsSubDTO = QuestionSubDTO().fromJSON(quizzDTO!.questions!); + }); + }, + fontSize: 18, + horizontal: 20, + vertical: 5 + ), + ), + ), + if(!showResponses) + Padding( + padding: const EdgeInsets.all(4.0), + child: SizedBox( + height: kIsWeb ? 50 : 50, + width: size.width * 0.6, + child: RoundedButton( + text: "Voir les réponses", + color: kBackgroundSecondGrey, + textColor: kBackgroundLight, + icon: Icons.assignment_turned_in, + press: () { + setState(() { + showResponses = true; + }); + }, + fontSize: 18, + horizontal: 20, + vertical: 5 + ), + ), + ) + ], + ), + ], + ), + ); + + } else { + return QuestionsListWidget( + isShowResponse: false, + questionsSubDTO: _questionsSubDTO, + onShowResponse: () { + setState(() { + showResult = true; + }); + } + ); + /*return Stack( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + if(_questionsSubDTO.isNotEmpty) + CarouselSlider( + carouselController: sliderController, + options: CarouselOptions( + onPageChanged: (int index, CarouselPageChangedReason reason) { + setState(() { + currentIndex = index + 1; + }); + }, + height: MediaQuery.of(context).size.height * 0.85, + enlargeCenterPage: true, + scrollPhysics: const NeverScrollableScrollPhysics(), + reverse: false, + ), + items: _questionsSubDTO.map((i) { + return Builder( + builder: (BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 1.0, right: 1.0, bottom: 15.0), + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + margin: const EdgeInsets.symmetric(horizontal: 1.5), + decoration: BoxDecoration( + //color: Colors.orange, //kBackgroundLight + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(20.0), + image: i.source_ != null ? DecorationImage( + fit: BoxFit.cover, + opacity: 0.35, + image: NetworkImage( + i.source_!, + ), + ): null, + boxShadow: const [ + BoxShadow( + color: kBackgroundSecondGrey, + spreadRadius: 0.5, + blurRadius: 5, + offset: Offset(0, 1.5), // changes position of shadow + ), + ], + ), + child: Column( + //crossAxisAlignment: CrossAxisAlignment.center, + //mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + SizedBox( + //color: Colors.red, + //width: MediaQuery.of(context).size.width *0.65, + height: MediaQuery.of(context).size.height *0.25, + child: Stack( + children: [ + Center( + child: Container( + decoration: BoxDecoration( + color: kBackgroundLight, //kBackgroundLight + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(10.0), + boxShadow: const [ + BoxShadow( + color: kBackgroundSecondGrey, + spreadRadius: 0.3, + blurRadius: 4, + offset: Offset(0, 2), // changes position of shadow + ), + ], + ), + width: MediaQuery.of(context).size.width *0.7, + height: MediaQuery.of(context).size.height *0.2, + child: Center( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(2.5), + child: Text(TranslationHelper.get(i.label, appContext.getContext() as VisitAppContext), textAlign: TextAlign.center, style: const TextStyle(fontSize: kDescriptionSize)), + ), + ), + ), + ) + ), + ] + ), + ), + /*SizedBox( + height: MediaQuery.of(context).size.height * 0.01, + ),*/ + Expanded( + child: Padding( + padding: const EdgeInsets.all(5.0), + child: SizedBox( + //height: MediaQuery.of(context).size.height * 0.75, + width: MediaQuery.of(context).size.width * 0.72, + //color: Colors.green, + child: Padding( + padding: const EdgeInsets.all(10.0), + child: GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 1, // TODO HERE IS TABLET + mainAxisExtent: kIsWeb ? 75 : 75, // TODO depends on percentage + mainAxisSpacing: kIsWeb ? 75 : 42.5, // TODO depends on percentage + crossAxisSpacing: kIsWeb ? 75 : 0, // TODO depends on percentage + ), + itemCount: i.responsesSubDTO!.length, + itemBuilder: (BuildContext ctx, index) { + return InkWell( + onTap: () { + print("COUCOU"); + setState(() { + i.chosen = index; + if(currentIndex == _questionsSubDTO.length && i.chosen == index) + { + showResult = true; + //_controllerCenter!.play(); // TODO Maybe show only confetti on super score .. + } else { + sliderController!.nextPage(duration: const Duration(milliseconds: 650), curve: Curves.fastOutSlowIn); + } + }); + }, + child: Padding( + padding: const EdgeInsets.all(2.5), + child: Container( + alignment: Alignment.center, + child: Text(TranslationHelper.get(i.responsesSubDTO![index].label, appContext.getContext() as VisitAppContext), textAlign: TextAlign.center, style: TextStyle(fontSize: kDescriptionSize, color: i.chosen == index ? Colors.white : Colors.black)), + decoration: BoxDecoration( + color: i.chosen == index ? kTestSecondColor : kBackgroundLight, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(10.0), + boxShadow: const [ + BoxShadow( + color: kBackgroundSecondGrey, + spreadRadius: 0.3, + blurRadius: 4, + offset: Offset(0, 2), // changes position of shadow + ), + ], + ), + ), + ), + ); + }), + ), + ), + ), + ), + ], + ) + ), + ); + }, + ); + }).toList(), + ), + ], + ), + if(_questionsSubDTO.length > 1 && currentIndex != _questionsSubDTO.length && _questionsSubDTO[currentIndex-1].chosen != null && quizzDTO!.questions!.isNotEmpty) + Positioned( + top: MediaQuery.of(context).size.height * 0.35, + right: 60, + child: InkWell( + onTap: () { + if(_questionsSubDTO[currentIndex-1].chosen != null && quizzDTO!.questions!.isNotEmpty) { + sliderController!.nextPage(duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn); + /*Fluttertoast.showToast( + msg: "Vous n'avez pas répondu à cette question", + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + timeInSecForIosWeb: 1, + backgroundColor: kMainRed, + textColor: Colors.white, + fontSize: 35.0 + );*/ + } + }, + child: Icon( + Icons.chevron_right, + size: kIsWeb ? 100 : 150, + color: kMainColor, + ), + ) + ), + if(_questionsSubDTO.length > 1 && currentIndex != 1) + Positioned( + top: MediaQuery.of(context).size.height * 0.35, + left: 60, + child: InkWell( + onTap: () { + if(currentIndex > 1) { + sliderController!.previousPage(duration: const Duration(milliseconds: 500), curve: Curves.fastOutSlowIn); + } + }, + child: Icon( + Icons.chevron_left, + size: kIsWeb ? 100 : 150, + color: kMainColor, + ), + ) + ), + if(_questionsSubDTO.isNotEmpty) + Padding( + padding: const EdgeInsets.only(bottom: 0), + child: Align( + alignment: Alignment.bottomCenter, + child: InkWell( + child: Text( + currentIndex.toString()+'/'+quizzDTO!.questions!.length.toString(), + style: const TextStyle(fontSize: 30, fontWeight: FontWeight.w500), + ), + ) + ), + ), + if(_questionsSubDTO.isEmpty) + const Center( + child: + Text("Aucune question à afficher", style: TextStyle(fontSize: kNoneInfoOrIncorrect), textAlign: TextAlign.center), + ) + ] + );*/ + } + + /*if(size.height > size.width) { return Column( children: [ Text("TODO show QUESTION THEN COLUMN WITH ALL RESPONSE") @@ -83,7 +538,7 @@ class _QuizzPageState extends State { ], ), ); - } + }*/ } else { return const Loading(); } @@ -256,6 +711,8 @@ class _QuizzPageState extends State { quizzDTO = QuizzDTO.fromJson(jsonDecode(sectionDTO!.data!)); } if(quizzDTO != null) { + quizzDTO!.questions!.sort((a, b) => a.order!.compareTo(b.order!)); + _questionsSubDTO = QuestionSubDTO().fromJSON(quizzDTO!.questions!); if(quizzDTO!.questions != null && quizzDTO!.questions!.isNotEmpty) { quizzDTO!.questions!.sort((a, b) => a.order!.compareTo(b.order!)); /*var audioIdArticle = articleDTO!.audioIds!.where((audioId) => audioId.language == (appContext.getContext() as VisitAppContext).language); diff --git a/lib/Screens/Visit/visit.dart b/lib/Screens/Visit/visit.dart index c820a44..fadba5d 100644 --- a/lib/Screens/Visit/visit.dart +++ b/lib/Screens/Visit/visit.dart @@ -461,7 +461,7 @@ class _VisitPageState extends State with WidgetsBindingObserver { var status = await Permission.bluetoothScan.status; print(status); } - listeningState(); + await listeningState(); } } diff --git a/lib/constants.dart b/lib/constants.dart index 7b45f9f..4ecbed8 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -36,6 +36,8 @@ const kBlue2 = Color(0xFF309cb0); const kBackgroundLight = Color(0xfff3f3f3); +const kGreen = Color(0xFF15bd5b); + const kTitleSize = 40.0; const kDescriptionSize = 25.0;