import 'package:diacritic/diacritic.dart'; import 'package:flutter/material.dart'; import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; import 'package:location_picker_flutter_map/location_picker_flutter_map.dart'; import 'package:manager_app/Components/confirmation_dialog.dart'; import 'package:manager_app/Components/geoloc_input_container.dart'; import 'package:manager_app/Components/common_loader.dart'; import 'package:manager_app/Components/message_notification.dart'; import 'package:manager_app/Models/managerContext.dart'; import 'package:manager_app/Screens/Configurations/Section/SubSection/Map/category_input_container.dart'; import 'package:manager_app/Components/dropDown_input_container.dart'; import 'package:manager_app/Components/fetch_section_icon.dart'; import 'package:manager_app/Components/resource_input_container.dart'; import 'package:manager_app/Components/multi_select_container.dart'; import 'package:manager_app/Components/single_select_container.dart'; import 'package:manager_app/Components/slider_input_container.dart'; import 'package:manager_app/Components/string_input_container.dart'; import 'package:manager_app/Screens/Configurations/Section/SubSection/Map/showNewOrUpdateGeoPoint.dart'; import 'package:manager_app/app_context.dart'; import 'package:manager_app/client.dart'; import 'package:manager_app/constants.dart'; import 'package:manager_api_new/api.dart'; import 'package:manager_app/Screens/Configurations/Section/SubSection/Parcours/parcours_config.dart'; import 'package:provider/provider.dart'; class MapConfig extends StatefulWidget { final String? color; final String? label; final MapDTO initialValue; final ValueChanged onChanged; const MapConfig({ Key? key, this.color, this.label, required this.initialValue, required this.onChanged, }) : super(key: key); @override _MapConfigState createState() => _MapConfigState(); } class _MapConfigState extends State { late MapDTO mapDTO; late List pointsToShow = []; //List? selectedCategories = []; String mapType = "hybrid"; String mapTypeMapBox = "standard"; String filterSearch = ''; final ValueNotifier?> selectedCategoriesNotifier = ValueNotifier([]); final ValueNotifier searchNotifier = ValueNotifier(""); @override void initState() { super.initState(); //mapDTO = MapDTO.fromJson(json.decode(widget.initialValue))!; mapDTO = widget.initialValue; if (mapDTO.mapType != null) { switch (mapDTO.mapType!.value) { case 0: mapType = "none"; break; case 1: mapType = "normal"; break; case 2: mapType = "satellite"; break; case 3: mapType = "terrain"; break; case 4: mapType = "hybrid"; break; } } if (mapDTO.mapTypeMapbox != null) { switch (mapDTO.mapTypeMapbox!.value) { case 0: mapTypeMapBox = "standard"; break; case 1: mapTypeMapBox = "streets"; break; case 2: mapTypeMapBox = "outdoors"; break; case 3: mapTypeMapBox = "light"; break; case 4: mapTypeMapBox = "dark"; break; case 5: mapTypeMapBox = "satellite"; break; case 6: mapTypeMapBox = "satellite_streets"; break; } } } @override Widget build(BuildContext context) { final appContext = Provider.of(context); Size size = MediaQuery.of(context).size; var mapProviderIn = ""; switch (mapDTO.mapProvider) { case MapProvider.Google: mapProviderIn = "Google"; break; case MapProvider.MapBox: mapProviderIn = "MapBox"; break; default: mapProviderIn = "Google"; break; } return DefaultTabController( length: 2, child: Column( mainAxisSize: MainAxisSize.min, children: [ _buildMapHeader(size, mapProviderIn), TabBar( labelColor: kPrimaryColor, unselectedLabelColor: Colors.grey, indicatorColor: kPrimaryColor, tabs: [ Tab(icon: Icon(Icons.map), text: "Points d'Intérêt"), Tab(icon: Icon(Icons.route), text: "Parcours"), ], ), Container( height: 700, child: TabBarView( children: [ // Tab 1: Configuration & Points SingleChildScrollView( child: Column( children: [ FutureBuilder( future: getGeoPoints( (appContext.getContext() as ManagerAppContext) .clientAPI!), builder: (context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Center(child: CommonLoader()); } else { if (snapshot.connectionState == ConnectionState.done) { mapDTO.points = snapshot.data; pointsToShow = mapDTO.points!; pointsToShow.sort((a, b) => a.title! .firstWhere((t) => t.language == 'FR') .value! .toLowerCase() .compareTo(b.title! .firstWhere((t) => t.language == 'FR') .value! .toLowerCase())); selectedCategoriesNotifier.value = mapDTO .categories! .map((categorie) => categorie.id!) .toList(); return Padding( padding: const EdgeInsets.all(8.0), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(25), border: Border.all( width: 1.5, color: kSecond)), child: Stack(children: [ Container( constraints: BoxConstraints(minHeight: 100), child: Padding( padding: const EdgeInsets.only( top: 95, left: 10, right: 10, bottom: 10), child: ValueListenableBuilder( valueListenable: searchNotifier, builder: (context, searchValue, child) { return ValueListenableBuilder< List?>( valueListenable: selectedCategoriesNotifier, builder: (context, selectedCategories, child) { if (selectedCategories == null || selectedCategories .length == 0) { pointsToShow = mapDTO .points! .where((point) => point .categorieId == null) .toList(); } else { pointsToShow = mapDTO .points! .where((point) => selectedCategories.any((tps) => point.categorieId == null || point.categorieId == tps)) .toList(); } pointsToShow = searchValue != null && searchValue .trim() .isNotEmpty ? pointsToShow .where((GeoPointDTO pointGeo) => removeDiacritics(pointGeo .title! .firstWhere((t) => t.language == "FR") .value! .toUpperCase()) .contains( removeDiacritics( searchValue.toUpperCase()))) .toList() : pointsToShow; return GridView .builder( shrinkWrap: true, gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 8), itemCount: pointsToShow .length, itemBuilder: (BuildContext context, int index) { return Container( decoration: boxDecoration( pointsToShow[ index], appContext), padding: const EdgeInsets .all( 5), margin: EdgeInsets.symmetric( vertical: 10, horizontal: 10), child: getElement( index, pointsToShow[ index], size, appContext), ); }); }); }), ), ), Positioned( top: 10, left: 10, child: Text( "Points géographiques", style: TextStyle(fontSize: 15), ), ), Positioned( top: 10, left: 175, child: MultiSelectContainer( label: null, color: kSecond, width: size.width * 0.45, initialValue: mapDTO.categories! .where((cat) => selectedCategoriesNotifier .value! .contains(cat.id)) .map((categorie) => categorie .label! .firstWhere((element) => element.language == 'FR') .value!) .toList(), isMultiple: true, isHTMLLabel: true, values: mapDTO.categories! .map((categorie) => categorie .label! .firstWhere((element) => element.language == 'FR') .value!) .toList(), onChanged: (value) { var tempOutput = new List.from(value); selectedCategoriesNotifier.value = mapDTO.categories! .where((c) => tempOutput .contains(c.label! .firstWhere( (element) => element .language == 'FR') .value!)) .map((cat) => cat.id!) .toList(); }, )), Positioned( top: 0, right: 150, child: Container( height: size.height * 0.1, constraints: BoxConstraints(minHeight: 85), child: StringInputContainer( label: "Recherche:", isSmall: true, fontSize: 15, fontSizeText: 15, onChanged: (String value) { searchNotifier.value = value; }, ), ), ), Positioned( top: 10, right: 10, child: InkWell( onTap: () { showNewOrUpdateGeoPoint( mapDTO, null, (GeoPointDTO geoPoint) async { try { await (appContext.getContext() as ManagerAppContext) .clientAPI! .sectionMapApi! .sectionMapCreate( mapDTO.id!, geoPoint); showNotification( kSuccess, kWhite, 'Le point géographique a été créé avec succès', context, null); setState(() { // refresh ui print("Refresh UI"); }); } catch (e) { showNotification( kError, kWhite, 'Une erreur est survenue lors de la création du point géographique', context, null); } }, appContext, context); }, child: Container( height: 40, width: 40, child: Icon( Icons.add, color: kTextLightColor, size: 25.0, ), decoration: BoxDecoration( color: kSuccess, shape: BoxShape.rectangle, borderRadius: BorderRadius.circular(20.0), boxShadow: [ BoxShadow( color: kSecond, spreadRadius: 0.5, blurRadius: 5, offset: Offset(0, 1.5), // changes position of shadow ), ], ), ), ), ) ]), ), ); } else { return Center( child: Text( "Une erreur est survenue lors de la récupération des points géographiques"), ); } } }), ], ), ), // Tab 2: Parcours ParcoursConfig( initialValue: mapDTO.guidedPaths ?? [], parentId: mapDTO.id!, isEvent: false, onChanged: (paths) { setState(() { mapDTO.guidedPaths = paths; widget.onChanged(mapDTO); }); }, ), ], ), ), ], ), ); } Future?> getGeoPoints(Client client) async { List? geoPoints = await client.sectionMapApi! .sectionMapGetAllGeoPointsFromSection(widget.initialValue.id!); return geoPoints ?? []; } getElement(int index, GeoPointDTO? point, Size size, AppContext appContext) { return Container( width: double.infinity, height: double.infinity, child: Stack( children: [ Center( child: Padding( padding: const EdgeInsets.all(8.0), child: HtmlWidget( point != null ? point.title == null ? "" : point.title![0].value! : "", //textAlign: TextAlign.left, customStylesBuilder: (element) { return {'text-align': 'center'}; }, textStyle: TextStyle(fontSize: 20)), ), ), Positioned( top: 0, right: 0, child: Icon( getSectionIcon(SectionType.Map), color: kSecond, size: 18.0, ), ), Positioned( bottom: 0, left: 0, child: InkWell( onTap: () { showNewOrUpdateGeoPoint(mapDTO, pointsToShow[index], (GeoPointDTO geoPoint) async { try { await (appContext.getContext() as ManagerAppContext) .clientAPI! .sectionMapApi! .sectionMapUpdate(geoPoint); showNotification( kSuccess, kWhite, 'Le point géographique a été mis à jour avec succès', context, null); setState(() { // refresh ui print("Refresh UI"); }); } catch (e) { showNotification( kError, kWhite, 'Une erreur est survenue lors de la mise à jour du point géographique', context, null); } /*setState(() { var pointToUpdate = pointsToShow[index]; var pointToUpdateMapDTOPoints = mapDTO.points!.firstWhere((p) => p.longitude == pointToUpdate.longitude && p.latitude == pointToUpdate.latitude); var mapDTOPointsIndex = mapDTO.points!.indexOf(pointToUpdateMapDTOPoints); mapDTO.points![mapDTOPointsIndex] = pointToUpdate; mapDTO.points!.sort((a, b) => a.title!.firstWhere((t) => t.language == 'FR').value!.toLowerCase().compareTo(b.title!.firstWhere((t) => t.language == 'FR').value!.toLowerCase())); //widget.onChanged(jsonEncode(mapDTO).toString()); widget.onChanged(mapDTO); });*/ }, appContext, context); }, child: Icon( Icons.edit, color: kPrimaryColor, size: 20.0, )), ), Positioned( bottom: 0, right: 0, child: InkWell( onTap: () async { showConfirmationDialog( "Êtes-vous sûr de vouloir supprimer ce point géographique ?", () {}, () async { try { var pointToRemove = pointsToShow[index]; (appContext.getContext() as ManagerAppContext) .clientAPI! .sectionMapApi! .sectionMapDelete(pointToRemove.id!); showNotification( kSuccess, kWhite, 'Le point géographique a été supprimé avec succès', context, null); // refresh UI setState(() { print("Refresh UI"); }); } catch (e) { showNotification( kError, kWhite, 'Une erreur est survenue lors de la suppression du point géographique', context, null); } }, context); }, child: Icon( Icons.delete, color: kError, size: 20.0, )), ) ], ), ); } Widget _buildMapHeader(Size size, String mapProviderIn) { return Container( padding: const EdgeInsets.all(16.0), margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(15), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.1), spreadRadius: 2, blurRadius: 5, offset: Offset(0, 3), ), ], border: Border.all(color: kPrimaryColor.withOpacity(0.2)), ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ SingleSelectContainer( label: "Service :", color: Colors.black, initialValue: mapProviderIn, inputValues: map_providers, onChanged: (String value) { switch (value) { case "Google": mapDTO.mapProvider = MapProvider.Google; break; case "MapBox": mapDTO.mapProvider = MapProvider.MapBox; break; } widget.onChanged(mapDTO); }), GeolocInputContainer( label: "Point de centrage :", initialValue: mapDTO.centerLatitude != null && mapDTO.centerLongitude != null ? LatLong(double.parse(mapDTO.centerLatitude!), double.parse(mapDTO.centerLongitude!)) : null, color: kPrimaryColor, onChanged: (LatLong? localisation) { if (localisation != null) { mapDTO.centerLongitude = localisation.longitude.toString(); mapDTO.centerLatitude = localisation.latitude.toString(); } widget.onChanged(mapDTO); }, isSmall: true), ResourceInputContainer( label: "Icône :", initialValue: mapDTO.iconResourceId, color: kPrimaryColor, imageFit: BoxFit.contain, onChanged: (ResourceDTO resource) { if (resource.id == null) { mapDTO.iconSource = null; mapDTO.iconResourceId = null; } else { mapDTO.iconResourceId = resource.id; mapDTO.iconSource = resource.url; } widget.onChanged(mapDTO); }, isSmall: true), ], ), const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ if (mapDTO.mapProvider == MapProvider.Google) DropDownInputContainer( label: "Type :", values: map_types, initialValue: mapType, onChange: (String? value) { mapDTO.mapType = MapTypeApp.fromJson(value); widget.onChanged(mapDTO); }, ), if (mapDTO.mapProvider == MapProvider.MapBox) DropDownInputContainer( label: "Type :", values: map_types_mapBox, initialValue: mapTypeMapBox, onChange: (String? value) { mapDTO.mapTypeMapbox = MapTypeMapBox.fromJson(value); widget.onChanged(mapDTO); }, ), SliderInputContainer( label: "Zoom :", initialValue: mapDTO.zoom != null ? mapDTO.zoom!.toDouble() : 18, color: kPrimaryColor, min: 0, max: 30, onChanged: (double value) { mapDTO.zoom = value.toInt(); widget.onChanged(mapDTO); }, ), Container( height: 70, child: CategoryInputContainer( label: "Catégories :", initialValue: mapDTO.categories != null ? mapDTO.categories! : [], color: kPrimaryColor, onChanged: (List? value) { if (value != null) { mapDTO.categories = value; if (mapDTO.points != null) { mapDTO.points!.forEach((p) { if (p.categorieId != null && !mapDTO.categories!.map((c) => c.id).any( (e) => e != null && e == p.categorieId)) { p.categorieId = null; } }); } widget.onChanged(mapDTO); } }, ), ) ], ), ], ), ); } } boxDecoration(GeoPointDTO geoPointDTO, appContext) { return BoxDecoration( color: kBackgroundColor, shape: BoxShape.rectangle, border: Border.all(width: 1.5, color: kSecond), borderRadius: BorderRadius.circular(10.0), boxShadow: [ BoxShadow( color: kSecond, spreadRadius: 0.5, blurRadius: 5, offset: Offset(0, 1.5), // changes position of shadow ), ], ); }