Flutter iOS - не удается получить текущее местоположение для initialCameraPosition до загрузки виджета Google Maps - PullRequest
0 голосов
/ 20 апреля 2020

Я пытаюсь реализовать простую функциональность карты в моем приложении Flutter iOS. Я следовал инструкциям, начиная с Readme пакета местоположения Flutter и до Среда "Внедрение обновлений местоположения в реальном времени на Google Maps в ваших приложениях Flutter" , заканчивая многими вопросами стека, касающимися подобных проблем.

Проблема в том, что я не могу получить текущее местоположение перед загрузкой карт, поэтому получение его LatLng в initialCameraPosition приводит к NPE. Что я могу сделать, так это загружать карты жестко закодированным initialCameraPosition, тогда текущее местоположение корректно загружается, и я могу перемещать камеру к нему, перетаскивая или нажимая кнопку плавающего местоположения в правом нижнем углу.

Здесь мой пример кода:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';

const double CAMERA_ZOOM = 16;
const LatLng SOURCE_LOCATION = LatLng(37.3317, -122.0325086);

class MapPage extends StatefulWidget {
  @override
  _MapPageState createState() => _MapPageState();
}

class _MapPageState extends State<MapPage> {
  GoogleMapController mapController;
  LocationData currentLocation;
  Location location;
  bool _serviceEnabled;
  PermissionStatus _permissionGranted;

  @override
  void initState() {
    super.initState();
    location = new Location();

    location.onLocationChanged.listen((event) {
      currentLocation = event;
    });

    setInitialLocation();
  }

  void setInitialLocation() async {
    _serviceEnabled = await location.serviceEnabled();
    if (!_serviceEnabled) {
      _serviceEnabled = await location.requestService();
      if (!_serviceEnabled) {
        return;
      }
    }

    _permissionGranted = await location.hasPermission();
    if (_permissionGranted == PermissionStatus.denied) {
      _permissionGranted = await location.requestPermission();
      if (_permissionGranted != PermissionStatus.granted) {
        return;
      }
    }

    currentLocation =  await location.getLocation();
  }

  void _onMapCreated(GoogleMapController controller) {
    mapController = controller;
  }


  CameraPosition initialCameraPosition() {
    LatLng target;
    if (currentLocation != null)
      target = LatLng(currentLocation.latitude, currentLocation.longitude);
    else
      target = SOURCE_LOCATION;
    return CameraPosition(target: target, zoom: CAMERA_ZOOM);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.orangeAccent,
        title: Text('Map'),
        centerTitle: true,
      ),
      body:
      GoogleMap(
      onMapCreated: _onMapCreated,
      initialCameraPosition: initialCameraPosition(),
      myLocationEnabled: true,
      ),
    );
  }
}

Я понимаю, что получение текущего местоположения является асинхронным процессом, поэтому я также попытался использовать FutureBuilder в разделе тела:

      FutureBuilder<LocationData>(
        future: currentLocation,
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            LocationData currentLocation = snapshot.data;
            return GoogleMap(
              onMapCreated: _onMapCreated,
              initialCameraPosition: CameraPosition(
                  target: LatLng(
                      currentLocation.latitude, currentLocation.longitude)),
              myLocationEnabled: true,
            );
          } else if (snapshot.hasError) {
            return (Text("${snapshot.error}"));
          }
          return Center(
            child: CircularProgressIndicator(
              backgroundColor: Colors.orangeAccent,
            ),
          );
        },
      ),

, который работал как талисман, когда мне приходилось извлекать данные из HTTP-запроса GET, но на этот раз это приводит только к бесконечному CircularProgressIndicator.

Я пробовал много других решений из вопросов стека, включая возврат пустого Container, когда currentLocation равно нулю, что создает условия для FutureBuilder snapshot.connectionState, опять же безуспешно.

Забавно, что, когда я углубился в метод Location.getCurrentLocation(), я нашел:

/// Gets the current location of the user.
  ///
  /// Throws an error if the app has no permission to access location.
  /// Returns a [LocationData] object.
  Future<LocationData> getLocation() async {
    return LocationPlatform.instance.getLocation();
  }

который затем переходит к:

/// Gets the current location of the user.
  ///
  /// Throws an error if the app has no permission to access location.
  /// Returns a [LocationData] object.
  Future<LocationData> getLocation() {
    throw UnimplementedError();
  }

