Flutter Google maps not reloading after changing markers - flutter

I'm working with the google map widget, when I render the map for the first time with static markers it displays all the markers, but when I get the data from API and try to add new markers to the map, it won't display them.
below the code that i'm using :
class Map extends StatefulWidget {
final Position initialPosition;
final List<Marker> allSitesMarkers;
final List<Site> placesSite;
final List<String> mapMarkersCategories;
const Map(this.initialPosition, this.allSitesMarkers, this.placesSite,
this.mapMarkersCategories,
{Key key})
: super(key: key);
#override
State<StatefulWidget> createState() => MapState();
}
class MapState extends State<Map> {
Completer<GoogleMapController> _controller = Completer();
String currentCategoryFilter;
bool filtred = false;
Set<Marker> _markers = Set();
List<Marker> markersToShow;
#override
void initState() {
super.initState();
refreshMarkers();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: (widget.placesSite.isNotEmpty)
? Stack(
children: [
GoogleMap(
initialCameraPosition: const CameraPosition(
target: LatLng(36.798, 10.1717), zoom: 15),
mapType: MapType.normal,
myLocationEnabled: true,
markers: _markers,
onMapCreated: (GoogleMapController controller) {
controller.setMapStyle(
'[ //... ]');
_controller.complete(controller);
},
),
_chips()
],
)
: const NoListMap(),
);
}
Widget _chips() {
//..
}
filterSites(String category) {
setState(() {
_markers.clear();
filtred = true;
markersToShow = widget.allSitesMarkers
.where((element) => element.infoWindow.title == category)
.toList();
});
refreshMarkers();
}
refreshMarkers() {
if (filtred) {
setState(() {
_markers.clear();
_markers.addAll(markersToShow);
});
} else {
setState(() {
_markers.clear();
_markers.addAll(widget.allSitesMarkers);
});
}
}
}
I'm using:
Flutter 2.10.2
Dart 2.16.1
google_maps_flutter : 2.0.9

Your _markers is a Set, and in setState you use clear and addAll to empty it and to add new items to the set. But the value of _markers does not change when you do so, therefore it will not trigger a rebuild.
This happens because _markers holds a reference to the Set (like lists, objects etc.).
You need to assign a new value to _markers to trigger a rebuild, for example:
setState(() {
_markers = markersToShow.toSet();
});
This will actually change the reference kept in _markers.

Related

Firebase Firestore and Google Maps Custom Marker only showing one marker

