Flutter地图集群管理插件google_maps_cluster_manager的使用

发布于 1周前 作者 songsunli 来自 Flutter

Flutter 地图集群管理插件 google_maps_cluster_manager 的使用

介绍

google_maps_cluster_manager 是一个用于在 Flutter 中对 Google Maps 上的标记进行聚类管理的插件。它基于 Geohash 算法实现,可以帮助你在地图上高效地管理和显示大量标记。该插件受到了 clustering_google_maps 的启发。

示例动图

使用方法

1. 添加依赖

首先,在你的 pubspec.yaml 文件中添加 google_maps_cluster_manager 作为依赖:

dependencies:
  google_maps_cluster_manager: ^最新版本号
2. 创建 ClusterItem

你的地图项需要实现 ClusterItem 混入,并提供 LatLng location getter。例如:

class Place with ClusterItem {
  final String name;
  final LatLng latLng;

  Place({required this.name, required this.latLng});

  [@override](/user/override)
  LatLng get location => latLng;
}
3. 初始化 ClusterManager

接下来,初始化 ClusterManager 实例,并传入你要聚类的项列表、更新标记的方法以及可选的自定义参数:

ClusterManager<Place>(
  _items, // 你要聚类的项列表
  _updateMarkers, // 当标记更新时调用的方法
  markerBuilder: _markerBuilder, // 可选:自定义标记的构建方法
  levels: [1, 4.25, 6.75, 8.25, 11.5, 14.5, 16.0, 16.5, 20.0], // 可选:配置聚类精度变化的缩放级别
  extraPercent: 0.2, // 可选:在可见地图边界外额外渲染 20% 的区域
  stopClusteringZoom: 17.0, // 可选:停止聚类的缩放级别
);
4. 设置 mapId

GoogleMapController 创建后,你需要通过 setMapId 方法设置 mapId

_controller.complete(controller);
_manager.setMapId(controller.mapId);
5. 更新标记

你可以通过 _updateMarkers 方法来更新地图上的标记:

void _updateMarkers(Set<Marker> markers) {
  print('Updated ${markers.length} markers');
  setState(() {
    this.markers = markers;
  });
}
6. 自定义标记

你可以通过 markerBuilder 参数来自定义聚类标记的外观。以下是一个简单的示例,展示如何根据聚类中的项数量创建不同的标记图标:

Future<Marker> Function(Cluster<Place>) get _markerBuilder => (cluster) async {
  return Marker(
    markerId: MarkerId(cluster.getId()),
    position: cluster.location,
    onTap: () {
      print('---- $cluster');
      cluster.items.forEach((p) => print(p));
    },
    icon: await _getMarkerBitmap(cluster.isMultiple ? 125 : 75, text: cluster.isMultiple ? cluster.count.toString() : null),
  );
};

Future<BitmapDescriptor> _getMarkerBitmap(int size, {String? text}) async {
  if (kIsWeb) size = (size / 2).floor();

  final PictureRecorder pictureRecorder = PictureRecorder();
  final Canvas canvas = Canvas(pictureRecorder);
  final Paint paint1 = Paint()..color = Colors.orange;
  final Paint paint2 = Paint()..color = Colors.white;

  canvas.drawCircle(Offset(size / 2, size / 2), size / 2.0, paint1);
  canvas.drawCircle(Offset(size / 2, size / 2), size / 2.2, paint2);
  canvas.drawCircle(Offset(size / 2, size / 2), size / 2.8, paint1);

  if (text != null) {
    TextPainter painter = TextPainter(textDirection: TextDirection.ltr);
    painter.text = TextSpan(
      text: text,
      style: TextStyle(
        fontSize: size / 3,
        color: Colors.white,
        fontWeight: FontWeight.normal,
      ),
    );
    painter.layout();
    painter.paint(
      canvas,
      Offset(size / 2 - painter.width / 2, size / 2 - painter.height / 2),
    );
  }

  final img = await pictureRecorder.endRecording().toImage(size, size);
  final data = await img.toByteData(format: ImageByteFormat.png) as ByteData;

  return BitmapDescriptor.fromBytes(data.buffer.asUint8List());
}

