首页 > 解决方案 > 让相机移动到 Flutter Map (Mapbox) 中的点击标记

问题描述

我正在使用 Flutter 地图包(https://pub.dev/packages/flutter_map),因为我不想在这个项目中使用 Googlemaps。问题是我从未使用过它并且文档很差,所以我无法将相机移动到点击标记:

  mapController.move(LatLng(latitude, longitude), 10.0);

当然还有像上面这样声明地图控制器:

MapController mapController = MapController();

基本上,移动功能应该在Tap上工作,但由于某种原因它不起作用,它给了我一些奇怪的运行时错误,任何想法我做错了什么?

这是代码:

  Widget loadMap() {
...Streambuilder...
  builder: (context, snapshot) {
    if (!snapshot.hasData) return Text('Loading maps... Please wait');
    for (int i = 0; i < snapshot.data.docs.length; i++) {
      allMarkers.add(Marker(
        width: 45.0,
        height: 45.0,
        point: LatLng(
          snapshot.data.docs[i]['location'].latitude,
          snapshot.data.docs[i]['location'].longitude,
        ),
        builder: (context) => Column(
          children: [
            Expanded(
              child: Container(
                child: IconButton(
                  icon: Icon(Icons
                      .location_on), 
                
                  color: _markerColor,
                  iconSize: 45.0,
                  onPressed: () {
                    
                    //this doesn't work here
                    mapController.move(LatLng(latitude, longitude), 10.0);
                    print(snapshot.data.docs[i]['eventName']);
                    showModalBottomSheet(
                        isDismissible:
                            false, 
                        context: context,
                        builder: (builder) {
                          if (snapshot.data.docs[i]['description'].length >
                              0) {
                            //not the most elegant solution
                            
                          }

                        });
                     //this returns error when marker is clicked, and doesn't move the camera to the marker
                    mapController.move(
                        LatLng(
                          snapshot.data.docs[i]['location'].latitude,
                          snapshot.data.docs[i]['location'].longitude,
                        ),
                        10.0);
                  },
                ),
              ),
            ),
           
          ],
        ),
      ));
    }
    return FlutterMap(
     //flutter map displayed with Mapbox
    );
  },

}

在 initState 中添加了地图控制器,但仍然出现错误:

    void initState() {
    super.initState();
    mapController = MapController();
  }

-错误:

The following LateError was thrown while handling a gesture:
LateInitializationError: Field '_state@244051772' has not been initialized.

When the exception was thrown, this was the stack
#0      MapControllerImpl._state (package:flutter_map/src/map/map.dart)
package:flutter_map/…/map/map.dart:1
#1      MapControllerImpl.move
package:flutter_map/…/map/map.dart:41
#2      _MapScreenState.loadMap.<anonymous closure>.<anonymous closure>.<anonymous closure>
package:directr/pages/map_screen.dart:67

标签: flutterdartmapbox

解决方案


前言:是的,HTML 文档不是很详细,但是在代码示例方面的文档令人惊叹——所有内容都有示例。如果您正在寻找文档,还请检查任何库的“example”文件夹和“test”文件夹——它们大多数时候比 HTML 文档有用得多。

如果您没有自己投入工作,请不要在提及其他人的工作时使用“差”之类的词。

我刚刚检查的库的“示例”文件夹中有一个工作示例,它正在工作,如果您希望我们解决您的特定问题,请提供一个可以执行的完整示例。

下面的代码已更改为能够在没有示例文件夹中的其他文件的情况下“独立”运行它,但我没有功劳。

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:location/location.dart';
    
class MapControllerPage extends StatefulWidget {
  static const String route = 'map_controller';

  @override
  MapControllerPageState createState() {
    return MapControllerPageState();
  }
}

class MapControllerPageState extends State<MapControllerPage> {
  static LatLng london = LatLng(51.5, -0.09);
  static LatLng paris = LatLng(48.8566, 2.3522);
  static LatLng dublin = LatLng(53.3498, -6.2603);

  late final MapController mapController;
  double rotation = 0.0;

  @override
  void initState() {
    super.initState();
    mapController = MapController();
  }

  @override
  Widget build(BuildContext context) {
    var markers = <Marker>[
      Marker(
        width: 80.0,
        height: 80.0,
        point: london,
        builder: (ctx) => Container(
          key: Key('blue'),
          child: FlutterLogo(),
        ),
      ),
      Marker(
        width: 80.0,
        height: 80.0,
        point: dublin,
        builder: (ctx) => Container(
          child: FlutterLogo(
            key: Key('green'),
            textColor: Colors.green,
          ),
        ),
      ),
      Marker(
        width: 80.0,
        height: 80.0,
        point: paris,
        builder: (ctx) => Container(
          key: Key('purple'),
          child: FlutterLogo(textColor: Colors.purple),
        ),
      ),
    ];

    return Scaffold(
      appBar: AppBar(title: Text('MapController')),
      body: Padding(
        padding: EdgeInsets.all(8.0),
        child: Column(
          children: [
            Padding(
              padding: EdgeInsets.only(top: 8.0, bottom: 8.0),
              child: Row(
                children: <Widget>[
                  MaterialButton(
                    onPressed: () {
                      mapController.move(london, 18.0);
                    },
                    child: Text('London'),
                  ),
                  MaterialButton(
                    onPressed: () {
                      mapController.move(paris, 5.0);
                    },
                    child: Text('Paris'),
                  ),
                  MaterialButton(
                    onPressed: () {
                      mapController.move(dublin, 5.0);
                    },
                    child: Text('Dublin'),
                  ),
                  CurrentLocation(mapController: mapController),
                ],
              ),
            ),
            Padding(
              padding: EdgeInsets.only(top: 8.0, bottom: 8.0),
              child: Row(
                children: <Widget>[
                  MaterialButton(
                    onPressed: () {
                      var bounds = LatLngBounds();
                      bounds.extend(dublin);
                      bounds.extend(paris);
                      bounds.extend(london);
                      mapController.fitBounds(
                        bounds,
                        options: FitBoundsOptions(
                          padding: EdgeInsets.only(left: 15.0, right: 15.0),
                        ),
                      );
                    },
                    child: Text('Fit Bounds'),
                  ),
                  Builder(builder: (BuildContext context) {
                    return MaterialButton(
                      onPressed: () {
                        final bounds = mapController.bounds!;

                        ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                          content: Text(
                            'Map bounds: \n'
                            'E: ${bounds.east} \n'
                            'N: ${bounds.north} \n'
                            'W: ${bounds.west} \n'
                            'S: ${bounds.south}',
                          ),
                        ));
                      },
                      child: Text('Get Bounds'),
                    );
                  }),
                  Text('Rotation:'),
                  Expanded(
                    child: Slider(
                      value: rotation,
                      min: 0.0,
                      max: 360,
                      onChanged: (degree) {
                        setState(() {
                          rotation = degree;
                        });
                        mapController.rotate(degree);
                      },
                    ),
                  )
                ],
              ),
            ),
            Flexible(
              child: FlutterMap(
                mapController: mapController,
                options: MapOptions(
                  center: LatLng(51.5, -0.09),
                  zoom: 5.0,
                  maxZoom: 5.0,
                  minZoom: 3.0,
                ),
                layers: [
                  TileLayerOptions(
                      urlTemplate:
                          'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
                      subdomains: ['a', 'b', 'c']),
                  MarkerLayerOptions(markers: markers)
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class CurrentLocation extends StatefulWidget {
  const CurrentLocation({
    Key? key,
    required this.mapController,
  }) : super(key: key);

  final MapController mapController;

  @override
  _CurrentLocationState createState() => _CurrentLocationState();
}

class _CurrentLocationState extends State<CurrentLocation> {
  int _eventKey = 0;

  var icon = Icons.gps_not_fixed;
  late final StreamSubscription<MapEvent> mapEventSubscription;

  @override
  void initState() {
    super.initState();

    mapEventSubscription =
        widget.mapController.mapEventStream.listen(onMapEvent);
  }

  @override
  void dispose() {
    mapEventSubscription.cancel();
    super.dispose();
  }

  void setIcon(IconData newIcon) {
    if (newIcon != icon && mounted) {
      setState(() {
        icon = newIcon;
      });
    }
  }

  void onMapEvent(MapEvent mapEvent) {
    if (mapEvent is MapEventMove && mapEvent.id == _eventKey.toString()) {
      setIcon(Icons.gps_not_fixed);
    }
  }

  void _moveToCurrent() async {
    _eventKey++;
    var location = Location();

    try {
      var currentLocation = await location.getLocation();
      var moved = widget.mapController.move(
        LatLng(currentLocation.latitude!, currentLocation.longitude!),
        18,
        id: _eventKey.toString(),
      );

      if (moved) {
        setIcon(Icons.gps_fixed);
      } else {
        setIcon(Icons.gps_not_fixed);
      }
    } catch (e) {
      setIcon(Icons.gps_off);
    }
  }

  @override
  Widget build(BuildContext context) {
    return IconButton(
      icon: Icon(icon),
      onPressed: _moveToCurrent,
    );
  }
}

推荐阅读