You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用GetX在Flutter中显示聚类质心标记?

问题:Flutter Map聚类标记无法显示,仅用户位置标记正常

我开发的Flutter应用基于flutter_map、get、LatLng2包,用GetX做状态管理,需求是从REST API拉取数据并在地图展示。目前用户位置标记能正常显示,但调用populateMarkers()获取的聚类质心标记始终无法渲染。


MapController代码

class MapAppController extends GetxController {
  LatLng center = LatLng(28.0339, 1.6596);
  Marker? userLocationMarker;
  bool isLoading = false;
  List<Marker> markers = [];

  Future<void> getLocation() async {
    isLoading = true;
    update();

    LocationPermission permission = await Geolocator.requestPermission();

    if (permission == LocationPermission.denied) {
      // TODO: 处理权限拒绝场景
    } else if (permission == LocationPermission.deniedForever) {
      // TODO: 处理权限永久拒绝场景
    } else {
      Position position = await Geolocator.getCurrentPosition(
          desiredAccuracy: LocationAccuracy.high);

      LatLng userLocation = LatLng(position.latitude, position.longitude);
      center = userLocation;
      userLocationMarker = Marker(
        width: 40.0,
        height: 40.0,
        point: userLocation,
        builder: (ctx) => const Icon(
          Icons.location_on,
          size: 45,
          color: Colors.red,
        ),
      );

      isLoading = false;
      update();
    }
  }

  Future<void> populateMarkers() async {
    isLoading = true;
    update();

    final Map<String, LatLng> clustersCentroid =
        await DjangoHelper.getClusterCentroid();

    markers.clear();

    for (var cluster in clustersCentroid.entries) {
      markers.add(
        Marker(
          width: 80.0,
          height: 80.0,
          point: cluster.value,
          builder: (ctx) => const Icon(
            Icons.circle,
            size: 70,
            color: Colors.green,
          ),
        ),
      );
    }

    isLoading = false;
    update();
  }
}

getLocation()方法运行正常,能正确展示用户位置标记;populateMarkers()会清空markers列表、为每个聚类创建标记并添加,最后调用update()触发UI重建,但聚类标记始终不显示。


地图页面代码

final mapController = Get.find<MapAppController>();
final userInfoController = Get.find<InscriptionController>();

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GetBuilder<MapAppController>(
        builder: (controller) => Stack(
          children: [
            FlutterMap(
              options: MapOptions(
                center: controller.center,
                zoom: 5.0,
              ),
              children: [
                TileLayer(
                  urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
                  subdomains: const ['a', 'b', 'c'],
                ),
                MarkerLayer(
                  markers: [
                    if (mapController.userLocationMarker != null)
                      mapController.userLocationMarker!,
                    if (mapController.markers.isNotEmpty)
                      ...mapController.markers,
                  ],
                ),
              ],
            ),
            Visibility(
              visible: mapController.isLoading,
              child: const CustomLoadingIndicator(),
            ),
            FloatingActionButton(
              onPressed: () async {
                await mapController.populateMarkers();
              },
              child: const Icon(Icons.info),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          await mapController.getLocation();
        },
        child: const Icon(Icons.gps_fixed),
      ),
    );
  }
}

问题原因及解决方法

1. 全局变量与GetBuilder实例不匹配

页面中直接使用全局的mapController变量,但GetBuilder的builder回调已经提供了当前监听的控制器实例,两者可能存在状态同步延迟。全部改用GetBuilder回调里的controller实例,避免直接引用全局变量:

// 修改MarkerLayer部分
MarkerLayer(
  markers: [
    if (controller.userLocationMarker != null)
      controller.userLocationMarker!,
    ...controller.markers,
  ],
),
// 修改Loading状态判断
Visibility(
  visible: controller.isLoading,
  child: const CustomLoadingIndicator(),
),

2. 列表内部变化未被GetX检测到

直接对markers列表执行clear()和add()操作,GetX无法感知列表内部的元素变化,导致update()无法触发正确的UI重建。有两种解决方式:

方案A:改用RxList

在控制器中声明可观察列表:

final markers = <Marker>[].obs;

populateMarkers()中添加标记的代码不变,页面中使用时转为普通列表:

...controller.markers.toList(),

方案B:创建新列表替换原列表

不要直接修改原列表,而是生成新列表替换:

// populateMarkers()中修改
final newMarkers = <Marker>[];
for (var cluster in clustersCentroid.entries) {
  newMarkers.add(
    Marker(
      width: 80.0,
      height: 80.0,
      point: cluster.value,
      builder: (ctx) => const Icon(
        Icons.circle,
        size: 70,
        color: Colors.green,
      ),
    ),
  );
}
markers = newMarkers;

3. 验证API返回数据有效性

在populateMarkers()中添加日志,确认接口返回的聚类数据不为空且坐标有效:

print("聚类数据数量:${clustersCentroid.length}");
clustersCentroid.forEach((key, value) => print("聚类坐标:${value.latitude}, ${value.longitude}"));

4. 检查标记层级与可见性

聚类标记的绿色圆圈可能被用户位置标记遮挡,或地图缩放级别过低导致看不到。可以先注释用户位置标记的代码,单独测试聚类标记是否显示;或调整聚类标记的添加顺序(放在用户标记前面)提升层级。


内容的提问来源于stack exchange,提问作者Mikelenjilo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 03:35:02