mapbox-maps-flutter
mapbox-maps-flutter copied to clipboard
Clusters are not visible if map's style is any other than a default MapboxStyles.LIGHT
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.
Any updates on that?
no ((