Added navigation to point from geoFilter (mapbox and google)

This commit is contained in:
Thomas Fransolet 2024-04-23 16:16:43 +02:00
parent 2428d22558
commit d3fc3c416c
8 changed files with 296 additions and 21 deletions

View File

@ -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

View File

@ -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<TreeNode> 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<FilterTree> {
@override
void initState() {
super.initState();
}
Map<String, int> 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<TreeNode> 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;
}
}
}
}

View File

@ -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<GeoPointDTO> geoPoints;
@ -72,7 +76,7 @@ class _GeoPointFilterState extends State<GeoPointFilter> {
{
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<GeoPointFilter> {
// 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<GeoPointFilter> {
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<GeoPointFilter> {
final appContext = Provider.of<AppContext>(context);
TabletAppContext tabletAppContext = appContext.getContext();
var currentLanguage = tabletAppContext.language;
final mapContext = Provider.of<MapContext>(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<GeoPointFilter> {
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<GeoPointFilter> {
//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),
),

View File

@ -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<GoogleMapView> {
ConfigurationDTO? configurationDTO;
Completer<GoogleMapController> _controller = Completer();
GoogleMapController? _GoogleMapcontroller;
Set<Marker> markers = {};
List<GeoPointDTO>? pointsToShow = [];
//List<String>? selectedCategories = [];
@ -102,6 +97,8 @@ class _GoogleMapViewState extends State<GoogleMapView> {
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<GoogleMapView> {
//_controllerWeb.complete(controller);
} else {
_controller.complete(controller);
_GoogleMapcontroller = controller;
}
},
markers: markers,
@ -166,6 +164,29 @@ class _GoogleMapViewState extends State<GoogleMapView> {
},
),
),
Container(
child: Builder(
builder: (context) {
return Consumer<MapContext>(
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,

View File

@ -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<MapBoxView> {
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<MapContext>(
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,

View File

@ -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();
}
}

View File

@ -159,6 +159,7 @@ class _MarkerInfoWidget extends State<MarkerViewWidget> {
),
),
Row(
//mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(left: 15),

View File

@ -0,0 +1,42 @@
class TreeNode {
bool checked;
bool show;
int id;
int pid;
int commonID;
String title;
List<TreeNode> 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<String, dynamic> 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<dynamic>)
.map((childJson) => TreeNode.fromJson(childJson))
.toList(),
);
}
List<int> getAllChildrenTitles() {
List<int> id = [];
for (var child in children) {
id.add(child.id);
id.addAll(child.getAllChildrenTitles());
}
return id;
}
}