完整示例代码

以下是一个完整的示例应用,展示了如何使用 google_maps_cluster_manager 插件:

import 'dart:async';
import 'dart:ui';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_cluster_manager/google_maps_cluster_manager.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Cluster Manager Demo',
      home: MapSample(),
    );
  }
}

// Clustering maps
class MapSample extends StatefulWidget {
  [@override](/user/override)
  State<MapSample> createState() => MapSampleState();
}

class MapSampleState extends State<MapSample> {
  late ClusterManager _manager;

  Completer<GoogleMapController> _controller = Completer();

  Set<Marker> markers = Set();

  final CameraPosition _parisCameraPosition =
      CameraPosition(target: LatLng(48.856613, 2.352222), zoom: 12.0);

  List<Place> items = [
    for (int i = 0; i < 10; i++)
      Place(
          name: 'Place $i',
          latLng: LatLng(48.848200 + i * 0.001, 2.319124 + i * 0.001)),
    for (int i = 0; i < 10; i++)
      Place(
          name: 'Restaurant $i',
          isClosed: i % 2 == 0,
          latLng: LatLng(48.858265 - i * 0.001, 2.350107 + i * 0.001)),
    for (int i = 0; i < 10; i++)
      Place(
          name: 'Bar $i',
          latLng: LatLng(48.858265 + i * 0.01, 2.350107 - i * 0.01)),
    for (int i = 0; i < 10; i++)
      Place(
          name: 'Hotel $i',
          latLng: LatLng(48.858265 - i * 0.1, 2.350107 - i * 0.01)),
    for (int i = 0; i < 10; i++)
      Place(
          name: 'Test $i',
          latLng: LatLng(66.160507 + i * 0.1, -153.369141 + i * 0.1)),
    for (int i = 0; i < 10; i++)
      Place(
          name: 'Test2 $i',
          latLng: LatLng(-36.848461 + i * 1, 169.763336 + i * 1)),
  ];

  [@override](/user/override)
  void initState() {
    _manager = _initClusterManager();
    super.initState();
  }

  ClusterManager _initClusterManager() {
    return ClusterManager<Place>(items, _updateMarkers, markerBuilder: _markerBuilder);
  }

  void _updateMarkers(Set<Marker> markers) {
    print('Updated ${markers.length} markers');
    setState(() {
      this.markers = markers;
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: GoogleMap(
        mapType: MapType.normal,
        initialCameraPosition: _parisCameraPosition,
        markers: markers,
        onMapCreated: (GoogleMapController controller) {
          _controller.complete(controller);
          _manager.setMapId(controller.mapId);
        },
        onCameraMove: _manager.onCameraMove,
        onCameraIdle: _manager.updateMap,
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _manager.setItems([
            for (int i = 0; i < 30; i++)
              Place(
                  name: 'New Place ${DateTime.now()} $i',
                  latLng: LatLng(48.858265 + i * 0.01, 2.350107))
          ]);
        },
        child: Icon(Icons.update),
      ),
    );
  }

  Future<Marker> Function(Cluster<Place>) get _markerBuilder =>
      (cluster) async {
        return Marker(
          markerId: MarkerId(cluster.getId()),
          position: cluster.location,
          onTap: () {
            print('---- $cluster');
            cluster.items.forEach((p) => print(p));
          },
          icon: await _getMarkerBitmap(cluster.isMultiple ? 125 : 75, text: cluster.isMultiple ? cluster.count.toString() : null),
        );
      };