I'm trying to use the plugin Custom Marker to show on google maps, I got it close to working but it's only showing one marker from my model when I expect it to show all of them. Why is firestore only showing the one marker, how do I get the full list to show all of the markers instead of just one?
Edit 1: Removed old create marker function, that worked to make normal google map markers. Now all that's left is functioning code, it just only shows one marker instead of the entire list.
Edit 2: After reviewing the initial suggestion below I realize I'm getting a similar error message as to their suggestion.
Edit 3: Working code is at the bottom per suggestion by #Denzel
Unhandled Exception: setState() callback argument returned a Future.
E/flutter (31835): The setState() method on _HomePageState#73fe9 was called with a closure or method that returned a Future. Maybe it is marked as "async".
E/flutter (31835): Instead of performing asynchronous work inside a call to setState(), first execute the work (without updating the widget state), and then synchronously update the state inside a call to setState().
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:custom_marker/marker_icon.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:flutter/material.dart';
import '../models/marker_collect_model.dart';
class CustomMarkies extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<CustomMarkies> {
Set<Marker> list = <Marker>{};
List<String> listDocuments = [];
Future<void> readDataFromFirebase() async {
FirebaseFirestore firestore = FirebaseFirestore.instance;
CollectionReference<Map<String, dynamic>> collectionReference =
firestore.collection('2022TABR');
collectionReference.snapshots().listen((event) {
List<DocumentSnapshot> snapshots = event.docs;
for (var map in snapshots) {
Map<String, dynamic> data =
map.data() as Map<String, dynamic>; // add this line
MarkerCollectModel model =
MarkerCollectModel.fromMap(data); // use data here
String nameDocument = map.id;
listDocuments.add(nameDocument);
setState(() async {
Random random = Random();
int i = random.nextInt(10000);
String idString = 'id$i';
list.add(Marker(
markerId: MarkerId(idString),
icon: await MarkerIcon.downloadResizePicture(
url: model.urlavatar!, imageSize: 250),
position: LatLng(model.lat!, model.lng!),
);
});
}
});
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GoogleMap(
initialCameraPosition: CameraPosition(target: LatLng(45.4279, -123.6880), zoom: 3),
markers: list,
),
floatingActionButton: FloatingActionButton.extended(
label: FittedBox(child: Text('Add Markers')),
onPressed: () async {
readDataFromFirebase();
list.toSet();
setState(() {});
},
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}
working code
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:custom_marker/marker_icon.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:flutter/material.dart';
import '../models/marker_collect_model.dart';
class CustomMarkies extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<CustomMarkies> {
Set<Marker> list = <Marker>{};
List<String> listDocuments = [];
Future<void> readDataFromFirebase() async {
FirebaseFirestore firestore = FirebaseFirestore.instance;
CollectionReference<Map<String, dynamic>> collectionReference =
firestore.collection('2022TABR');
collectionReference.snapshots().listen((event) async {
List<DocumentSnapshot> snapshots = event.docs;
for (var map in snapshots) {
Map<String, dynamic> data =
map.data() as Map<String, dynamic>; // add this line
MarkerCollectModel model =
MarkerCollectModel.fromMap(data); // use data here
String nameDocument = map.id;
listDocuments.add(nameDocument);
list.add(Marker(
markerId: MarkerId(nameDocument),
icon: await MarkerIcon.downloadResizePicture(
url: model.urlavatar!, imageSize: 250),
position: LatLng(model.lat!, model.lng!),
));
}
});
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GoogleMap(
initialCameraPosition: CameraPosition(target: LatLng(45.4279, -123.6880), zoom: 3),
markers: list,
),
floatingActionButton: FloatingActionButton.extended(
label: FittedBox(child: Text('Add Markers')),
onPressed: () async {
readDataFromFirebase();
list.toSet();
setState(() {});
},
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}
class CustomMarkies extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<CustomMarkies> {
Set<Marker> list = <Marker>{};
List<String> listDocuments = [];
/// This [async] here covers for everything within the function,
/// You can use [await] anywhere you want to
/// [collectionReference.snapshots()] returns a [Stream], so you [listen] to it
/// There's no need for [await] because [Stream] handles real-time data
/// In essence, it would update all it's [listeners] about the new change and this change will take effect in your UI
Future<void> readDataFromFirebase() async {
FirebaseFirestore firestore = FirebaseFirestore.instance;
CollectionReference<Map<String, dynamic>> collectionReference =
firestore.collection('2022TABR');
collectionReference.snapshots().listen((event) {
for (var map in snapshots) {
Map<String, dynamic> data =
map.data() as Map<String, dynamic>; // add this line
MarkerCollectModel model =
MarkerCollectModel.fromMap(data); // use data here
String nameDocument = map.id;
listDocuments.add(nameDocument);
list.add(Marker(
markerId: MarkerId(nameDocument),
icon: await MarkerIcon.downloadResizePicture(
url: model.urlavatar!, imageSize: 250),
position: LatLng(model.lat!, model.lng!),
));
}
});
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GoogleMap(
initialCameraPosition:
CameraPosition(target: LatLng(45.4279, -123.6880), zoom: 3),
markers: list,
),
floatingActionButton: FloatingActionButton.extended(
label: FittedBox(child: Text('Add Markers')),
onPressed: () async {
// you have to `await readDataFromFirebase()` because it is a future.
await readDataFromFirebase();
list.toSet();
setState(() {});
},
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}
I put the comments to explain further what I meant

how i can use google map to get current location or choose location?

i start with this code but all what i get white screen
how i can use google map to get current location or choose location.I am using GoogleMap package in flutter to get the required current location.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget { #override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Google Maps Demo', home: MapSample(), ); } }
class MapSample extends StatefulWidget { #override
State createState() => MapSampleState();
}
class MapSampleState extends State {
Completer _controller = Completer();
static final CameraPosition _kGooglePlex = CameraPosition( target:
LatLng(37.42796133580664, -122.085749655962), zoom: 14.4746, );
static final CameraPosition _kLake = CameraPosition( bearing:
192.8334901395799, target: LatLng(37.43296265331129,
-122.08832357078792), tilt: 59.440717697143555, zoom:
19.151926040649414);
#override Widget build(BuildContext context) { return new Scaffold(
body: GoogleMap(
mapType: MapType.hybrid, initialCameraPosition: _kGooglePlex,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
),
floatingActionButton:
FloatingActionButton.extended(
onPressed: _goToTheLake,
label:
Text('To the lake!'),
icon: Icon(Icons.directions_boat), ),
);
}
Future _goToTheLake() async {
final GoogleMapController controller = await _controller.future;
controller.animateCamera(CameraUpdate.newCameraPosition(_kLake));
}
}
If you're using Url properly(no spelling/parameter mistakes) with API key and there's no error from the server which happens a lot due to restrictions. Then there are other steps you need to, ask Location permission on app loading using https://pub.dev/packages/permission_handler package like this.
Code can be something like this.
Create an app_persmission_provider file.
class AppPermissionProvider with ChangeNotifier {
PermissionStatus _locationStatus = PermissionStatus.denied;
LatLng? _locationCenter;
final location_package.Location _location =
location_package.Location();
location_package.LocationData? _locationData;
// Getter
get locationStatus => _locationStatus;
get locationCenter => _locationCenter;
get location => _location;
void getLocationStatus() async {
final status = await Permission.location.request();
_locationStatus = status;
notifyListeners();
print(_locationStatus);
}
void getLocation() async {
_locationData = await _location.getLocation();
_locationCenter = LatLng(
_locationData!.latitude as double, _locationData!.longitude as double);
notifyListeners();
}
}
Then declare the provider on the root maybe, and In my case, I'm initializing some functions onInit. By doing so, android will ask for permission for location when your page loads.
#override
void initState() {
super.initState();
appPermission = Provider.of<AppPermissionProvider>(context, listen: false);
appPermission.getLocationStatus();
appPermission.getLocation();
}
And then using consumer.
SafeArea(
child: Consumer<AppPermissionProvider>(
builder: (context, appPermissionProvider, _) => appPermission
.locationCenter ==
null
? const Center(child: CircularProgressIndicator())
:
GoogleMap( myLocationButtonEnabled:
appPermissionProvider.locationStatus ==
PermissionStatus.granted
? true
: false,
myLocationEnabled: true,
initialCameraPosition: CameraPosition(
target: appPermission.locationCenter,
zoom: 14,
),
onMapCreated: onMapCreated,
mapType: MapType.normal,
compassEnabled: true,
),
),
)
Here, I am checking if permission is granted, also I am only enabling myLocationButton if location permission is granted.
You can use google_maps_flutter package ,
A short example how you can use it.
For more clearity you can visit
Here or you can use geolocator.
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
LatLng _initialcameraposition = LatLng(20.5937, 78.9629);
GoogleMapController _controller;
Location _location = Location();
void _onMapCreated(GoogleMapController _cntlr)
{
_controller = _cntlr;
_location.onLocationChanged.listen((l) {
_controller.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(target: LatLng(l.latitude, l.longitude),zoom: 15),
),
);
});
}

LateInitializationError: Field 'currentLatLng' has not been initialized in Flutter

I am displaying my current location on google maps using this library: package:location/location.dart. Pub Dev Link
The problem is whenever I open the app in release mode it crashes and shows:
LateInitializationError: Field 'currentLatLng' has not been
initialized
It does not crash in debug mode on an Android Device.
It does, however, crash on iOS (release and debug mode).
I am not sure what I am doing wrong. Here's my widget and attempt:
class TestGoogle extends StatefulWidget {
const TestGoogle({Key? key}) : super(key: key);
#override
_TestGoogleState createState() => _TestGoogleState();
}
class _TestGoogleState extends State<TestGoogle> {
late LatLng currentLatLng;
late GoogleMapController mapController;
Location location = new Location();
var latitude;
var longitude;
late LocationData _locationData;
get() async {
_locationData = await location.getLocation();
latitude = _locationData.latitude;
longitude = _locationData.longitude;
setState(() {
currentLatLng = new LatLng(latitude, longitude);
});
}
#override
initState() {
super.initState();
get();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GoogleMap(
myLocationEnabled: true,
onCameraMove: (CameraPosition cameraPosition) {
print(cameraPosition.zoom);
},
initialCameraPosition:
CameraPosition(target: currentLatLng, zoom: 15.0),
),
);
}
}
The below codes should fix your problem. Your problem reason, try use variable before initializing.
class TestGoogle extends StatelessWidget {
TestGoogle({Key? key}) : super(key: key);
late GoogleMapController mapController;
Location location = Location();
Future<LatLng> get() async {
final _locationData = await location.getLocation();
return LatLng(_locationData.latitude, _locationData.longitude);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<LatLng>(
future: get(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final locationModel = snapshot.data!;
final latitude = locationModel.latitude;
final longitude = locationModel.longitude;
return GoogleMap(
myLocationEnabled: true,
onCameraMove: (CameraPosition cameraPosition) {
print(cameraPosition.zoom);
},
initialCameraPosition: CameraPosition(target: locationModel, zoom: 15.0),
);
}
return const CircularProgressIndicator();
},
),
);
}
}
I Fixed this by
class TestGoogle extends StatefulWidget {
const TestGoogle({Key? key}) : super(key: key);
#override
_TestGoogleState createState() => _TestGoogleState();
}
class _TestGoogleState extends State<TestGoogle> {
LatLng? currentLatLng;
late GoogleMapController mapController;
Location location = new Location();
var latitude;
var longitude;
late LocationData _locationData;
get() async {
_locationData = await location.getLocation();
latitude = _locationData.latitude;
longitude = _locationData.longitude;
setState(() {
currentLatLng = new LatLng(latitude, longitude);
});
}
#override
initState() {
super.initState();
get();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: currentLatLng == null
? Center(child: PlatformCircularProgressIndicator())
: GoogleMap(
myLocationEnabled: true,
onCameraMove: (CameraPosition cameraPosition) {
print(cameraPosition.zoom);
},
initialCameraPosition:
CameraPosition(target: currentLatLng, zoom: 15.0),
),
);
}
}

