Карта не отображается в местоположении пользователя при загрузке экрана

Я все еще новичок в Flutter / Dart, использую шаблон блока / репозитория flutter_bloc, и у меня странное поведение в моем приложении. Все распечатки показывают, что все потоки и шаблон блока работают правильно, входящие состояния несут координаты, а переменная, содержащая самые новые координаты, никогда не равна нулю, но при загрузке MapScreen она не рисует карту в координатах userLocation. Также при нажатии центральной кнопки иногда появляется The method 'move' was called on null. ошибка.

В MapScreen userLocation, который получает свое значение из входящего состояния

return BlocBuilder<MapBloc, MapState>(
//        bloc: MapBloc(mapRepository: _mapRepository),
        builder: (BuildContext context, MapState state) {
      userLocation = (state as LocationStream).location;
//      if (state is LocationStream) {
//        userLocation = (state).location;
//      }
      return Scaffold( 

затем используется для рисования карты в местоположении пользователя

child: FlutterMap(
                    options: MapOptions(
                      center: userLocation,
                      minZoom: 5.0,
                      maxZoom: 19.0,
                    ),
                    mapController: _mapController,

для отображения пользователя на карте

Marker(
       point: userLocation,
       height: 200,
       width: 200,
       builder: (context) => IconButton(
           icon: Icon(Icons.location_on),
           color: Colors.red,
           iconSize: 60,
           onPressed: () {
            print('icon tapped');
           },
       ),
     ),

и центрировать карту относительно местоположения пользователя.

RaisedButton(
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(5)),
                      onPressed: () {
                        print(
                            ' @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   userLocation is $userLocation');
                        _mapController.move(userLocation, 16);
                      },
                      color: Colors.red,
                      child: Padding(
                        padding: EdgeInsets.all(8.0),
                        child: Text(
                          'center',
                          style: TextStyle(color: Colors.white, fontSize: 30),
                        ),
                      ),
                    ),

Странно то, что иногда, когда я нажимаю центральную кнопку, я получаю The method 'move' was called on null. ошибку, но не всегда.

Иногда срабатывает каждый раз, когда я нажимаю на нее, но иногда она находит значение null после того, как несколько раз сработало.

При первой сборке центральная кнопка работает должным образом и центрирует карту, но всегда находит значение null после горячей перезагрузки или горячего перезапуска.

Вы видите, что я делаю не так? Большое спасибо за ваше время и помощь.

Полный код пользовательского интерфейса:

class MapScreen extends StatelessWidget {
  final String name;
  final MapRepository _mapRepository;
  final MapController _mapController;
//  final AlertRepository _alertRepository;
  List<Marker> alerts;
  LatLng userLocation;
  MapScreen(
      {Key key, @required this.name, @required MapRepository mapRepository})
      : assert(mapRepository != null),
        _mapRepository = mapRepository,
        _mapController = MapController(),
//        _alertRepository = alertRepository,
        super(key: key);


  @override
  Widget build(BuildContext context) {
    return BlocBuilder<MapBloc, MapState>(
//        bloc: MapBloc(mapRepository: _mapRepository),
        builder: (BuildContext context, MapState state) {
      userLocation = (state as LocationStream).location;
//      if (state is LocationStream) {
//        userLocation = (state).location;
//      }
      return Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.transparent,
          elevation: 0,
          title: Text(
            'Home',
            style: TextStyle(color: Colors.orangeAccent, fontSize: 40),
          ),
          actions: <Widget>[
            IconButton(
              icon: Icon(
                Icons.exit_to_app,
                color: Colors.orange,
                size: 35,
              ),
              onPressed: () {
                BlocProvider.of<AuthenticationBloc>(context).add(
                  LoggedOut(),
                );
              },
            ),
          ],
        ),
        backgroundColor: Colors.white,
        body: SafeArea(
          minimum: EdgeInsets.symmetric(horizontal: 20),
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Container(
                  height: 570,
                  width: 320,
                  child: FlutterMap(
                    options: MapOptions(
                      center: userLocation,
                      minZoom: 5.0,
                      maxZoom: 19.0,
                    ),
                    mapController: _mapController,
                    layers: [
                      //
//        PolygonLayer(polygonOpts, map, stream)
//                    PolygonLayerOptions(
//                      polygons:
//                    ),
                      TileLayerOptions(
                          urlTemplate:
                              'https://api.openrouteservice.org/mapsurfer/{z}/{x}/{y}.png?api_key=5b3ce3597851110001cf62484c4b65d85bc844eca3a2c6b9f300ddf4',
//                              urlTemplate:
//                                  'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
                          subdomains: ['a', 'b', 'c'],
                          keepBuffer: 20),
                      new MarkerLayerOptions(
                        markers: [
                          Marker(
                            point: userLocation,
                            height: 200,
                            width: 200,
                            builder: (context) => IconButton(
                              icon: Icon(Icons.location_on),
                              color: Colors.red,
                              iconSize: 60,
                              onPressed: () {
                                print('icon tapped');
                              },
                            ),
                          ),
//                          Marker(
//                            height: ,
//                            builder: BlocBuilder<AlertBloc,AlertState>(
//                                builder: (BuildContext context, AlertState state) {
//                                  alerts = (state as AlertsUpdated).alerts;
//                                  return
//                                }),
//                          )
                        ],
                      ),
                    ],
                  ),
                ),
                SizedBox(
                  height: 10,
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: <Widget>[
                    RaisedButton(
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(5)),
                      onPressed: () {
                        print(
                            ' @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   userLocation is $userLocation');
                        _mapController.move(userLocation, 16);
                      },
                      color: Colors.red,
                      child: Padding(
                        padding: EdgeInsets.all(8.0),
                        child: Text(
                          'center',
                          style: TextStyle(color: Colors.white, fontSize: 30),
                        ),
                      ),
                    ),
                    RaisedButton(
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(5)),
                      onPressed: () {
                        //TODO  this goes actually in a alert icon callbac, here just navigates icons vc
                      },
                      color: Colors.red,
                      child: Padding(
                        padding: EdgeInsets.all(8.0),
                        child: Text(
                          'alert',
                          style: TextStyle(color: Colors.white, fontSize: 30),
                        ),
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
      );
    });
  }
}

Я также собрал образец приложения на случай, если вы захотите взглянуть на него. https://github.com/vinnytwice/flutter_app

ОБНОВИТЬ:

Обнаружив, что FlutterMap должен быть в виджете с отслеживанием состояния, я изменил свой. Во входящем состоянии я выполнил проверку типа состояния и на стороне, которая вызвала setState () с новым значением из состояния. Ошибка .. Конструктор, который восстанавливает ошибку вида строительного виджета, если я правильно помню ошибку. .. BlocBuilder больше не был правильным выбором, поэтому я заменил его на BlocListener, у которого есть обратный вызов для состояний, и вызвал там setState (). FlutterMap теперь отрисовывается на userLocation на buttonPressed даже после Hot-reload или Hot-restart ... и больше не The method 'move' was called on null. ошибок ... так что одна проблема решена.

По-прежнему FlutterMap не отображается на userLocation при загрузке экрана. Вы видите, что мне не хватает? Возможно, какие-то начальные координаты? В любом случае ... не следует ли setState () отслеживать, где используется userLocation, и перестраивать эти виджеты?

Если я вызываю _mapController.move (userLocation, 16); сразу после setState (), карта будет следовать за местоположением пользователя, что полезно в пошаговом навигаторе, но это не то, что мне нужно ... также, если вы панорамируете ее как как только появляется новое состояние, карта перемещается ..

Итак, новый код:

// stateful widget using BlocListener:

class MapScreen extends StatefulWidget {
  final String name;
  final MapRepository _mapRepository;
//  MapController _mapController;
//  final AlertRepository _alertRepository;

  MapScreen(
      {Key key, @required this.name, @required MapRepository mapRepository})
      : assert(mapRepository != null),
        _mapRepository = mapRepository,
//        _mapController = MapController(),
//        _alertRepository = alertRepository,
        super(key: key);

  @override
  _MapScreenState createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  List<Marker> alerts;

  LatLng userLocation;

  MapController _mapController = MapController();

  @override
  Widget build(BuildContext context) {
    return BlocListener<MapBloc, MapState>(
//        bloc: MapBloc(mapRepository: _mapRepository),
      listener: (BuildContext context, MapState state) {
//      userLocation = (state as LocationStream).location;
        if (state is LocationStream) {
          setState(() {
            userLocation = (state).location;
          });
        }
      },
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.transparent,
          elevation: 0,
          title: Text(
            'Home',
            style: TextStyle(color: Colors.orangeAccent, fontSize: 40),
          ),
          actions: <Widget>[
            IconButton(
              icon: Icon(
                Icons.exit_to_app,
                color: Colors.orange,
                size: 35,
              ),
              onPressed: () {
//              BlocProvider.of<AuthenticationBloc>(context).add(
//                LoggedOut(),
//              );
              },
            ),
          ],
        ),
        backgroundColor: Colors.white,
        body: SafeArea(
          minimum: EdgeInsets.symmetric(horizontal: 20),
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Container(
                  height: 570,
                  width: 320,
                  child: FlutterMap(
                    options: MapOptions(
                      center: userLocation,
                      minZoom: 5.0,
                      maxZoom: 19.0,
                    ),
                    mapController: _mapController,
                    layers: [
                      //
//        PolygonLayer(polygonOpts, map, stream)
//                    PolygonLayerOptions(
//                      polygons:
//                    ),
                      TileLayerOptions(
                          urlTemplate:
                              'https://api.openrouteservice.org/mapsurfer/{z}/{x}/{y}.png?api_key=5b3ce3597851110001cf62484c4b65d85bc844eca3a2c6b9f300ddf4',
//                              urlTemplate:
//                                  'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
                          subdomains: ['a', 'b', 'c'],
                          keepBuffer: 20),
                      new MarkerLayerOptions(
                        markers: [
                          Marker(
                            point: userLocation,
                            height: 200,
                            width: 200,
                            builder: (context) => IconButton(
                              icon: Icon(Icons.location_on),
                              color: Colors.red,
                              iconSize: 60,
                              onPressed: () {
                                print('icon tapped');
                              },
                            ),
                          ),
//                          Marker(
//                            height: ,
//                            builder: BlocBuilder<AlertBloc,AlertState>(
//                                builder: (BuildContext context, AlertState state) {
//                                  alerts = (state as AlertsUpdated).alerts;
//                                  return
//                                }),
//                          )
                        ],
                      ),
                    ],
                  ),
                ),
                SizedBox(
                  height: 10,
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: <Widget>[
                    RaisedButton(
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(5)),
                      onPressed: () {
                        print(
                            ' @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   userLocation is $userLocation');
                        _mapController.move(userLocation, 16);
                      },
                      color: Colors.red,
                      child: Padding(
                        padding: EdgeInsets.all(8.0),
                        child: Text(
                          'center',
                          style: TextStyle(color: Colors.white, fontSize: 30),
                        ),
                      ),
                    ),
                    RaisedButton(
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(5)),
                      onPressed: () {
                        //TODO  this goes actually in a alert icon callbac, here just navigates icons vc
                      },
                      color: Colors.red,
                      child: Padding(
                        padding: EdgeInsets.all(8.0),
                        child: Text(
                          'alert',
                          style: TextStyle(color: Colors.white, fontSize: 30),
                        ),
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
} 

person Vincenzo    schedule 04.03.2020    source источник
comment
Пожалуйста, ознакомьтесь с рекомендациями по вопросам, чтобы улучшить изменения, которые вы получите в ответ: stackoverflow.com/help/how-to-ask   -  person João Soares    schedule 04.03.2020
comment
@ JoãoSoares, вы абсолютно правы, я действительно думал, что вставил пример кода в вопрос. Спасибо, что указали на это.   -  person Vincenzo    schedule 04.03.2020
comment
Пожалуйста, не добавляйте в заголовок вопроса такие вещи, как «Решено» или «увидеть обновление» - это не требуется на этом сайте. Кроме того, похоже, что ваше обновление - это ответ, поэтому напишите его как ответ, опубликуйте его, а затем отметьте как ответ. Спасибо ;)   -  person slugster    schedule 05.03.2020
comment
Извините за заголовок. На самом деле я не публикую это как ответ, потому что не все исправлено. Карта по-прежнему не отображается на userLocation при загрузке MapScreen. Кроме того, я не редактировал вопрос и не удалял старую часть, для которой нашел решение, но поместил ее в раздел ОБНОВЛЕНИЕ, чтобы она была полезна другим, показывая всю историю проблемы.   -  person Vincenzo    schedule 05.03.2020


Ответы (1)


Наконец-то я заставил это работать. В итоге я использовал подход, который, думая, что всего лишь Stream<LatLng> userLocation, я буду отсортирован, я отложил неделю назад, потому что еще не знал, как правильно использовать Futures.

В репозитории я использую метод, который просто возвращает Future<LatLng>:

Future<LatLng> getLocation() async {
    print('getLocation() called');
    Position position;
    LatLng location;
    try {
      position = await locationManager
          .getCurrentPosition(
              desiredAccuracy: LocationAccuracy.bestForNavigation)
          .timeout(Duration(seconds: 5));
      location = LatLng(position.latitude, position.longitude);
      print('getLocation() location is: $location');
      return location;
    } catch (error) {
      print(
          'getLocation(): error getting current location: ${error.toString()}');
    }
  }

От BlocProvider в main() я сейчас отправляю два события:

BlocProvider<MapBloc>(create: (context) {
          return MapBloc(
            mapRepository: mapRepository,
          )
            ..add(GetLocationStream())
            ..add(GetInitialLocation());
        }),

В блоке вывести состояние со значением из getLocation ().

Stream<MapState> _mapCenterMapToState(GetInitialLocation event) async* {
    location = await _mapRepository.getLocation();
    print('_mapCenterMapToState location is : ${_mapRepository.getLocation()}');
    yield MapLoading(location);
  } 

В BlocListener я добавил новую проверку состояния и использовал значение состояния для перемещения карты.

BlocListener<MapBloc, MapState>(
            listener: (BuildContext context, MapState state) {
          if (state is LocationStream) {
            setState(() {
              userLocation = (state).location;
            });
          }
          if (state is MapLoading) {
            userLocation = (state).location;
            print(
                ' @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ MapBloc initial state location is : $userLocation');
            _mapController.move(userLocation, 16);
          }
        })

Карта теперь загружается в местоположении пользователя, но не перемещается вместе с ним, значок местоположения пользователя постоянно обновляется, а центральная кнопка центрирует карту по местоположению пользователя при нажатии.

Надеюсь, этот длинный вопрос и ответ помогут другим, только начинающим, как и я. Ваше здоровье.

person Vincenzo    schedule 05.03.2020