745 lines
34 KiB
Dart

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<MapDTO> 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<MapConfig> {
late MapDTO mapDTO;
late List<GeoPointDTO> pointsToShow = [];
//List<String>? selectedCategories = [];
String mapType = "hybrid";
String mapTypeMapBox = "standard";
String filterSearch = '';
final ValueNotifier<List<int>?> selectedCategoriesNotifier =
ValueNotifier([]);
final ValueNotifier<String?> 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<AppContext>(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<dynamic> 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<String?>(
valueListenable:
searchNotifier,
builder: (context,
searchValue, child) {
return ValueListenableBuilder<
List<int>?>(
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<String>.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<List<GeoPointDTO>?> getGeoPoints(Client client) async {
List<GeoPointDTO>? 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<CategorieDTO>? 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
),
],
);
}