diff --git a/lib/com/hydrologis/flutterlibs/forms/forms_widgets.dart b/lib/com/hydrologis/flutterlibs/forms/forms_widgets.dart index 9df7f8e..1fc53a1 100644 --- a/lib/com/hydrologis/flutterlibs/forms/forms_widgets.dart +++ b/lib/com/hydrologis/flutterlibs/forms/forms_widgets.dart @@ -2376,7 +2376,7 @@ class GeometryWidgetState extends State { GeojsonSource? geojsonSource; late String keyStr; double _iconSize = 32; - SmashMapWidget? sWidget; + SmashMapFormsWidget? sWidget; Future getMapView(BuildContext context) async { String value = ""; //$NON-NLS-1$ @@ -2428,7 +2428,7 @@ class GeometryWidgetState extends State { } if (sWidget == null) { - sWidget = SmashMapWidget( + sWidget = SmashMapFormsWidget( key: UniqueKey()); // TODO check this ValueKey(keyStr)); sWidget!.setInitParameters( canRotate: false, diff --git a/lib/com/hydrologis/flutterlibs/maps/mapviewforms.dart b/lib/com/hydrologis/flutterlibs/maps/mapviewforms.dart new file mode 100644 index 0000000..d7f29e4 --- /dev/null +++ b/lib/com/hydrologis/flutterlibs/maps/mapviewforms.dart @@ -0,0 +1,329 @@ +part of smashlibs; + +// ignore: must_be_immutable +class SmashMapFormsWidget extends StatelessWidget { + JTS.Coordinate? _initCenterCoordonate; + JTS.Envelope? _initBounds; + double _initZoom = 13.0; + double _minZoom = SmashMapState.MINZOOM; + double _maxZoom = SmashMapState.MAXZOOM; + bool _canRotate = false; + bool _useLayerManager = true; + bool _addBorder = false; + bool consumerBuild = false; + bool _isMapReady = false; + + MapController _mapController = MapController(); + FlutterMap? flutterMap; + List preLayers = []; + List postLayers = []; + List layerSources = []; + void Function(LatLng, double) _handleTap = (ll, z) {}; + void Function(LatLng, double) _handleLongTap = (ll, z) {}; + void Function() _onMapReady = () {}; + void Function(MapCamera, bool) _onPositionChanged = + (mapPosition, hasGesture) {}; + + SmashMapFormsWidget({Key? key}) + : super(key: key != null ? key : ValueKey("SMASH_MAPVIEW_FORMS")); + + void setInitParameters({ + JTS.Coordinate? centerCoordinate, + JTS.Envelope? initBounds, + double? initZoom, + double? minZoom, + double? maxZoom, + bool canRotate = false, + bool useLayerManager = true, + bool addBorder = false, + }) { + if (centerCoordinate != null) _initCenterCoordonate = centerCoordinate; + if (initBounds != null) _initBounds = initBounds; + if (initZoom != null) _initZoom = initZoom; + if (minZoom != null) _minZoom = minZoom; + if (maxZoom != null) _maxZoom = maxZoom; + _canRotate = canRotate; + _useLayerManager = useLayerManager; + _addBorder = addBorder; + } + + void setTapHandlers( + {Function(LatLng, double)? handleTap, + Function(LatLng, double)? handleLongTap}) { + if (handleTap != null) _handleTap = handleTap; + if (handleLongTap != null) _handleLongTap = handleLongTap; + } + + void setOnMapReady(Function()? onMapReady) { + if (onMapReady != null) _onMapReady = onMapReady; + } + + bool isMapReady() { + return _isMapReady; + } + + void setOnPositionChanged(Function(MapCamera, bool)? onPositionChanged) { + if (onPositionChanged != null) _onPositionChanged = onPositionChanged; + } + + /// Clear all layers list (pre, post and manual [LayerSource]s). + void clearLayers() { + preLayers.clear(); + postLayers.clear(); + layerSources.clear(); + } + + /// Add a [LayerSource]. If [_useLayerManager] is set to false, + /// this creates a custom datasource list to be used (in case the common + /// layers are not wanted in a different map view). + void addLayerSource(LayerSource layerSource) { + if (_useLayerManager) { + LayerManager().addLayerSource(layerSource); + } else if (!layerSources.contains(layerSource)) { + layerSources.add(layerSource); + } + } + + void removeLayerSource(LayerSource layerSource) { + if (_useLayerManager) { + LayerManager().removeLayerSource(layerSource); + } else if (layerSources.contains(layerSource)) { + layerSources.remove(layerSource); + } + } + + /// Add a layer to the list of layers that is loaded before + /// the [LayerManager] layers. + void addPreLayer(Widget layer) { + int index = _getLayerIndex(preLayers, layer); + if (index != -1) { + preLayers[index] = layer; + } else { + preLayers.add(layer); + } + } + + /// Add a layer to the list of layers that is loaded after + /// the [LayerManager] layers. + void addPostLayer(Widget layer) { + int index = _getLayerIndex(postLayers, layer); + if (index != -1) { + postLayers[index] = layer; + } else { + postLayers.add(layer); + } + } + + void clearPostLayers() { + postLayers.clear(); + } + + int _getLayerIndex(List list, Widget layer) { + int i = 0; + for (var item in list) { + if (item.key == layer.key) { + return i; + } + i++; + } + return -1; + } + + void triggerRebuild(BuildContext context) { + Provider.of(context, listen: false).reBuild(); + } + + void zoomToBounds(JTS.Envelope bounds) { + _mapController.fitCamera(CameraFit.bounds( + bounds: LatLngBounds( + LatLng(bounds.getMinY(), bounds.getMinX()), + LatLng(bounds.getMaxY(), bounds.getMaxX()), + ), + )); + } + + void centerOn(JTS.Coordinate ll) { + _mapController.move( + LatLngExt.fromCoordinate(ll), _mapController.camera.zoom); + } + + void zoomTo(double newZoom) { + _mapController.move(_mapController.camera.center, newZoom); + } + + void zoomIn() { + var z = _mapController.camera.zoom + 1; + if (z > _maxZoom) z = _maxZoom; + zoomTo(z); + } + + void zoomOut() { + var z = _mapController.camera.zoom - 1; + if (z < _minZoom) z = _minZoom; + zoomTo(z); + } + + void centerAndZoomOn(JTS.Coordinate ll, double newZoom) { + _mapController.move(LatLngExt.fromCoordinate(ll), newZoom); + } + + void rotate(double heading) { + _mapController.rotate(heading); + } + + JTS.Envelope? getBounds() { + return LatLngBoundsExt.fromBounds(_mapController.camera.visibleBounds) + .toEnvelope(); + } + + @override + Widget build(BuildContext context) { + consumerBuild = false; + return Consumer(builder: (context, mapBuilder, child) { + consumerBuild = true; + // mapBuilder.scaffoldKey = _scaffoldKey; + return consumeBuild(mapBuilder, context); + }); + } + + Widget consumeBuild(SmashMapBuilder mapBuilder, BuildContext context) { + print("SmashMapFormsWidget consumeBuild"); + var layers = []; + + layers.addAll(preLayers); + if (_useLayerManager) { + layers.addAll(LayerManager().getActiveLayers()); + } else { + layers.addAll(layerSources + .map((l) => SmashMapLayer( + l, + key: ValueKey(l.getName()), + )) + .toList()); + } + layers.addAll(postLayers); + + var mapFlags = InteractiveFlag.all & + ~InteractiveFlag.flingAnimation & + ~InteractiveFlag.pinchMove; + if (!_canRotate) { + mapFlags = mapFlags & ~InteractiveFlag.rotate; + } + + // ! TODO check + // GeometryEditorState editorState = + // Provider.of(context, listen: false); + // if (editorState.isEnabled) { + // GeometryEditManager().startEditing(editorState.editableGeometry, () { + // // editorState.refreshEditLayer(); + // triggerRebuild(context); + // }); + // GeometryEditManager().addEditLayers(layers); + // } + var layerKey = "SmashMapEditLayer-${key.toString()}"; + layers.add(SmashMapEditLayer( + key: ValueKey(layerKey), + )); + + var mapKey = "FlutterMapWidget-${key.toString()}"; + flutterMap = FlutterMap( + key: ValueKey(mapKey), + options: new MapOptions( + initialCameraFit: _initBounds != null + ? CameraFit.bounds( + bounds: LatLngBounds( + LatLng(_initBounds!.getMinY(), _initBounds!.getMinX()), + LatLng(_initBounds!.getMaxY(), _initBounds!.getMaxX()), + ), + ) + : null, + initialCenter: _initCenterCoordonate != null && _initBounds == null + ? new LatLng(_initCenterCoordonate!.y, _initCenterCoordonate!.x) + : const LatLng(46, 11), + initialZoom: _initZoom, + minZoom: _minZoom, + maxZoom: _maxZoom, + onPositionChanged: (newPosition, hasGesture) { + _onPositionChanged(newPosition, hasGesture); + }, + onMapEvent: (MapEvent mapEvent) { + if (mapEvent is MapEventDoubleTapZoom || + mapEvent is MapEventScrollWheelZoom || + mapEvent is MapEventMove) { + SmashMapState mapState = + Provider.of(context, listen: false); + mapState.notifyListenersMsg("manual zoom update"); + } + }, + onTap: (TapPosition tPos, LatLng point) => + _handleTap(point, _mapController.camera.zoom), + onLongPress: (TapPosition tPos, LatLng point) => + _handleLongTap(point, _mapController.camera.zoom), + interactionOptions: InteractionOptions( + flags: mapFlags, + ), + onMapReady: () { + _isMapReady = true; + _onMapReady(); + }, + backgroundColor: Colors.transparent, + ), + children: layers, + mapController: _mapController, + ); + + Widget finalWidget = flutterMap!; + if (_addBorder) { + finalWidget = Container( + child: flutterMap, + decoration: BoxDecoration( + color: SmashColors.mainBackground, + border: + Border.all(width: 1, color: SmashColors.mainDecorationsDarker)), + ); + } + return Stack( + children: [ + finalWidget, + mapBuilder.inProgress + ? Center( + child: SmashCircularProgress( + label: + SLL.of(context).mainView_loadingData, //"Loading data...", + ), + ) + : SizedBox.shrink(), + // Align( + // alignment: Alignment.bottomRight, + // child: _iconMode == IconMode.NAVIGATION_MODE + // ? IconButton( + // key: coachMarks.toolbarButtonKey, + // icon: Icon( + // MdiIcons.forwardburger, + // color: SmashColors.mainDecorations, + // size: 32, + // ), + // onPressed: () { + // setState(() { + // _iconMode = IconMode.TOOL_MODE; + // }); + // }, + // ) + // : IconButton( + // icon: Icon( + // MdiIcons.backburger, + // color: SmashColors.mainDecorations, + // size: 32, + // ), + // onPressed: () { + // BottomToolbarToolsRegistry.disableAll(context); + // setState(() { + // _iconMode = IconMode.NAVIGATION_MODE; + // }); + // }, + // ), + // ) + ], + ); + } +} diff --git a/lib/smashlibs.dart b/lib/smashlibs.dart index 99d28cd..8682be9 100644 --- a/lib/smashlibs.dart +++ b/lib/smashlibs.dart @@ -121,6 +121,7 @@ part 'com/hydrologis/flutterlibs/projectdb/othertables.dart'; // maps part 'com/hydrologis/flutterlibs/maps/mapview.dart'; +part 'com/hydrologis/flutterlibs/maps/mapviewforms.dart'; part 'com/hydrologis/flutterlibs/maps/toolbar_tools.dart'; part 'com/hydrologis/flutterlibs/maps/models/map_state.dart'; part 'com/hydrologis/flutterlibs/maps/models/preferences_state.dart';