Flutter setState from another class

So I have this 2 classes
class MapsModel {
MapsModel._();
...
static void Function(VoidCallback) setState;
static GoogleMap googleMap = new GoogleMap(
markers: markers,
onMapCreated: (GoogleMapController controller) {
setState((){
log("Marker added");
markers.add(
Marker(
markerId: MarkerId("<MARKER_ID>"),
position: LatLng(45.726697, 25.521418)
)
);
});
},
);
}
and
class MapPage extends StatefulWidget {
MapPage({Key key}) : super(key: key);
#override
State<StatefulWidget> createState() => MapPageState();
}
class MapPageState extends State<MapPage> {
#override
Widget build(BuildContext context) {
MapsModel.setState = (VoidCallback callback){
setState(() {
callback();
});
};
return Scaffold(
body: MapsModel.googleMap
);
}
}
So what I want to do is to update the map markers from the first class, using a custom setState function.
The problem is that even the message: Marker Added is shown, the map does not add the marker. Also when I change to another page and back, the marker is added.

cant set my location on flutter google maps

I am newbie in flutter, I am trying to set my location as the center point of the map that the app draw on the screen
I am using statefulwidget as the root of my app and add this code :
class MyMap extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyMapState();
}
}
class MyMapState extends State<MyMap> {
GoogleMapController googleMapController;
LocationData currentLocation;
LocationData distinationLocation;
Location location;
#override
void initState() {
location = Location();
setInitSourceAndDestination();
super.initState();
}
setInitSourceAndDestination() async {
currentLocation = await location.getLocation();
}
#override
Widget build(BuildContext context) {
CameraPosition initialCameraPosition = CameraPosition(
target: LatLng(currentLocation.latitude, currentLocation.longitude),);
return GoogleMap(
initialCameraPosition: initialCameraPosition,
onMapCreated: (GoogleMapController controller) => googleMapController = controller,
mapType: MapType.normal,
tiltGesturesEnabled: false,
compassEnabled: true,
myLocationEnabled: true,
);
}
}
But there are a problem I can not solve:
the map is drawn in the screen before currentLocation is set
I tried setState and the problem has not been solved.
How can I make the app draw the map after setInitSourceAndDestination method finish excuting?
What make me confused is that the code at this form the build method will be excuted before setInitSourceAndDestination method finished, but if I add setState and change the currentLocation value inside it to rebuild the screen I noticed that setState executed before build function but it still dont show my location
Add a condition to check the state. If the state is null, then draw a loading indicator. If not null, draw the maps
it can be look like this :
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';
class MapScreen extends StatefulWidget {
#override
_MapScreenState createState() => _MapScreenState();
}
class _MapScreenState extends State<MapScreen> {
double lat;
double long;
LatLng _pickedLoc;
Future _getLocation() async {
LocationData location = await Location().getLocation();
setState(() {
lat = location.latitude;
long = location.longitude;
});
}
_selectLocation(LatLng location) {
_pickedLoc = location;
}
#override
void initState() {
super.initState();
_getLocation();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: lat == null || long == null
? Center(
child: CircularProgressIndicator(),
)
: GoogleMap(
initialCameraPosition: CameraPosition(
zoom: 16,
target: LatLng(lat, long),
),
onTap: _selectLocation,
onCameraMove: (object) => {debugPrint(object.target.toString())},
markers: {
Marker(
markerId: MarkerId("m1"),
position: _pickedLoc,
)
},
),
);
}
}