Skip to content

[FEATURE] expose MapLibre through context #522

Description

@XanderD99

Feature Description

Add child widget option to the map view and expose mapController in context to child widgets.

As example I have done this myself to make it easier to create overlay on top of my map widget to add buttons or other widget that should interact with the map. This kind of removes the need to either create a very big map widget with all elements that interact with it or the struggle of having to pass down the mapcontroller.

I know you can use the static function of the MapLibrePlatform class but with this you don't know which map you are interacting with in the case where you are using multiple maps at once.

This is my map view widget:

class MapView extends StatefulWidget {
  final Widget? child;

  const MapView({super.key, this.child});

  @override
  State<MapView> createState() => MapViewState();

  static MapViewState? of(final context) {
    return context.findAncestorStateOfType<MapViewState>();
  }
}

class MapViewState extends State<MapView> {
  late final MapLibreMapController mapController;

  void _mapCreated(final MapLibreMapController controller) async {
    mapController = controller;
  }

  @override
  void dispose() {
    mapController.dispose();
    return super.dispose();
  }

  Future<bool?> _animateCamera(final CameraUpdate cameraUpdate) {
    return mapController.animateCamera(
      cameraUpdate,
      duration: const Duration(seconds: 1),
    );
  }

  Future<void> zoomIn() async {
    final cameraUpdate = CameraUpdate.zoomIn();
    await _animateCamera(cameraUpdate);
  }

  Future<void> zoomOut() async {
    final cameraUpdate = CameraUpdate.zoomOut();
    await _animateCamera(cameraUpdate);
  }

  Future<void> rotateNorth() async {
    final cameraUpdate = CameraUpdate.bearingTo(0);
    await _animateCamera(cameraUpdate);
  }

  @override
  Widget build(final context) {
    return Stack(
      children: [
        MapLibreMap(
          initialCameraPosition: const CameraPosition(target: LatLng(0, 0)),
          compassEnabled: false,
          myLocationEnabled: true,
          myLocationRenderMode: MyLocationRenderMode.gps,
          myLocationTrackingMode: MyLocationTrackingMode.trackingGps,
          styleString: 'assets/map/style.json',
          onMapCreated: _mapCreated,
          onStyleLoadedCallback: _mapLoaded,
        if (widget.child != null) widget.child!,
      ],
    );
  }
}

Then for example for zoom buttons I created following widget:

class ZoomControls extends StatelessWidget {
  const ZoomControls({super.key});

  @override
  Widget build(final context) {
    return _ControlButtonsSection(
      controls: [
        _ControlButton(
          onPressed: MapView.of(context)?.zoomIn,
          icon: const Icon(Icons.add),
        ),
        _ControlButton(
          onPressed: MapView.of(context)?.zoomOut,
          icon: const Icon(Icons.remove),
        ),
      ],
    );
  }
}

And my app.dart:

class App extends StatelessWidget {
  const App({ Key? key }) : super(key: key);

  @override
  Widget build(BuildContext context){
    return MapView(
      child: Stack(
        children: [
          Align(
            alignment: Alignment.bottomRight,
            child: ZoomControls(),
          )
        ],
      ),
    )
  }
}

Describe alternatives you've considered

No response

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions