mapbox / mapbox/mapbox-maps-flutter
Clusters are not visible if map's style is any other than a default MapboxStyles.LIGHT
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Dart
- Sterne
- 380
- Forks
- 204
- PR-Merge-Kennzahlen
- Keine gemergten PRs in 30 T.
Beschreibung
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:
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<MapScreenBloc>().add(OnPlaceClickEvent(id: annotation.textField ?? ''));
}
}
@RoutePage()
class MapScreen extends StatefulWidget {
const MapScreen({super.key});
@override
State<MapScreen> createState() => _MapScreenState();
}
class _MapScreenState extends StateWithBLoc<MapScreenBloc, MapScreen>
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<PlaceEntity>(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<PlaceEntity> places) async {
mapboxMap.annotations.createPointAnnotationManager().then((pointAnnotationManager) async {
var options = <PointAnnotationOptions>[];
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<Map<String, Object>> 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<IeyeColors>();
final statusBarHeight = context.statusBarHeight;
final navBarHeight = context.navBarHeight;
final height = MediaQuery.of(context).size.height;
return Scaffold(
body: BlocConsumer<MapScreenBloc, MapScreenState>(
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<MapScreenBloc>().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:
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.
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Beginne mit _onMapCreated und setMarkers in der bereitgestellten MapScreen und untersuche anschließend assets/map_styles/style.json sowie die JSON-Dateien der Cluster-Layer. Reproduziere das Problem mit dem bereitgestellten benutzerdefinierten Style und verifiziere, dass die Cluster und der nicht geclusterte Layer sichtbar bleiben, nachdem das Laden des Styles abgeschlossen ist.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- dart
- Bereich
- mobile
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Veraltet
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 35/100