flutterfluttermap

get geo coordinates (LatLng) for Container() stacked on FlutterMap()


I have an App developed using Flutter.

I use

return Scaffold(
  body: Stack(
  children:  [
    FlutterMap(...),
    Positioned(
      bottom: 20,
      right: 0,
      child: Container(
               color: Colors.white,
               child: getPoints(),  // just returning a Row with some Buttons to select a Point
             ),
    ),
   ]
  )
);


Widget getPoints() {
  return Row(
            children: [
                TextButton(  .... ), 
                ...
            ]);   
}

This displays the Container with some Buttons over the FlutterMap. That's working fine.

But now: The user can zoom and drag the map and he can select some 'Points' with Buttons shown within that Container. And now I like to move the selected 'Points' into the visible part of the map. I'm able to move the 'Point' into the map if it's outside of the map, but maybe the 'Point' is behind the Container and not outside of the map. Now I like to check the geo-positions hidden by the Container. But how can I do that?

here an screenshot

I didn't find a solution or even a similar question somewhere.


Solution

  • Use a GlobalKey on both the map and your Container to measure their positions/sizes on screen. Use the latLngToScreenPoint method of the FlutterMapController to get the screen coordinates. Check if the user’s point or bounding box overlaps with the container.

    import 'package:flutter/material.dart';
    import 'package:flutter_map/flutter_map.dart';
    import 'package:latlong2/latlong2.dart';
    import 'dart:math' as math;
    
    class MapWithContainer extends StatefulWidget {
      const MapWithContainer({Key? key}) : super(key: key);
    
      @override
      _MapWithContainerState createState() => _MapWithContainerState();
    }
    
    class _MapWithContainerState extends State<MapWithContainer> {
      final MapController _mapController = MapController();
      final GlobalKey _containerKey = GlobalKey(); //-> Key for the Container
      LatLng? _pointToCheck; //-> LatLng to check if it's hidden
    
      @override
      void initState() {
        super.initState();
        //-> Initialize the point to check.
        _pointToCheck = LatLng(48.8566, 2.3522); //-> Example Paris
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Stack(
            children: [
              FlutterMap(
                mapController: _mapController,
                options: MapOptions(
                  center: LatLng(48.8566, 2.3522), 
                  zoom: 8.0,
                ),
                children: [
                  TileLayer(
                    urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
                    subdomains: ['a', 'b', 'c'],
                  ),
                  if (_pointToCheck != null)
                    MarkerLayer(
                      markers: [
                        Marker(
                          width: 20.0,
                          height: 20.0,
                          point: _pointToCheck!,
                          builder: (ctx) => Container(
                            decoration: const BoxDecoration(
                              color: Colors.blue,
                              shape: BoxShape.circle,
                            ),
                          ),
                        ),
                      ],
                    ),
                ],
              ),
              Positioned(
                bottom: 20,
                right: 0,
                child: Container(
                  key: _containerKey, //-> Assign the key
                  color: Colors.white,
                  child: getPoints(), //-> just returning
                ),
              ),
              Align(
                alignment: Alignment.topCenter,
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: ElevatedButton(
                    onPressed: () {
                      //-> Check if point is obscured
                      bool isHidden = isLatLngObscured(_pointToCheck);
                      showDialog(
                        context: context,
                        builder: (BuildContext context) {
                          return AlertDialog(
                            title: const Text('Point Obscured?'),
                            content: Text('The point is obscured: $isHidden'),
                            actions: <Widget>[
                              TextButton(
                                child: const Text('Close'),
                                onPressed: () {
                                  Navigator.of(context).pop();
                                },
                              ),
                            ],
                          );
                        },
                      );
                    },
                    child: const Text("Check if point is obscured"),
                  ),
                ),
              ),
            ],
          ),
        );
      }
    
      Widget getPoints() {
        return Row(
          children: [
            TextButton(
              child: const Text('Point A'),
              onPressed: () {
                setState(() {
                  _pointToCheck = LatLng(48.8566, 2.3522); //-> Paris
                  _movePointIntoView(); 
                });
              },
            ),
            TextButton(
              child: const Text('Point B'),
              onPressed: () {
                setState(() {
                  _pointToCheck = LatLng(43.6047, 1.4442); //-> Toulouse
                  _movePointIntoView(); 
                });
              },
            ),
            TextButton(
              child: const Text('Point C'),
              onPressed: () {
                setState(() {
                  _pointToCheck = LatLng(45.7597, 4.8422); //-> Lyon
                  _movePointIntoView(); 
                });
              },
            ),
          ],
        );
      }
    
      bool isLatLngObscured(LatLng? latLng) {
        if (latLng == null) return false;
    
        final RenderBox containerBox =
            _containerKey.currentContext?.findRenderObject() as RenderBox;
        final Size containerSize = containerBox.size;
        final Offset containerPosition = containerBox.localToGlobal(Offset.zero);
    
        //-> Convert LatLng to screen coordinates
        final math.Point<double>? screenPoint =
            _mapController.latLngToScreenPoint(latLng);
    
        if (screenPoint == null) {
          return false;
        }
    
        //-> Check if the screen point is within the container's bounds
        return (screenPoint.x >= containerPosition.dx &&
            screenPoint.x <= containerPosition.dx + containerSize.width &&
            screenPoint.y >= containerPosition.dy &&
            screenPoint.y <= containerPosition.dy + containerSize.height);
      }
    
      void _movePointIntoView() {
        if (_pointToCheck != null && isLatLngObscured(_pointToCheck)) {
          final RenderBox containerBox =
              _containerKey.currentContext?.findRenderObject() as RenderBox;
          final Size containerSize = containerBox.size;
          final double latOffset = containerSize.height / 200.0;
          final LatLng newCenter =
              LatLng(_pointToCheck!.latitude + latOffset, _pointToCheck!.longitude);
    
          //-> Move the map to the new center.
          _mapController.move(newCenter, _mapController.zoom);
        }
      }
    }