diff --git a/README.md b/README.md index e1af99d..202982d 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,9 @@ For help getting started with Flutter, view our [online documentation](https://flutter.dev/docs), which offers tutorials, samples, guidance on mobile development, and a full API reference. +# Chewie issue +flutter packages upgrade + # OPENAPI Generation cmd flutter pub run build_runner build --delete-conflicting-outputs diff --git a/lib/Screens/Map/filter_tree.dart b/lib/Screens/Map/filter_tree.dart new file mode 100644 index 0000000..1cd0e38 --- /dev/null +++ b/lib/Screens/Map/filter_tree.dart @@ -0,0 +1,173 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; + +import 'package:collection/collection.dart'; +import 'package:tablet_app/Screens/Map/tree_node.dart'; + +class FilterTree extends StatefulWidget { + final List data; + final bool selectOneToAll; + final Function(TreeNode node, bool checked, int commonId) onChecked; + final Function(TreeNode node, int id) onClicked; + final Color? textColor; + final Color? checkBoxColor; + final EdgeInsets? childrenPadding; + + FilterTree( + {this.checkBoxColor, + this.textColor, + this.childrenPadding, + required this.onChecked, + required this.onClicked, + required this.data, + required this.selectOneToAll}); + + @override + _FilterTree createState() => _FilterTree(); +} + +class _FilterTree extends State { + @override + void initState() { + super.initState(); + } + + Map map = {}; + @override + Widget build(BuildContext context) { + return ListView.builder( + itemBuilder: (BuildContext context, int index) { + return _buildNode(widget.data[index], EdgeInsets.all(0)); + }, + itemCount: widget.data.length, + ); + } + + Widget _buildNode(TreeNode node, EdgeInsets childrenPadding) { + var nonCheckedIcon = Icon( + Icons.check_box_outline_blank, + color: widget.checkBoxColor ?? Colors.green, + ); + var checkedIcon = Icon( + Icons.check_box, + color: widget.checkBoxColor ?? Colors.green, + ); + + // Si le nœud a des enfants, retourne l'ExpansionTile avec les enfants + if (node.children.isNotEmpty) { + return Padding( + padding: childrenPadding, + child: InkWell( + onTap: () { + widget.onClicked(node, node.id); + }, + child: ExpansionTile( + iconColor: widget.checkBoxColor, + collapsedIconColor: widget.checkBoxColor, + tilePadding: EdgeInsets.fromLTRB(0, 0, 0, 0), + initiallyExpanded: node.show, + title: Container( + margin: EdgeInsets.only(left: 0), + padding: EdgeInsets.only(left: 0), + child: Text( + node.title, + style: TextStyle(color: widget.textColor ?? Colors.black), + ), + ), + leading: IconButton( + icon: node.checked ? checkedIcon : nonCheckedIcon, + onPressed: () { + setState(() { + _toggleNodeSelection(node, !node.checked); + widget.onChecked(node, node.checked, node.id); + }); + }, + ), + children: node.children + .map((child) => _buildNode( + child, widget.childrenPadding ?? EdgeInsets.all(0))) + .toList(), + ), + ), + ); + } else { + // Si le nœud n'a pas d'enfants, retourne simplement le titre sans ExpansionTile + // avec le même padding que les éléments avec des enfants + return Padding( + padding: childrenPadding, + child: InkWell( + onTap: () { + widget.onClicked(node, node.id); + }, + child: ListTile( + contentPadding: childrenPadding, // Utilisez le padding des enfants + title: Text( + node.title, + style: TextStyle(color: widget.textColor ?? Colors.black), + ), + leading: IconButton( + icon: node.checked ? checkedIcon : nonCheckedIcon, + onPressed: () { + setState(() { + _toggleNodeSelection(node, !node.checked); + widget.onChecked(node, node.checked, node.id); + }); + }, + ), + ), + ), + ); + } + } + + + void _toggleNodeSelection(TreeNode node, bool isChecked) { + node.checked = isChecked; + _updateParentNodes(node, isChecked); + for (var child in node.children) { + _toggleNodeSelection(child, isChecked); + } + } + + void _updateParentNodes(TreeNode node, bool checked) { + //First Check it has parent node + bool canContinue = false; + if (node.pid != 0) { + canContinue = true; + } + + //if it have parent node then check all childrens are checked or non checked + bool canCheck = true; + if (!checked) { + for (int i = 0; i < node.children.length; i++) { + if (node.children[i].checked != checked) { + canCheck = false; + } + } + } + + if (canCheck) { + node.checked = checked; + } + + if (canContinue) { + checkEachNode(widget.data, node, checked); + } + // if all childrens are non checked the check parent node in widget.node if it matches node id then check above parent node + } + + checkEachNode(List checkNode, TreeNode node, bool checked) { + bool canStop = false; + for (int i = 0; i < checkNode.length; i++) { + if (checkNode[i].children.isNotEmpty) { + checkEachNode(checkNode[i].children, node, checked); + } + if (checkNode[i].id == node.pid && !canStop) { + canStop = true; + _updateParentNodes(checkNode[i], checked); + break; + } + } + } +} diff --git a/lib/Screens/Map/geo_point_filter.dart b/lib/Screens/Map/geo_point_filter.dart index 4d58f2c..f7ec6b3 100644 --- a/lib/Screens/Map/geo_point_filter.dart +++ b/lib/Screens/Map/geo_point_filter.dart @@ -5,16 +5,20 @@ import 'dart:math'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; -import 'package:generate_tree/generate_tree.dart'; -import 'package:generate_tree/treeNode.dart'; +//import 'package:generate_tree/generate_tree.dart'; +//import 'package:generate_tree/treeNode.dart'; import 'package:manager_api/api.dart'; import 'package:provider/provider.dart'; import 'package:tablet_app/Helpers/translationHelper.dart'; import 'package:tablet_app/Models/tabletContext.dart'; +import 'package:tablet_app/Screens/Map/filter_tree.dart'; +import 'package:tablet_app/Screens/Map/tree_node.dart'; import 'package:tablet_app/app_context.dart'; import 'package:tablet_app/constants.dart'; import 'package:html/parser.dart' show parse; +import 'map_context.dart'; + class GeoPointFilter extends StatefulWidget { final String language; final List geoPoints; @@ -72,7 +76,7 @@ class _GeoPointFilterState extends State { { TreeNode nodeWithoutCat = TreeNode( - id: int.parse( + id: 000 + int.parse( (pointWithoutCat.latitude ?? '').substring(0, min(pointWithoutCat.latitude!.length, 10)).replaceAll(".", "") + (pointWithoutCat.longitude ?? '').substring(0, min(pointWithoutCat.longitude!.length, 10)).replaceAll(".", "") ), title: parse(pointWithoutCat.title!.firstWhere((l) => l.language == widget.language).value!).documentElement!.text, @@ -88,7 +92,7 @@ class _GeoPointFilterState extends State { // Pour chaque catégorie, créez un nœud parent for (var category in categories) { TreeNode categoryNode = TreeNode( - id: category.id ?? 0, + id: 100 + (category.id ?? 0), title: parse(category.label!.firstWhere((l) => l.language == widget.language).value!).documentElement!.text, children: [], checked: true, // default true @@ -101,7 +105,7 @@ class _GeoPointFilterState extends State { for (var geoPoint in geoPoints.where((gp) => gp.categorie != null || gp.categorieId != null)) { if (geoPoint.categorieId == category.id) { TreeNode geoPointNode = TreeNode( - id: int.parse( + id: 000 + int.parse( (geoPoint.latitude ?? '').substring(0, min(geoPoint.latitude!.length, 10)).replaceAll(".", "") + (geoPoint.longitude ?? '').substring(0, min(geoPoint.longitude!.length, 10)).replaceAll(".", "") ), title: parse(geoPoint.title!.firstWhere((l) => l.language == widget.language).value!).documentElement!.text, @@ -199,6 +203,7 @@ class _GeoPointFilterState extends State { final appContext = Provider.of(context); TabletAppContext tabletAppContext = appContext.getContext(); var currentLanguage = tabletAppContext.language; + final mapContext = Provider.of(context); var primaryColor = tabletAppContext.configuration != null ? tabletAppContext.configuration!.primaryColor != null ? new Color(int.parse(tabletAppContext.configuration!.primaryColor!.split('(0x')[1].split(')')[0], radix: 16)) : kTestSecondColor : kTestSecondColor; @@ -232,7 +237,7 @@ class _GeoPointFilterState extends State { cursorColor: Colors.black, style: TextStyle(color: Colors.black), decoration: InputDecoration( - labelText: TranslationHelper.getFromLocale("map.search", appContext.getContext()), + labelText: _searchController.text.isEmpty ? TranslationHelper.getFromLocale("map.search", appContext.getContext()) : "", labelStyle: TextStyle( color: Colors.black ), @@ -282,13 +287,20 @@ class _GeoPointFilterState extends State { //color: Colors.greenAccent, height: _isExpanded ? widget.provider == MapProvider.Google ? size.height * 0.68 : size.height * 0.65 : 0, width: size.width * 0.3, - child: GenerateTree( + child: FilterTree( data: _filteredNodes, selectOneToAll: true, textColor: Colors.black, onChecked: (node, checked, commonID) { sendFilteredGeoPoint(); }, + onClicked: (node, commonID) { + print("its clicked ! " + commonID.toString()); + var selectedNode = widget.geoPoints.firstWhere((point) => int.parse( + (point.latitude ?? '').substring(0, min(point.latitude!.length, 10)).replaceAll(".", "") + (point.longitude ?? '').substring(0, min(point.longitude!.length, 10)).replaceAll(".", "") + ) == commonID); + mapContext.setSelectedPointForNavigate(selectedNode); + }, checkBoxColor: primaryColor, childrenPadding: EdgeInsets.only(left: 20, top: 10, right: 0, bottom: 10), ), diff --git a/lib/Screens/Map/google_map_view.dart b/lib/Screens/Map/google_map_view.dart index d16fed0..b781907 100644 --- a/lib/Screens/Map/google_map_view.dart +++ b/lib/Screens/Map/google_map_view.dart @@ -1,19 +1,13 @@ import 'dart:async'; -import 'dart:typed_data'; -import 'package:enum_to_string/enum_to_string.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:manager_api/api.dart'; import 'package:provider/provider.dart'; -import 'package:tablet_app/Components/multi_select_container.dart'; -import 'package:tablet_app/Models/map-marker.dart'; import 'package:tablet_app/Models/tabletContext.dart'; -import 'package:tablet_app/Screens/Map/geo_point_filter.dart'; import 'package:tablet_app/Screens/Map/map_context.dart'; import 'package:html/parser.dart' show parse; import 'package:tablet_app/app_context.dart'; -import 'package:tablet_app/constants.dart'; class GoogleMapView extends StatefulWidget { final MapDTO mapDTO; @@ -35,6 +29,7 @@ class GoogleMapView extends StatefulWidget { class _GoogleMapViewState extends State { ConfigurationDTO? configurationDTO; Completer _controller = Completer(); + GoogleMapController? _GoogleMapcontroller; Set markers = {}; List? pointsToShow = []; //List? selectedCategories = []; @@ -102,6 +97,8 @@ class _GoogleMapViewState extends State { pointsToShow = widget.geoPoints; + GeoPointDTO? selectedPointNavigate = mapContext.getSelectedPointForNavigate() as GeoPointDTO?; + /*if(!init) { print("getmarkers in build");*/ getMarkers(widget.language, mapContext); @@ -155,6 +152,7 @@ class _GoogleMapViewState extends State { //_controllerWeb.complete(controller); } else { _controller.complete(controller); + _GoogleMapcontroller = controller; } }, markers: markers, @@ -166,6 +164,29 @@ class _GoogleMapViewState extends State { }, ), ), + Container( + child: Builder( + builder: (context) { + return Consumer( + builder: (context, mapContext, _) { + var geopoint = mapContext.getSelectedPointForNavigate() as GeoPointDTO?; + if (geopoint != null && _GoogleMapcontroller != null) { + print("COUCOU IL FAUT NAVUGATE"); + _GoogleMapcontroller!.animateCamera(CameraUpdate.newCameraPosition( + CameraPosition( + target: LatLng(double.tryParse(geopoint.latitude!)!, double.tryParse(geopoint.longitude!)!), + tilt: 0.0, + bearing: 0.0, + zoom: 15.0 + ) + )); + } + return SizedBox(); + }, + ); + } + ), + ) /*Positioned( left: 5, top: 35, diff --git a/lib/Screens/Map/map_box_view.dart b/lib/Screens/Map/map_box_view.dart index ba327db..66b40e4 100644 --- a/lib/Screens/Map/map_box_view.dart +++ b/lib/Screens/Map/map_box_view.dart @@ -1,21 +1,15 @@ -import 'dart:async'; -import 'dart:typed_data'; -import 'package:enum_to_string/enum_to_string.dart'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:manager_api/api.dart'; import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart' as mapBox; import 'package:provider/provider.dart'; -import 'package:tablet_app/Components/multi_select_container.dart'; -import 'package:tablet_app/Models/map-marker.dart'; import 'package:tablet_app/Models/tabletContext.dart'; -import 'package:tablet_app/Screens/Map/geo_point_filter.dart'; import 'package:tablet_app/Screens/Map/map_context.dart'; import 'package:html/parser.dart' show parse; import 'package:tablet_app/Screens/Map/map_view.dart'; import 'package:tablet_app/app_context.dart'; -import 'package:tablet_app/constants.dart'; class MapBoxView extends StatefulWidget { final MapDTO? mapDTO; @@ -187,10 +181,32 @@ class _MapBoxViewState extends State { mapContext.setSelectedPoint(null); }, cameraOptions: mapBox.CameraOptions( - center: mapBox.Point(coordinates: widget.mapDTO!.longitude != null && widget.mapDTO!.latitude != null ? mapBox.Position(double.tryParse(widget.mapDTO!.longitude!)!, double.tryParse(widget.mapDTO!.latitude!)!) : mapBox.Position(4.865105, 50.465503)).toJson(),//TODO add element in manager // MDLF 50.416639, 4.879169 / Namur 50.465503, 4.865105 + center: mapBox.Point(coordinates: widget.mapDTO!.longitude != null && widget.mapDTO!.latitude != null ? mapBox.Position(double.tryParse(widget.mapDTO!.longitude!)!, double.tryParse(widget.mapDTO!.latitude!)!) : mapBox.Position(4.865105, 50.465503)).toJson(), zoom: widget.mapDTO!.zoom != null ? widget.mapDTO!.zoom!.toDouble() : 12), ) ), + Container( + child: Builder( + builder: (context) { + return Consumer( + builder: (context, mapContext, _) { + var geoPoint = mapContext.getSelectedPointForNavigate() as GeoPointDTO?; + if (geoPoint != null && mapboxMap != null) { + print("COUCOU IL FAUT NAVUGATE MAPBOX"); + mapboxMap?.easeTo( + mapBox.CameraOptions( + center: mapBox.Point(coordinates: mapBox.Position(double.tryParse(geoPoint.longitude!)!, double.tryParse(geoPoint.latitude!)!)).toJson(), + zoom: 15, + bearing: 0, + pitch: 3), + mapBox.MapAnimationOptions(duration: 2000, startDelay: 0)); + } + return SizedBox(); + }, + ); + } + ), + ) /*Positioned( left: 5, top: 35, diff --git a/lib/Screens/Map/map_context.dart b/lib/Screens/Map/map_context.dart index 80e3796..c47104d 100644 --- a/lib/Screens/Map/map_context.dart +++ b/lib/Screens/Map/map_context.dart @@ -3,6 +3,7 @@ import 'package:manager_api/api.dart'; class MapContext with ChangeNotifier { GeoPointDTO? _selectedPoint; + GeoPointDTO? _selectedPointNavigate; MapContext(this._selectedPoint); @@ -12,4 +13,10 @@ class MapContext with ChangeNotifier { notifyListeners(); } + getSelectedPointForNavigate() => _selectedPointNavigate; + setSelectedPointForNavigate(GeoPointDTO? selectedPointNavigate) { + _selectedPointNavigate = selectedPointNavigate; + notifyListeners(); + } + } \ No newline at end of file diff --git a/lib/Screens/Map/marker_view.dart b/lib/Screens/Map/marker_view.dart index 148e62f..0a453da 100644 --- a/lib/Screens/Map/marker_view.dart +++ b/lib/Screens/Map/marker_view.dart @@ -159,6 +159,7 @@ class _MarkerInfoWidget extends State { ), ), Row( + //mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.only(left: 15), diff --git a/lib/Screens/Map/tree_node.dart b/lib/Screens/Map/tree_node.dart new file mode 100644 index 0000000..c8a4672 --- /dev/null +++ b/lib/Screens/Map/tree_node.dart @@ -0,0 +1,42 @@ +class TreeNode { + bool checked; + bool show; + int id; + int pid; + int commonID; + String title; + List children; + + TreeNode({ + required this.checked, + required this.show, + required this.id, + required this.pid, + required this.commonID, + required this.title, + required this.children, + }); + + factory TreeNode.fromJson(Map json) { + return TreeNode( + checked: json['checked'] ?? false, + show: json['show'] ?? false, + id: json['id'] ?? 0, + pid: json['pid'] ?? 0, + commonID: json['commonID'] ?? 0, + title: json['title'] ?? '', + children: (json['children'] as List) + .map((childJson) => TreeNode.fromJson(childJson)) + .toList(), + ); + } + + List getAllChildrenTitles() { + List id = []; + for (var child in children) { + id.add(child.id); + id.addAll(child.getAllChildrenTitles()); + } + return id; + } +}