From 378c4317625a959faaa42d99a5f063afcbc45534 Mon Sep 17 00:00:00 2001 From: Thomas Fransolet Date: Wed, 13 Mar 2024 13:22:52 +0100 Subject: [PATCH] add MapBox Agenda + updated quiz (full screen + confetti) --- lib/Screens/Agenda/agenda_view.dart | 23 ++++- lib/Screens/Agenda/event_popup.dart | 55 +++-------- lib/Screens/Quizz/drawStrawberry.dart | 63 ++++++++++++- lib/Screens/Quizz/quizz_view.dart | 126 +++++++++++++++++++++----- 4 files changed, 198 insertions(+), 69 deletions(-) diff --git a/lib/Screens/Agenda/agenda_view.dart b/lib/Screens/Agenda/agenda_view.dart index 8caaf44..32e9e5c 100644 --- a/lib/Screens/Agenda/agenda_view.dart +++ b/lib/Screens/Agenda/agenda_view.dart @@ -6,6 +6,7 @@ import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:manager_api/api.dart'; import 'package:provider/provider.dart'; import 'package:tablet_app/Components/loading_common.dart'; @@ -30,6 +31,7 @@ class _AgendaView extends State { AgendaDTO agendaDTO = AgendaDTO(); late Agenda agenda; late ValueNotifier> filteredAgenda = ValueNotifier>([]); + late Uint8List mapIcon; @override void initState() { @@ -61,6 +63,9 @@ class _AgendaView extends State { agenda.events = agenda.events.where((a) => a.dateFrom!.isAfter(DateTime.now())).toList(); agenda.events.sort((a, b) => a.dateFrom!.compareTo(b.dateFrom!)); filteredAgenda.value = agenda.events; + + mapIcon = await getByteIcon(); + return agenda; } catch(e) { print("Erreur lors du parsing du json : ${e.toString()}"); @@ -68,6 +73,22 @@ class _AgendaView extends State { } } + getByteIcon() async { + final ByteData bytes = await rootBundle.load('assets/icons/marker.png'); + var icon = await getBytesFromAsset(bytes, 25); + return icon; + } + + Future getBytesFromAsset(ByteData data, int width) async { + //ByteData data = await rootBundle.load(path); + ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(), + targetWidth: width); + ui.FrameInfo fi = await codec.getNextFrame(); + return (await fi.image.toByteData(format: ui.ImageByteFormat.png)) + !.buffer + .asUint8List(); + } + @override Widget build(BuildContext context) { Size size = MediaQuery.of(context).size; @@ -134,7 +155,7 @@ class _AgendaView extends State { showDialog( context: context, builder: (BuildContext context) { - return EventPopup(eventAgenda: eventAgenda, mapProvider: agendaDTO.mapProvider ?? MapProvider.Google); + return EventPopup(eventAgenda: eventAgenda, mapProvider: agendaDTO.mapProvider ?? MapProvider.Google, mapIcon: mapIcon); }, ); }, diff --git a/lib/Screens/Agenda/event_popup.dart b/lib/Screens/Agenda/event_popup.dart index 6158c21..6f9db43 100644 --- a/lib/Screens/Agenda/event_popup.dart +++ b/lib/Screens/Agenda/event_popup.dart @@ -21,8 +21,9 @@ import 'dart:ui' as ui; class EventPopup extends StatefulWidget { final EventAgenda eventAgenda; final MapProvider mapProvider; + final Uint8List mapIcon; - EventPopup({Key? key, required this.eventAgenda, required this.mapProvider}) : super(key: key); + EventPopup({Key? key, required this.eventAgenda, required this.mapProvider, required this.mapIcon}) : super(key: key); @override State createState() => _EventPopupState(); @@ -60,7 +61,7 @@ class _EventPopupState extends State { mapboxMap.annotations.createPointAnnotationManager().then((pointAnnotationManager) async { this.pointAnnotationManager = pointAnnotationManager; - pointAnnotationManager.createMulti(createPoints(LatLng(widget.eventAgenda.address!.lat, widget.eventAgenda.address!.lng), icon)); + pointAnnotationManager.createMulti(createPoints(LatLng(double.parse(widget.eventAgenda.address!.lat!.toString()), double.parse(widget.eventAgenda.address!.lng!.toString())), icon)); init = true; }); } @@ -85,21 +86,7 @@ class _EventPopupState extends State { return options; } - getByteIcon() async { - final ByteData bytes = await rootBundle.load('assets/icons/marker.png'); - var icon = await getBytesFromAsset(bytes, 25); - return icon; - } - Future getBytesFromAsset(ByteData data, int width) async { - //ByteData data = await rootBundle.load(path); - ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(), - targetWidth: width); - ui.FrameInfo fi = await codec.getNextFrame(); - return (await fi.image.toByteData(format: ui.ImageByteFormat.png)) - !.buffer - .asUint8List(); - } @override Widget build(BuildContext context) { @@ -301,32 +288,16 @@ class _EventPopupState extends State { }, markers: markers, ) : - FutureBuilder( - future: getByteIcon(), - builder: (context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Uint8List icon = snapshot.data!; - return mapBox.MapWidget( - key: ValueKey("mapBoxWidget"), - styleUri: mapBox.MapboxStyles.STANDARD, - onMapCreated: (maBoxMap) { - _onMapCreated(maBoxMap, icon); - }, - cameraOptions: mapBox.CameraOptions( - center: mapBox.Point(coordinates: mapBox.Position(double.parse(widget.eventAgenda.address!.lng!.toString()), double.parse(widget.eventAgenda.address!.lat!.toString()))).toJson(), - zoom: 14 - ), - ); - } else if (snapshot.connectionState == ConnectionState.none) { - return Text("No data"); - } else { - return Center( - child: Container( - child: LoadingCommon() - ) - ); - } - } + mapBox.MapWidget( + key: ValueKey("mapBoxWidget"), + styleUri: mapBox.MapboxStyles.STANDARD, + onMapCreated: (maBoxMap) { + _onMapCreated(maBoxMap, widget.mapIcon); + }, + cameraOptions: mapBox.CameraOptions( + center: mapBox.Point(coordinates: mapBox.Position(double.parse(widget.eventAgenda.address!.lng!.toString()), double.parse(widget.eventAgenda.address!.lat!.toString()))).toJson(), + zoom: 14 + ), ), ): SizedBox(), SizedBox( diff --git a/lib/Screens/Quizz/drawStrawberry.dart b/lib/Screens/Quizz/drawStrawberry.dart index b83d5b6..0d4ce3d 100644 --- a/lib/Screens/Quizz/drawStrawberry.dart +++ b/lib/Screens/Quizz/drawStrawberry.dart @@ -1,6 +1,8 @@ import 'dart:math'; import 'dart:ui'; +import 'package:flutter/material.dart'; + Path drawStrawberry(Size size) { // Method to convert degree to radians double degToRad(double deg) => deg * (pi / 180.0); @@ -119,4 +121,63 @@ Path drawStrawberry(Size size) { path.cubicTo(size.width / 2, size.height * 0.71, size.width / 2, size.height * 0.71, size.width / 2, size.height * 0.71); return path; -} \ No newline at end of file +} + +Path drawStar(Size size) { + // Méthode pour convertir les degrés en radians + double degToRad(double deg) => deg * (pi / 180.0); + + // Nombre de pointes de l'étoile + const numberOfPoints = 5; + + // Variables pour les dimensions de l'étoile + final halfWidth = size.width / 2; + final externalRadius = halfWidth; + final internalRadius = halfWidth / 2.5; + final degreesPerStep = degToRad(360 / numberOfPoints); + final halfDegreesPerStep = degreesPerStep / 2; + + // Création du chemin pour dessiner l'étoile + var path = Path(); + + // Début du dessin de l'étoile + path.moveTo(size.width, halfWidth); + + // Boucle pour dessiner chaque pointe de l'étoile + for (double step = 0; step < 2 * pi; step += degreesPerStep) { + // Pointe externe de l'étoile + path.lineTo(halfWidth + externalRadius * cos(step), + halfWidth + externalRadius * sin(step)); + + // Pointe interne de l'étoile + path.lineTo(halfWidth + internalRadius * cos(step + halfDegreesPerStep), + halfWidth + internalRadius * sin(step + halfDegreesPerStep)); + } + + // Fermeture du chemin + path.close(); + + // Retourne le chemin complet de l'étoile + return path; +} + +Path drawText(String text) { + final painter = TextPainter( + text: TextSpan(text: text, style: TextStyle()), + textDirection: TextDirection.ltr, + ); + + painter.layout(); + + final textWidth = painter.width; + final textHeight = painter.height; + + final offsetX = (50 - textWidth) / 2; + final offsetY = (50 - textHeight) / 2; + + final path = Path(); + path.addRect(Rect.fromLTWH(offsetX, offsetY, textWidth, textHeight)); + path.close(); + + return path; +} diff --git a/lib/Screens/Quizz/quizz_view.dart b/lib/Screens/Quizz/quizz_view.dart index e54ec32..308fa8c 100644 --- a/lib/Screens/Quizz/quizz_view.dart +++ b/lib/Screens/Quizz/quizz_view.dart @@ -8,6 +8,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; import 'package:manager_api/api.dart'; +import 'package:photo_view/photo_view.dart'; import 'package:provider/provider.dart'; import 'package:tablet_app/Components/Buttons/rounded_button.dart'; import 'package:tablet_app/Components/show_element_for_resource.dart'; @@ -100,17 +101,19 @@ class _QuizzView extends State { height: 5, child: ConfettiWidget( confettiController: _controllerCenter!, + numberOfParticles: 2, blastDirectionality: BlastDirectionality.explosive, shouldLoop: false, // start again as soon as the animation is finished colors: const [ Colors.red, - kMainRed, - kSecondRed + Colors.blueGrey, + Colors.green, + kTestSecondColor //Colors.pink, //Colors.orange, //Colors.purple ], // manually specify the colors to be used - createParticlePath: drawStrawberry, // define a custom shape/path. + createParticlePath: drawStar, // define a custom shape/path. ), ), ), @@ -123,7 +126,7 @@ class _QuizzView extends State { maxWidth: kIsWeb ? size.width * 0.20 : size.width * 0.20, //size.width * 0.25 ), alignment: Alignment.center, - child : getElementForResource(context, appContext, levelToShow.label!.firstWhere((translation) => translation.language == appContext.getContext().language)), + child : getElementForResource(context, appContext, levelToShow.label!.firstWhere((translation) => translation.language == appContext.getContext().language), true), ), Container( height: 90, @@ -337,7 +340,7 @@ class _QuizzView extends State { alignment: Alignment.center, child : Padding( padding: const EdgeInsets.all(10.0), - child: getElementForResource(context, appContext, i.label!.firstWhere((translation) => translation.language == appContext.getContext().language)), + child: getElementForResource(context, appContext, i.label!.firstWhere((translation) => translation.language == appContext.getContext().language), true), ) ), Padding( @@ -411,7 +414,7 @@ class _QuizzView extends State { alignment: Alignment.center, child : Padding( padding: const EdgeInsets.all(10.0), - child: getElementForResource(context, appContext, i.responsesSubDTO![index].label!.firstWhere((translation) => translation.language == appContext.getContext().language)), + child: getElementForResource(context, appContext, i.responsesSubDTO![index].label!.firstWhere((translation) => translation.language == appContext.getContext().language), true), ) ), Container( @@ -520,24 +523,66 @@ class _QuizzView extends State { } } -getElementForResource(BuildContext context, AppContext appContext, TranslationAndResourceDTO i) { +getElementForResource(BuildContext context, AppContext appContext, TranslationAndResourceDTO i, bool addFullScreen) { var widgetToInclude; + Size size = MediaQuery.of(context).size; switch(i.resourceType) { case ResourceType.Image: case ResourceType.ImageUrl: - widgetToInclude = Container( - //borderRadius: BorderRadius.all(Radius.circular(25.0)), - decoration: BoxDecoration( - color: const Color(0xff7c94b6), - image: DecorationImage( - image: ImageCustomProvider.getImageProvider(appContext, i.resourceId!, i.resourceUrl!), - fit: BoxFit.cover, - ), - borderRadius: BorderRadius.all(Radius.circular(15.0)), - border: Border.all( - color: kBackgroundGrey, - width: 1.0, + widgetToInclude = GestureDetector( + onTap: () { + if(addFullScreen) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20.0)) + ), + contentPadding: EdgeInsets.zero, + // title: Text(eventAgenda.name!), + content: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(20.0)), + color: kBackgroundColor, + ), + height: size.height * 0.8, + width: size.width * 0.8, + child: Container( + decoration: BoxDecoration( + color: const Color(0xff7c94b6), + borderRadius: BorderRadius.all(Radius.circular(15.0)), + ), + child: PhotoView( + imageProvider: ImageCustomProvider.getImageProvider(appContext, i.resourceId!, i.resourceUrl!), + minScale: PhotoViewComputedScale.contained * 0.8, + maxScale: PhotoViewComputedScale.contained * 3.0, + backgroundDecoration: BoxDecoration( + color: kBackgroundSecondGrey, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(15.0), + ), + ), + ), + ), + ); + }, + ); + } + }, + child: Container( + decoration: BoxDecoration( + color: const Color(0xff7c94b6), + image: DecorationImage( + image: ImageCustomProvider.getImageProvider(appContext, i.resourceId!, i.resourceUrl!), + fit: BoxFit.cover, + ), + borderRadius: BorderRadius.all(Radius.circular(15.0)), + border: Border.all( + color: kBackgroundGrey, + width: 1.0, + ), ), ), ); @@ -545,13 +590,44 @@ getElementForResource(BuildContext context, AppContext appContext, TranslationAn case ResourceType.Video: case ResourceType.VideoUrl: case ResourceType.Audio: - widgetToInclude = Container( - decoration: BoxDecoration( - color: kBackgroundSecondGrey, - shape: BoxShape.rectangle, - borderRadius: BorderRadius.circular(15.0), + widgetToInclude = GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + if(addFullScreen && i.resourceType != ResourceType.Audio) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20.0)) + ), + contentPadding: EdgeInsets.zero, + // title: Text(eventAgenda.name!), + content: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(20.0)), + color: kBackgroundColor, + ), + height: size.height * 0.8, + width: size.width * 0.8, + child: Center(child: showElementForResource(ResourceDTO(id: i.resourceId, url: i.resourceUrl, type: i.resourceType), appContext, false, true)), + ), + ); + }, + ); + } + }, + child: IgnorePointer( + ignoring: i.resourceType != ResourceType.Audio, + child: Container( + decoration: BoxDecoration( + color: kBackgroundSecondGrey, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(15.0), + ), + child: showElementForResource(ResourceDTO(id: i.resourceId, url: i.resourceUrl, type: i.resourceType), appContext, false, true), + ), ), - child: showElementForResource(ResourceDTO(id: i.resourceId, url: i.resourceUrl, type: i.resourceType), appContext, false, true), ); break; }