New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
disable map reload #5
Comments
Yes same issue here, would be cool if we could do live location updates, like Uber. Maybe process all the widget things in the background ? (Not sure that is possible in flutter) Or maybe once markers are added make it so we can move them without reloading ? |
I believe that for the nature of builders, the reload of the map itself cannot be disabled, but, you can use their own classes of custom markers and markers controller to generate your own custom markers in the state and just pass them to the map Here my code: GoogleMapController? _mapController;
List<Location> locations = [];
late MarkersController markersController;
Set<Marker> markers = {};
void markersControllerListener () {
// This listener only will be called when all the widgets are converted to images
if (markersController.images != null) {
try {
setState(() {
markers = locations.map<Marker> (
(location) => Marker (
markerId: MarkerId (location.id),
position: LatLng (location.lat, location.lon),
onTap: () => widget.openStore (location.id),
icon: BitmapDescriptor.fromBytes(markersController.images! [locations.indexOf(location)])
)
).toSet();
});
} catch (error) {
showErrorDialog(
context,
title: S.of(context).error,
message: S.of(context).anErrorOccurredLoadingMarkers
);
}
}
}
// Call this function in your initState
void _loadLocationMarkers () async {
if (mounted) {
locations = Provider.of<Locations> (context, listen: false).locations;
}
markersController = MarkersController (
value: List<Uint8List?>.filled(locations.length, null),
childCount: locations.length
);
markersController.addListener(markersControllerListener);
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
// Add A generated list of custom markers in the tree of your app to capture the images and convert them to pngs to be added as bytes in the google marker
...(
markersController.ready
? []
: List.generate(
markersController.childCount,
(index) => Positioned(
left: -MediaQuery.of(context).size.width,
child: CustomMarker(
child: locations[index].record.type.marker , // Here i have an enum with a widget called marker
screenshotDelay: Duration.zero, // Remove delay
onImageCaptured: (data) {
markersController.updateRenderedImage(index, data);
},
),
)
)
),
GoogleMap(
myLocationEnabled: runtime != "Development",
myLocationButtonEnabled: false,
tiltGesturesEnabled: false,
zoomControlsEnabled: false,
mapToolbarEnabled: false,
mapType: MapType.normal,
rotateGesturesEnabled: false,
buildingsEnabled: false,
minMaxZoomPreference: const MinMaxZoomPreference(
0, 20
),
onMapCreated: (controller) async {
...
},
markers: markers,
),
],
);
} As a note, I had to extract markers controller from the package cause it is private and custom markers you can use the model from the package located in /src folder. Good Luck! |
I have used the code from @Djcharles26 to try to add Widget containing markers to the map on click of a fab. ^ import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:collection/collection.dart';
import 'dart:ui' as ui;
import 'package:synchronized/synchronized.dart';
class MapOptiTemp extends StatefulWidget {
const MapOptiTemp({Key? key}) : super(key: key);
@override
State<MapOptiTemp> createState() => _MapOptiTempState();
}
class _MapOptiTempState extends State<MapOptiTemp> {
// GoogleMapController? _mapController;
List<Point> locations = [];
late MarkersController markersController;
Set<Marker> markers = {};
double latitude = 0.0;
double longitude = 0.0;
@override
void initState() {
_loadLocationMarkers();
super.initState();
}
void markersControllerListener() {
// This listener only will be called when all the widgets are converted to images
if (markersController.images != null) {
try {
setState(() {
markers = locations
.map<Marker>((point) => Marker(
markerId: MarkerId(point.id),
position: LatLng(point.lat, point.long),
onTap: () {},
icon: BitmapDescriptor.fromBytes(
markersController.images![locations.indexOf(point)])))
.toSet();
});
} catch (error) {
print("Error custom Map: $error");
}
}
}
// Call this function in your initState
void _loadLocationMarkers() async {
if (mounted) {
locations = [
Point("init point 1", 45.413811, 6.991759),
Point("init point 2", 45.414698, 6.988807)
];
}
markersController = MarkersController(
value: List<Uint8List?>.filled(locations.length, null),
childCount: locations.length);
markersController.addListener(markersControllerListener);
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
// Add A generated list of custom markers in the tree of your app to capture the images and convert them to pngs to be added as bytes in the google marker
...(markersController.ready
? []
: List.generate(
markersController.childCount,
(index) => Positioned(
left: -MediaQuery.of(context).size.width,
child: CustomMarker(
child: Icon(Icons.account_circle),
screenshotDelay: Duration.zero, // Remove delay
onImageCaptured: (data) {
markersController.updateRenderedImage(index, data);
},
),
))),
GoogleMap(
initialCameraPosition: CameraPosition(
target: LatLng(45.449320496612295, 6.976185903919156)),
myLocationButtonEnabled: false,
tiltGesturesEnabled: false,
zoomControlsEnabled: false,
mapToolbarEnabled: false,
mapType: MapType.hybrid,
rotateGesturesEnabled: false,
buildingsEnabled: false,
onCameraMove: (object) {
print("Markers : $markers");
latitude = object.target.latitude;
longitude = object.target.longitude;
},
minMaxZoomPreference: const MinMaxZoomPreference(0, 20),
onMapCreated: (controller) async {},
markers: markers,
),
Positioned(
bottom: 16,
left: 0,
right: 0,
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () async {
print("latitude $latitude");
print("longitude $longitude");
locations.add(Point("New point", latitude, longitude));
markersController.prepareAddWidget();
markersController.childCount = locations.length;
setState(() {});
}))
],
);
}
}
class Point {
String id = "";
double lat = 0.0;
double long = 0.0;
Point(this.id, this.lat, this.long);
}
/// [MarkersController] handles the state of rendered markers and notify
/// listeners when all marker are rendered and captured.
class MarkersController extends ValueNotifier<List<Uint8List?>> {
int childCount = 0;
List<Uint8List?> renderedWidgets = List.empty(growable: true);
MarkersController({required List<Uint8List?> value, required this.childCount})
: super(value) {
renderedWidgets = List<Uint8List?>.filled(childCount, null);
}
updateRenderedImage(int index, Uint8List? data) {
renderedWidgets[index] = data;
if (ready) {
value = List.from(renderedWidgets);
}
}
bool get ready => !renderedWidgets.any((image) => image == null);
void prepareAddWidget() {
renderedWidgets = renderedWidgets.toList();
renderedWidgets.add(null);
childCount++;
}
List<Uint8List>? get images => ready ? value.cast<Uint8List>() : null;
}
////////////////CUSTOM MARKER//////////////////////
/// a widgets that capture a png screenshot of its content and pass it through
/// [onImageCaptured].
class CustomMarker extends StatefulWidget {
final Widget child;
final Function(Uint8List?)? onImageCaptured;
final Duration? screenshotDelay;
const CustomMarker(
{Key? key,
required this.child,
this.onImageCaptured,
this.screenshotDelay})
: super(key: key);
@override
_CustomMarkerState createState() => _CustomMarkerState();
}
class _CustomMarkerState extends State<CustomMarker> {
final GlobalKey key = GlobalKey();
final Function eq = const ListEquality().equals;
Uint8List? _lastImage;
final lock = Lock();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) {
lock.synchronized(() async {
await Future.delayed(
widget.screenshotDelay ?? const Duration(milliseconds: 500));
final _image = await _capturePng(key);
if (_lastImage == null || !eq(_lastImage!, _image)) {
_lastImage = _image;
widget.onImageCaptured?.call(_image);
} else {
widget.onImageCaptured?.call(_lastImage);
}
});
});
return RepaintBoundary(
key: key,
child: widget.child,
);
}
Future<Uint8List?> _capturePng(GlobalKey iconKey) async {
try {
final RenderRepaintBoundary? boundary =
iconKey.currentContext?.findRenderObject() as RenderRepaintBoundary?;
if (kDebugMode && (boundary?.debugNeedsPaint ?? false)) {
await Future.delayed(const Duration(milliseconds: 200));
return _capturePng(iconKey);
}
ui.Image? image = await boundary?.toImage(pixelRatio: 3.0);
ByteData? byteData =
await image?.toByteData(format: ui.ImageByteFormat.png);
var pngBytes = byteData?.buffer.asUint8List();
return pngBytes;
} catch (e) {
return null;
}
}
}
|
- Solved marker blinking issue every time when map reloads or when we add a marker to the existing list or remove or update the marker list. issue related to IbrahimTabba#5
reload I think is so bad UX for user
an example is for test data which build it in if we get data from the server and wait for data and reload the map takes many seconds.
please find a right way to implement this issue
The text was updated successfully, but these errors were encountered: