Update map markers of Future Builder

944 views
Skip to first unread message

Fer Buero Trebino

unread,
Sep 4, 2020, 6:44:50 AM9/4/20
to Flutter Development (flutter-dev)
Hello to all the Flutter community! I am about to finish an app, but I have this issue that I cannot solve, I looked online for correct implementation of what I'm trying to achieve without luck.

As you can see in the next image when the user opens this page see a map with the original red marker for the user location and then the markers of the shops.

In order to show this, I create a future builder who first checks the location of the user and then makes an API call who retrieves the shops surrounding the user.

Also, the user can select categories, but this is not important for the problem.

Screenshot_20200904-122130.png

All that I describe before works fine, but my client ask that if the user moves the map, the location of the user should update (considering the new user location as the center of the map) and make the API call again to update the shops surrounding.

I can get the new location of the user based on the map, so _onCameraIdle if the user location is different of the one before, a new API call is made and the markers list is updated, the problem is that the map is nos refreshing the markers, I can see the markers list on the console updating, so I try to put notifyListeners inside the Future where I assign the markers, but if I do this, the future builders enter in a loop.

Can anyone help me to solve this?

How can achieve this? Should I use something different than a Future Builder?


This is the complete code of the screen:

import 'package:app_pickolos/generated/l10n.dart';
import 'package:app_pickolos/src/providers/categories_list_provider.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geolocator/geolocator.dart';
import 'package:provider/provider.dart';

import '../styles.dart';
import 'widgets/background_gradient.dart';

class SearchMap extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var colorValue = Theme.of(context).primaryColorLight;
    Locale myLocale = Localizations.localeOf(context);

    CategoriesListProvider categoriesListProvider =
        Provider.of<CategoriesListProvider>(context);

    LatLng _locationSelected;
    LatLng _lastMapPosition;

    Future _getUserLocation(locationSelected) async {
      Position position = await Geolocator()
          .getCurrentPosition(desiredAccuracy: LocationAccuracy.high);

      if (locationSelected == null) {
        categoriesListProvider.userPositionLon = position.longitude;
        categoriesListProvider.userPositionLat = position.latitude;
        await categoriesListProvider.getMapMarkers(
          context: context,
          lat: position.latitude,
          lon: position.longitude,
        );
      } else {
        categoriesListProvider.userPositionLon = locationSelected.longitude;
        categoriesListProvider.userPositionLat = locationSelected.latitude;
        await categoriesListProvider.getMapMarkers(
          context: context,
          lat: locationSelected.latitude,
          lon: locationSelected.longitude,
        );
      }
    }

    void _onCameraMove(CameraPosition position) {
      _lastMapPosition = (position.target);
      categoriesListProvider.userPositionLon = position.target.longitude;
      categoriesListProvider.userPositionLat = position.target.latitude;
    }

    void _onCameraIdle() {
      if (_lastMapPosition != _locationSelected) {
        _getUserLocation(_locationSelected);
        _locationSelected = _lastMapPosition;
      }
    }

    return Scaffold(
      appBar: AppBar(
        title: Text(AppLocalizations.of(context).mapTitle),
      ),
      body: Container(
        decoration: buildBackgroundGradient(context),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          mainAxisSize: MainAxisSize.min,
          children: [
            DropDownSelect(
                categoriesListProvider: categoriesListProvider,
                myLocale: myLocale),
            SizedBox(
              height: 20,
            ),
            FutureBuilder(
              future: _getUserLocation(_locationSelected),
              builder: (
                context,
                snapshot,
              ) {
                switch (snapshot.connectionState) {
                  case ConnectionState.none:
                    return Text(AppLocalizations.of(context).errorNoConection);
                  case ConnectionState.waiting:
                    return Container(
                      height: 320,
                      child: Center(
                        child: CircularProgressIndicator(
                            valueColor:
                                AlwaysStoppedAnimation<Color>(colorValue)),
                      ),
                    );
                  case ConnectionState.active:
                    return Container(
                      height: 320,
                      child: Center(
                        child: CircularProgressIndicator(
                            valueColor:
                                AlwaysStoppedAnimation<Color>(colorValue)),
                      ),
                    );
                  case ConnectionState.done:
                    if (snapshot.hasError) {
                      return Text(
                        '${snapshot.error}',
                        style: TextStyle(color: Colors.red),
                      );
                    } else {
                      return Container(
                        width: MediaQuery.of(context).size.width * 0.8,
                        height: 320,
                        margin: EdgeInsets.symmetric(horizontal: 16.0),
                        child: ClipRRect(
                          borderRadius: borderRadiusPickolos(),
                          child: Stack(
                            children: [
                              GoogleMap(
                                onCameraMove: _onCameraMove,
                                onCameraIdle: _onCameraIdle,
                                mapType: MapType.normal,
                                initialCameraPosition: CameraPosition(
                                  target: LatLng(
                                      categoriesListProvider.userPositionLat,
                                      categoriesListProvider.userPositionLon),
                                  zoom: 14,
                                ),
                                markers: Set<Marker>.of(
                                    categoriesListProvider.markersList),
                              ),
                            ],
                          ),
                        ),
                      );
                    }
                }
                return Container(
                  height: 320,
                  child: Center(
                    child: CircularProgressIndicator(
                        valueColor: AlwaysStoppedAnimation<Color>(colorValue)),
                  ),
                );
              },
            ),
            Expanded(child: Container()),
            Container(
              width: 60,
              height: 60,
              decoration: BoxDecoration(
                shape: BoxShape.circle,
                color: Colors.white38,
              ),
              child: IconButton(
                icon: Icon(
                  Icons.close,
                  size: 40,
                ),
                onPressed: () {
                  Navigator.popAndPushNamed(context, 'home');
                },
              ),
            ),
            SizedBox(
              height: 40,
            ),
          ],
        ),
      ),
    );
  }
}