  Future<BitmapDescriptor> _getMarkerBitmap(int size, {String? text}) async {
    if (kIsWeb) size = (size / 2).floor();

    final PictureRecorder pictureRecorder = PictureRecorder();
    final Canvas canvas = Canvas(pictureRecorder);
    final Paint paint1 = Paint()..color = Colors.orange;
    final Paint paint2 = Paint()..color = Colors.white;

    canvas.drawCircle(Offset(size / 2, size / 2), size / 2.0, paint1);
    canvas.drawCircle(Offset(size / 2, size / 2), size / 2.2, paint2);
    canvas.drawCircle(Offset(size / 2, size / 2), size / 2.8, paint1);

    if (text != null) {
      TextPainter painter = TextPainter(textDirection: TextDirection.ltr);
      painter.text = TextSpan(
        text: text,
        style: TextStyle(
          fontSize: size / 3,
          color: Colors.white,
          fontWeight: FontWeight.normal,
        ),
      );
      painter.layout();
      painter.paint(
        canvas,
        Offset(size / 2 - painter.width / 2, size / 2 - painter.height / 2),
      );
    }

    final img = await pictureRecorder.endRecording().toImage(size, size);
    final data = await img.toByteData(format: ImageByteFormat.png) as ByteData;

    return BitmapDescriptor.fromBytes(data.buffer.asUint8List());
  }
}

更多关于Flutter地图集群管理插件google_maps_cluster_manager的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter地图集群管理插件google_maps_cluster_manager的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用google_maps_cluster_manager插件来实现地图集群管理的示例代码。这个插件允许你在Google地图上高效地显示大量标记点,通过集群来优化显示。

首先,确保你已经在pubspec.yaml文件中添加了google_maps_fluttergoogle_maps_cluster_manager依赖:

dependencies:
  flutter:
    sdk: flutter
  google_maps_flutter: ^2.1.1  # 请检查最新版本号
  google_maps_cluster_manager: ^2.1.0  # 请检查最新版本号

然后,运行flutter pub get来安装依赖。

接下来,在你的Flutter项目中,你可以按照以下步骤来实现地图集群管理:

  1. 导入必要的包
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_maps_cluster_manager/google_maps_cluster_manager.dart';
  1. 定义地图和集群管理器
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MapScreen(),
    );
  }
}

class MapScreen extends StatefulWidget {
  @override
  _MapScreenState createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  late GoogleMapController _mapController;
  late ClusterManager<MyMarker> _clusterManager;

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

  void _initClusterManager() {
    // 初始化地图控制器和集群管理器
    _clusterManager = ClusterManager<MyMarker>(
      map: GoogleMap(
        mapType: MapType.normal,
        initialCameraPosition: CameraPosition(
          target: LatLng(37.7749, -122.4194),
          zoom: 11.0,
        ),
        onMapCreated: (GoogleMapController controller) {
          _mapController = controller;
          _clusterManager.onMapReady(controller);
        },
      ),
      builder: (context, cluster) {
        // 自定义集群标记
        return BitmapDescriptor.defaultMarkerWithHue(
          BitmapDescriptor.hueHueCyan,
        );
      },
      algorithm: NonHierarchicalDistanceBasedAlgorithm<MyMarker>(
        0.01, // 集群半径
      ),
      updateCallback: (markers, clusters) {
        // 更新UI或处理集群变化
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Google Maps Cluster Manager'),
      ),
      body: _clusterManager.map,
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 添加一些标记点
          _addMarkers();
        },
        tooltip: 'Add Markers',
        child: Icon(Icons.add),
      ),
    );
  }

  void _addMarkers() {
    List<MyMarker> markers = [
      MyMarker(position: LatLng(37.7749, -122.4194)),
      MyMarker(position: LatLng(37.7750, -122.4184)),
      MyMarker(position: LatLng(37.7751, -122.4174)),
      // 添加更多标记点...
    ];
    _clusterManager.addItems(markers);
  }
}

// 自定义标记数据类
class MyMarker {
  final LatLng position;

  MyMarker({required this.position});
}
  1. 运行你的应用

现在,你可以运行你的Flutter应用,并看到一个带有集群功能的Google地图。你可以通过点击浮动按钮来添加标记点,这些标记点会根据集群算法进行聚合。

这个示例展示了如何使用google_maps_cluster_manager插件来管理大量地图标记点,并通过集群来提高性能和用户体验。你可以根据需求进一步自定义集群的样式、算法以及交互行为。

回到顶部