mapbox / mapbox/mapbox-maps-flutter

Clusters are not visible if map's style is any other than a default MapboxStyles.LIGHT

Aperta
#349 2 commenti 1 reazione 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Lingua principale
Dart
Stelle
380
Fork
204
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Descrizione

Version mapbox_maps_flutter: ^0.5.1 is being used.
Flutter version 3.16.2

If I apply my own map style other than a default `MapboxStyles.LIGHT`, all clusters and `unClusteredLayer` are below the main map layout and not visible - means that I see them during some milliseconds (before map layout was loaded) and later map just covers them. Even if I use `MapboxStyles.DARK` the same problem exists.

Here is my code:

```dart
import 'dart:convert';

import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:ieye/features/map/domain/entities/lat_lng.dart';
import 'package:ieye/index.dart';
import 'package:ieye/router/router.gr.dart';
import 'package:ieye/theme.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';

class AnnotationClickListener extends OnPointAnnotationClickListener {
final BuildContext context;

AnnotationClickListener({required this.context});
@override
void onPointAnnotationClick(PointAnnotation annotation) {
context.read().add(OnPlaceClickEvent(id: annotation.textField ?? ''));
}
}

@RoutePage()
class MapScreen extends StatefulWidget {
const MapScreen({super.key});

@override
State createState() => _MapScreenState();
}

class _MapScreenState extends StateWithBLoc
with TickerProviderStateMixin, AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;

@override
void onNewsReceived(BlocNews news) {
if (news is ShowPlaceDetailsScreen) {
AutoRouter.of(context).pop();
context.router.push(PlaceDetailsRoute(place: news.place));
} else if (news is ShowPlaceDetailsBottomSheet) {
final place = news.place;
final size = MediaQuery.of(context).size;
showModalBottomSheet(
useRootNavigator: true,
backgroundColor: Colors.white,
context: context,
isScrollControlled: true,
isDismissible: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(24.0))),
clipBehavior: Clip.hardEdge,
builder: (_) {
return DraggableScrollableSheet(
initialChildSize: 0.7,
maxChildSize: ((size.height - context.statusBarHeight) / size.height) * 0.95,
minChildSize: 0.5,
expand: false,
snap: false,
builder: (_, scrollController) {
return VenueShortBottomSheet(
key: ValueKey(place),
scrollController: scrollController,
place: place,
onDetailsTap: () {
bloc.add(ShowPlaceDetailsScreenEvent(place: place));
},
); //whatever you're returning, does not have to be a Container
});
},
);
}
}

final pageController = PageController();
late TextEditingController controller;
late MapboxMap mapboxMap;

Position center = Position(-0.1882464, 51.5382123);
Position currentPosition = Position(-0.1882464, 51.5382123);
double zoom = 10.0;
bool mapLoaded = false;

_onMapCreated(MapboxMap mapbox) async {
setState(() {
mapboxMap = mapbox;
mapLoaded = true;
mapboxMap.style;
});
final styleJson = await rootBundle.loadString('assets/map_styles/style.json');
mapboxMap.style.setStyleJSON(styleJson);
mapboxMap.compass.updateSettings(CompassSettings(enabled: false, opacity: 0.0));
mapboxMap.logo.updateSettings(LogoSettings(marginBottom: -100.0));
mapboxMap.attribution.updateSettings(AttributionSettings(marginBottom: -100.0));
mapboxMap.scaleBar.updateSettings(ScaleBarSettings(enabled: false));
mapboxMap.gestures.updateSettings(GesturesSettings(rotateEnabled: false));
mapboxMap.location.updateSettings(LocationComponentSettings(enabled: false));
}

_onMapIdleListener(_) async {
final cam = await mapboxMap.getCameraState();

final coordinates = cam.center['coordinates'] as List;
setState(() {
zoom = cam.zoom;
currentPosition = Position(
num.tryParse(coordinates.first.toString()) ?? 0.0,
num.tryParse(coordinates.last.toString()) ?? 0.0,
);
});
}

@override
void initState() {
super.initState();
controller = TextEditingController();
}

@override
void dispose() {
controller.dispose();
mapboxMap.dispose();
super.dispose();
}

void setZoom(double value) {
if (value > 18 || value < 4) return;
setState(() => zoom = value);
mapboxMap.setCamera(CameraOptions(
center: Point(coordinates: currentPosition).toJson(),
zoom: value,
));
}

void setCenter() {
mapboxMap.setCamera(CameraOptions(
center: Point(coordinates: center).toJson(),
zoom: zoom,
));
}

void onTap() {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
FocusManager.instance.primaryFocus?.unfocus();
}
}

void setMarkers(BuildContext context, List places) async {
mapboxMap.annotations.createPointAnnotationManager().then((pointAnnotationManager) async {
var options = [];

for (var i = 0; i < places.length; i++) {
final point = points[i];
final place = places[i];

final widget = IEyeMarker(
place: place,
onDetailsTap: () {},
);

final Uint8List? image = await createImageFromWidget(context, widget);

options.add(
PointAnnotationOptions(
geometry: Point(coordinates: Position(point.lng, point.lat)).toJson(),
image: image,
textField: place.id.toString(),
textColor: 0x00000000,
iconSize: 1,
),
);
}

pointAnnotationManager.createMulti(options);
pointAnnotationManager
.addOnPointAnnotationClickListener(AnnotationClickListener(context: context));
});

List> features = [];

for (var i = 0; i < places.length; i++) {
final point = points[i];
final place = places[i];
features.add({
'type': 'Feature',
'id': place.id,
'properties': {
'point_count_abbreviated': '1',
'cluster_id': place.id,
// 'cluster': true,
// 'point_count': 1
},
'geometry': {
'type': 'Point',
'coordinates': [point.lng, point.lat, 0.0]
}
});
}

final data = {
'type': 'FeatureCollection',
'crs': {
'type': 'name',
'properties': {'name': 'urn:ogc:def:crs:OGC:1.3:CRS84'}
},
'features': features,
};

final layout = {
'type': 'geojson',
'data': data,
'cluster': true,
'clusterMaxZoom': 14,
'clusterRadius': 50,
};

mapboxMap.style.styleSourceExists('places').then((value) async {
if (!value) {
var source = jsonEncode(layout);
mapboxMap.style.addStyleSource('places', source.toString());
}
});

mapboxMap.style.styleLayerExists('clusters').then((value) async {
if (!value) {
var layer = await rootBundle.loadString('assets/cluster/cluster_layer.json');
mapboxMap.style.addStyleLayer(layer, null);

var clusterCountLayer =
await rootBundle.loadString('assets/cluster/cluster_count_layer.json');
mapboxMap.style.addStyleLayer(clusterCountLayer, null);

// var unClusteredLayer =
// await rootBundle.loadString('assets/cluster/unclustered_point_layer.json');
// mapboxMap.style.addStyleLayer(unClusteredLayer, null);
}
});
}

void setPositionFromGps(LatLng? position) {
setState(() => center = Position(position?.lng ?? 0.0, position?.lat ?? 0.0));

mapboxMap.setCamera(
CameraOptions(
center: Point(coordinates: Position(position?.lng ?? 0.0, position?.lat ?? 0.0)).toJson(),
),
);
}

@override
Widget build(BuildContext context) {
super.build(context);
final colors = context.theme.extension();
final statusBarHeight = context.statusBarHeight;
final navBarHeight = context.navBarHeight;
final height = MediaQuery.of(context).size.height;

return Scaffold(
body: BlocConsumer(
listener: (BuildContext context, MapScreenState state) {
if (state.places.isNotEmpty && mapLoaded) {
setMarkers(context, state.places);
}
if (state.gpsPosition != null) {
// setPositionFromGps(state.gpsPosition);
}
},
bloc: bloc,
builder: (context, state) {
return Stack(
children: [
MapWidget(
key: const ValueKey('mapWidget'),
resourceOptions: ResourceOptions(
accessToken:
'pk.eyJ1IjoiaWV5ZSIsImEiOiJjbGN2dDlhaWkxMG44M3BxdHIyb3MyeHZpIn0.D9Uo5M87jYRCnHU7qDYVMQ',
),
styleUri: MapboxStyles.LIGHT,
cameraOptions: CameraOptions(
center: Point(coordinates: currentPosition).toJson(),
zoom: zoom,
),
onMapCreated: (MapboxMap mapboxMap) {
_onMapCreated(mapboxMap);
context.read().add(OnMapInitEvent());
},
onMapIdleListener: _onMapIdleListener,
),
Positioned(
left: 8,
right: 8,
top: statusBarHeight,
child: const MapSearchTextFieldView(),
),
Positioned(
right: 8,
bottom: navBarHeight + 92,
child: RoundButton(
onTap: setCenter,
iconAssetPath: UiAssets.navigationFalse,
borderRadius: 24,
backgroundColor: colors?.opacityWhite80,
),
),
Positioned(
right: 8,
top: (height - 48 - 48 - 12) / 2,
child: RoundButton(
onTap: () => setZoom(zoom + 1),
iconAssetPath: UiAssets.plus,
borderRadius: 24,
backgroundColor: colors?.opacityWhite80,
),
),
Positioned(
right: 8,
bottom: (height - 48 - 48 - 12) / 2,
child: RoundButton(
onTap: () => setZoom(zoom - 1),
iconAssetPath: UiAssets.minus,
borderRadius: 24,
backgroundColor: colors?.opacityWhite80,
),
),
],
);
},
),
);
}
}

final points = [
LatLng(lat: 51.5382123, lng: -0.1882464),
LatLng(lat: 51.4382123, lng: -0.1982464),
LatLng(lat: 51.5090229, lng: -0.2886548),
LatLng(lat: 51.1090229, lng: -0.2786548),
LatLng(lat: 51.5054563, lng: -0.0798412),
LatLng(lat: 51.5090215, lng: -0.1959988),
LatLng(lat: 51.5190215, lng: -0.1859988),
LatLng(lat: 51.5577676, lng: -0.2008447),
];

```

Just for info here is my flutter doctor result:

```dart
Doctor summary (to see all details, run flutter doctor -v):
[!] Flutter (Channel stable, 3.16.2, on macOS 14.2.1 23C71 darwin-arm64, locale ru-RU)
! Warning: `dart` on your path resolves to /opt/homebrew/Cellar/dart-sdk/3.1.5/libexec/bin/dart, which is not inside your current Flutter SDK checkout at /Users/andreifufylev/fvm/versions/3.16.2. Consider
adding /Users/andreifufylev/fvm/versions/3.16.2/bin to the front of your path.
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 15.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2023.1)
[✓] IntelliJ IDEA Community Edition (version 2023.2.5)
[✓] VS Code (version 1.85.1)
[✓] Connected device (4 available)
[✓] Network resources

! Doctor found issues in 1 category.
```

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia con _onMapCreated e setMarkers nel MapScreen fornito, quindi esamina assets/map_styles/style.json e i file JSON dei layer dei cluster. Riproduci il problema con lo stile personalizzato fornito e verifica che i cluster e il layer non raggruppato rimangano visibili dopo il completamento del caricamento dello stile.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
dart
Ambito
mobile
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
35/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.