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

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

pub package

简介

google_maps_cluster_manager_2 是一个用于在Flutter应用中实现Google Maps上标记点聚合的插件。它基于Geohash算法,能够高效地管理和显示大量地理标记点,并且提供了丰富的自定义选项。

使用方法

依赖配置

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

dependencies:
  google_maps_cluster_manager: ^最新版本号

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

定义数据模型

为了让地图上的项目支持聚类,你需要让它们继承或混入 ClusterItem 类,并实现 LatLng location 的 getter 方法:

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

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

  @override
  LatLng get location => latLng;
}

初始化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, // 设置额外渲染范围的比例
    stopClusteringZoom: 17.0 // 停止聚类的缩放级别
);

地图控制器初始化

GoogleMapController 创建完成后,需要设置 mapId

_controller.complete(controller);
_manager.setMapId(controller.mapId);

自定义标记图标

你可以通过提供 markerBuilder 参数来自定义聚类标记的外观:

static Future<Marker> Function(Cluster) get markerBuilder => (cluster) async {
  return Marker(
    markerId: MarkerId(cluster.getId()),
    position: cluster.location,
    onTap: () {
      print(cluster.items);
    },
    icon: await getClusterBitmap(cluster.isMultiple ? 125 : 75, text: cluster.isMultiple ? cluster.count.toString() : null),
  );
};

static Future<BitmapDescriptor> getClusterBitmap(int size, {String? text}) async {
  final PictureRecorder pictureRecorder = PictureRecorder();
  final Canvas canvas = Canvas(pictureRecorder);
  final Paint paint1 = Paint()..color = Colors.red;

  canvas.drawCircle(Offset(size / 2, size / 2), size / 2.0, 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);

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

完整示例代码

下面是一个完整的示例应用程序,展示了如何将上述所有部分整合在一起:

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

import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_maps_cluster_manager_2/google_maps_cluster_manager_2.dart';

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

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

class MapSample extends StatefulWidget {
  @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
  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
  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());
  }
}

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

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

  @override
  LatLng get location => latLng;
}

这个例子展示了如何使用 google_maps_cluster_manager_2 插件来管理和展示大量的地理标记点。你可以根据自己的需求调整和扩展此代码。


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

1 回复

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


当然,以下是一个关于如何在Flutter应用中使用google_maps_cluster_manager_2插件的示例代码。这个插件主要用于在Google地图上管理和显示大量的标记点,通过集群(Clustering)功能来提高性能和用户体验。

首先,确保你的pubspec.yaml文件中包含了对google_maps_fluttergoogle_maps_cluster_manager_2的依赖:

dependencies:
  flutter:
    sdk: flutter
  google_maps_flutter: ^2.1.1  # 请检查最新版本
  google_maps_cluster_manager_2: ^0.5.0  # 请检查最新版本

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

接下来,在你的Flutter应用中,你可以按照以下步骤使用google_maps_cluster_manager_2插件:

  1. 导入必要的包
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_maps_cluster_manager_2/google_maps_cluster_manager_2.dart';
  1. 定义你的标记数据
List<MyLocation> generateDummyData() {
  List<MyLocation> dummyData = [];
  for (int i = 0; i < 1000; i++) {
    double lat = 37.7749 + (i % 200) * 0.001;
    double lng = -122.4194 + (i % 200) * 0.001;
    dummyData.add(MyLocation(id: i, latitude: lat, longitude: lng));
  }
  return dummyData;
}

class MyLocation {
  final int id;
  final double latitude;
  final double longitude;

  MyLocation({required this.id, required this.latitude, required this.longitude});
}
  1. 创建集群管理器
class _MyHomePageState extends State<MyHomePage> {
  late GoogleMapController _controller;
  late ClusterManager<MyLocation> _clusterManager;

  @override
  void initState() {
    super.initState();
    _clusterManager = ClusterManager<MyLocation>(
      map: _mapController,
      markersCreator: (List<MyLocation> locations) {
        return locations.map((location) {
          return Marker(
            markerId: MarkerId(location.id.toString()),
            position: LatLng(location.latitude, location.longitude),
          );
        }).toList();
      },
      updateCallback: (List<Marker> markers) {
        setState(() {
          _markers = markers;
        });
      },
      algorithm: NonHierarchicalDistanceBasedAlgorithm<MyLocation>(
        distance: 100, // 集群半径
      ),
    );

    _clusterManager.addItems(generateDummyData());
  }

  @override
  void dispose() {
    _clusterManager.dispose();
    super.dispose();
  }

  // 其他代码...
}
  1. 在GoogleMap小部件中使用集群管理器
class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late GoogleMapController _controller;
  late ClusterManager<MyLocation> _clusterManager;
  late Set<Marker> _markers = Set<Marker>();
  final Completer<GoogleMapController> _mapController = Completer();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Google Maps Cluster Manager Demo'),
      ),
      body: GoogleMap(
        mapType: MapType.normal,
        initialCameraPosition: CameraPosition(
          target: LatLng(37.7749, -122.4194),
          zoom: 11.0,
        ),
        markers: _markers,
        onMapCreated: (GoogleMapController controller) {
          _controller = controller;
          _mapController.complete(controller);
          _clusterManager.setMapId(controller);
        },
      ),
    );
  }

  // 其他代码(如initState和dispose)已在上面的代码段中定义
}

这个示例代码展示了如何创建一个包含大量标记点的Google地图,并使用google_maps_cluster_manager_2插件来管理这些标记点,通过集群功能提高性能和用户体验。注意,这里的NonHierarchicalDistanceBasedAlgorithm是集群算法的一种,你可以根据需求选择其他算法,如GridBasedAlgorithm

请确保在实际项目中处理异常和错误,并根据需要进行进一步的优化和定制。

回到顶部