class DropDownSelect extends StatelessWidget {
 ***Not important
}


And this is the API call


Future getMapMarkers({context, double lat, double lon}) async {
    final Map<String, String> headers = {
      'Api-Token': _apitoken,
      'Session-Token': _prefs.sessionId,
      'Content-Type': 'application/json'
    };

    var body = {
      "longitude": lon,
      "latitude": lat,
      "maxDistance": 10000,
    };

    final url =
        'XXXXXXXm/api/v1/businesses/by-location';

    var response = await http.post(
      url,
      body: json.encode(body),
      headers: headers,
    );

    final mapBusinessListModel = mapBusinessListFromJson(response.body);

    final bitmapIcon = await BitmapDescriptor.fromAssetImage(
        ImageConfiguration(size: Size(48, 48)), 'assets/map_icon.png');
        
    _markers = mapBusinessListModel
        .map(
          (mapBusinessListModel) => Marker(
            markerId: MarkerId(mapBusinessListModel.id),
            position: LatLng(
              mapBusinessListModel.location.latitude,
              mapBusinessListModel.location.longitude,
            ),
            icon: bitmapIcon,
            infoWindow: InfoWindow(
              snippet: AppLocalizations.of(context).tapToView,
              onTap: () {
                Provider.of<UserProvider>(context, listen: false)
                    .setBusinessDetailID = mapBusinessListModel.id;

                Navigator.pushNamed(context, 'businessPreview');
                print('tap sobre icono mapa');
              },
              title: mapBusinessListModel.name,
            ),
          ),
        )
        .toList();

    _markers.add(Marker(
      markerId: MarkerId('user'),
      position: LatLng(lat, lon),
    ));
    //notifyListeners();
    print('agrgegue a marker list ${_markers.toString()}');
  }

Edimar Martins

unread,
May 10, 2021, 7:48:06 AM5/10/21
to Flutter Development (flutter-dev)
Hi, did you solve your issue to load markers in a Future function without looping ?
I'm struggling with  to solve the same problem.

Thanks
Edimar
Reply all
Reply to author
Forward
0 new messages