, что ставит меня под сомнение, работает ли этот плагин вообще.

Вот мой flutter doctor -v результат:

[✓] Flutter (Channel master, v1.18.0-6.0.pre.82, on Mac OS X 10.15.4 19E287, locale pl-PL)
    • Flutter version 1.18.0-6.0.pre.82 at /Users/Vuco/flutter
    • Framework revision f35b673f2b (26 hours ago), 2020-04-19 02:45:01 +0530
    • Engine revision a5e0b2f2f2
    • Dart version 2.9.0 (build 2.9.0-1.0.dev 5b19445d9c)


[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
    • Android SDK at /Users/Vuco/Library/Android/sdk
    • Platform android-29, build-tools 29.0.3
    • Java binary at: /Users/Vuco/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/192.6308749/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 11.4.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 11.4.1, Build version 11E503a
    • CocoaPods version 1.9.1

[✓] Android Studio (version 3.6)
    • Android Studio at /Users/Vuco/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/192.6308749/Android Studio.app/Contents
    • Flutter plugin version 45.0.1
    • Dart plugin version 192.7761
    • Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)

[!] IntelliJ IDEA Ultimate Edition (version 2020.1)
    • IntelliJ at /Users/Vuco/Applications/JetBrains Toolbox/IntelliJ IDEA Ultimate.app
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
    • For information about installing plugins, see
      https://flutter.dev/intellij-setup/#installing-the-plugins

[✓] VS Code (version 1.44.0)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.9.1

[✓] Connected device (1 available)
    • iPhone 8 • F60856B8-2C6E-401D-A0F9-06FFC7E09876 • ios • com.apple.CoreSimulator.SimRuntime.iOS-13-4 (simulator)

! Doctor found issues in 1 category.

и зависимости, которые я использую:

google_maps_flutter: ^0.5.25+3
location: ^3.0.2

Ответы [ 2 ]

0 голосов
/ 20 апреля 2020

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

GoogleMapController mapController;
  LocationData currentLocation;
  Location location;
  bool _serviceEnabled;
  PermissionStatus _permissionGranted;
  CameraPosition _center =
      CameraPosition(target: SOURCE_LOCATION, zoom: CAMERA_ZOOM);
  bool isLoading = false;

  @override
  void initState() {
    super.initState();
    location = new Location();

    _getLocation();
  }

  _getLocation() async {
    setState(() {
      isLoading = true;
    });
    initialize();
    currentLocation = await location.getLocation();
    if (currentLocation == null) {
      return;
    }
    _center = CameraPosition(
        target: LatLng(currentLocation.latitude, currentLocation.longitude),
        zoom: CAMERA_ZOOM);

    setState(() {
      isLoading = false;
    });

    print("CurrentLocation: $currentLocation");
  }

  Future<void> initialize() async {
    _serviceEnabled = await location.serviceEnabled();
    if (!_serviceEnabled) {
      _serviceEnabled = await location.requestService();
      if (!_serviceEnabled) {
        return;
      }
    }

    _permissionGranted = await location.hasPermission();
    if (_permissionGranted == PermissionStatus.DENIED) {
      _permissionGranted = await location.requestPermission();
      if (_permissionGranted != PermissionStatus.GRANTED) {
        return;
      }
    }
  }

  void _onMapCreated(GoogleMapController controller) {
    mapController = controller;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.orangeAccent,
        title: Text('Map'),
        centerTitle: true,
      ),
      body: isLoading
          ? CircularProgressIndicator()
          : Container(
              child: GoogleMap(
                onMapCreated: _onMapCreated,
                initialCameraPosition: _center,
                myLocationEnabled: true,
              ),
            ),
    );
  }
0 голосов
/ 20 апреля 2020

Я бы рекомендовал использовать пакет Geolocator . Это то, что я использую во всех приложениях, которые мне нужны, чтобы получить местоположение и подписаться на местоположения, и это хорошо документировано. Попробуйте использовать его, и если у вас все еще есть проблемы с вашим FutureBuilder с этим пакетом, обновите сообщение с новым кодом, и я могу попытаться помочь вам в дальнейшем.

FutureBuilder - это путь к go, хотя если вы не инициализируют и не извлекают местоположение до построения карты.

...