cluster_marker_style 0.3.0 copy "cluster_marker_style: ^0.3.0" to clipboard
cluster_marker_style: ^0.3.0 copied to clipboard

Beautiful, count-aware, cached cluster marker icons for Google Maps. A drop-in styling/rendering layer for any map clustering algorithm.

example/lib/main.dart

import 'dart:math';

import 'package:cluster_marker_style/cluster_marker_style_gmcm.dart';
import 'package:flutter/material.dart';
// google_maps_flutter also exports `Cluster` and `ClusterManager` (its native
// clustering); hide them so the names refer to google_maps_cluster_manager_2.
import 'package:google_maps_flutter/google_maps_flutter.dart'
    hide Cluster, ClusterManager;
import 'package:google_maps_cluster_manager_2/google_maps_cluster_manager_2.dart';

void main() => runApp(const DemoApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'cluster_marker_style demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
      home: const ClusterDemoPage(),
    );
  }
}

/// A clustered point. Any class can be clustered by mixing in [ClusterItem]
/// and exposing a [location]; the mixin supplies the geohash.
class Place with ClusterItem {
  Place(this.latLng);

  final LatLng latLng;

  @override
  LatLng get location => latLng;
}

/// The three named styles the demo cycles through.
enum StyleChoice {
  soft('Soft'),
  flat('Flat'),
  outlined('Outlined');

  const StyleChoice(this.label);
  final String label;

  ClusterStyle build() => switch (this) {
        StyleChoice.soft => ClusterStyle.soft(),
        StyleChoice.flat => ClusterStyle.flat(),
        StyleChoice.outlined => ClusterStyle.outlined(),
      };
}

class ClusterDemoPage extends StatefulWidget {
  const ClusterDemoPage({super.key});

  @override
  State<ClusterDemoPage> createState() => _ClusterDemoPageState();
}

class _ClusterDemoPageState extends State<ClusterDemoPage> {
  static const _center = LatLng(37.7749, -122.4194); // San Francisco

  late final ClusterManager<Place> _manager;
  Set<Marker> _markers = <Marker>{};

  StyleChoice _choice = StyleChoice.soft;
  // The renderer is swapped when the style changes. clusterMarkerBuilder reads
  // it dynamically, so switching styles needs no map rebuild.
  late ClusterRenderer _renderer = ClusterRenderer(_choice.build());
  double _devicePixelRatio = 2;

  @override
  void initState() {
    super.initState();
    _manager = ClusterManager<Place>(
      _generatePlaces(3000),
      (markers) => setState(() => _markers = markers),
      markerBuilder: _buildClusterMarker,
      stopClusteringZoom: 17,
    );
  }

  // One line replaces ~40 lines of PictureRecorder/Canvas/TextPainter code.
  Future<Marker> _buildClusterMarker(Cluster<Place> cluster) {
    return clusterMarkerBuilder<Place>(
      renderer: _renderer,
      devicePixelRatio: _devicePixelRatio,
      onTap: (c) => debugPrint('Tapped a cluster of ${c.count}'),
    )(cluster);
  }

  void _switchStyle(StyleChoice choice) {
    setState(() {
      _choice = choice;
      _renderer = ClusterRenderer(choice.build()); // fresh, empty cache
    });
    _manager.updateMap(); // re-style the visible clusters
  }

  static List<Place> _generatePlaces(int count) {
    final rng = Random(7);
    return List<Place>.generate(count, (_) {
      final lat = _center.latitude + (rng.nextDouble() - 0.5) * 0.7;
      final lng = _center.longitude + (rng.nextDouble() - 0.5) * 0.7;
      return Place(LatLng(lat, lng));
    });
  }

  @override
  Widget build(BuildContext context) {
    _devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
    return Scaffold(
      body: Stack(
        children: [
          GoogleMap(
            initialCameraPosition: const CameraPosition(
              target: _center,
              zoom: 10,
            ),
            markers: _markers,
            onMapCreated: (controller) => _manager.setMapId(controller.mapId),
            onCameraMove: _manager.onCameraMove,
            onCameraIdle: _manager.updateMap,
          ),
          SafeArea(
            child: Align(
              alignment: Alignment.bottomCenter,
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: SegmentedButton<StyleChoice>(
                  segments: [
                    for (final choice in StyleChoice.values)
                      ButtonSegment<StyleChoice>(
                        value: choice,
                        label: Text(choice.label),
                      ),
                  ],
                  selected: {_choice},
                  onSelectionChanged: (selection) =>
                      _switchStyle(selection.first),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
0
likes
160
points
39
downloads

Documentation

API reference

Publisher

verified publisherkakadulabs.com

Weekly Downloads

Beautiful, count-aware, cached cluster marker icons for Google Maps. A drop-in styling/rendering layer for any map clustering algorithm.

Homepage
Repository (GitHub)
View/report issues

Topics

#maps #google-maps #markers #clustering

License

MIT (license)

Dependencies

fluster, flutter, google_maps_cluster_manager_2, google_maps_flutter

More

Packages that depend on cluster_marker_style