Flutter урок по GPS геолокации (перевод урока с дополнениями)

Егор Долгов · 06.06.2019
This article was written for an older Flutter version and is being updated. Some code may not compile on the current version.

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

Это перевод статьи Flutter - GPS Geolocation Tutorial [EN]. Перевод подготовлен Михайловой Марией. В статье вставлены дополнения и пояснения которых нет в оригинале.

Это руководство покажет вам как получить доступ к местонахождению устройства во Flutter, используя GPS, включая то как получить разрешения, текущее местоположение и постоянное его обновление.

Зависимости

Пакет geolocator (геолакатор) во Flutter предоставляет геолакационные функции. Добавьте его в ваш файл pubspec.yaml и запустите команду packages get (получить пакеты).

В оригинале используется версия geolocator: ^3.0.1 , на момент публикации этого перевода, самая последняя версия ^5.0.0. Но для ее работы на Android требуется совершить переход к AndroidX (смотрите как это сделать тут https://flutter.su/note/146 ). Без этого перехода - приложение будет вылетать при запросе к методам geolocator.

dependencies {
  ...
  geolocator: ^5.0.0
  ...
}

Разрешения

Вам нужно добавить разрешения в каждую платформу.

Для Android

Нужны разрешения ACCESS_FINE_LOCATION и ACCESS_COARSE_LOCATION .

Добавьте их в AndroidManifest.xml путь к файлу: <app dir>/android/app/src/main/AndroidManifest.xml

В документации, https://flutter.dev/docs/deployment/android#review-the-app-manifest , указан именно этот путь. Но уже сейчас в новых flutter проектах есть еще несколько файлов AndroidManifest.xml в директории debug и main . В них указаны разрешения на использование сети:

<uses-permission android:name="android.permission.INTERNET"/>

По видимому, документация скоро обновится и будет указано в каком именно манифесте должны быть указаны разрешения.

Сообщение о предоставлении разрешения на использование геолокации.

Сообщение о предоставлении разрешения на использование геолокации.
Сообщение о предоставлении разрешения на использование геолокации.

Для IOS

Нужно NSLocationWhenInUseUsageDescription разрешение. Добавьте его в файл Info.plist .

NSLocationWhenInUseUsageDescription
This app needs access to location when open.

Пример кода

Ниже представлена структура кода этого руководства. Нам нужно создать экземпляр Geolocator (геолокатора) и сохранить значение последней Position (позиции). Приложение будет использовать значение Position , чтобы показать широту и долготу.

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';

class GeolocationExampleState extends State {
  Geolocator _geolocator;
  Position _position;

  @override
  void initState() {
    super.initState();
    _geolocator = Geolocator();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Geolocation Example'),
      ),
      body: Center(
          child: Text(
             'Latitude: ${_position != null ? _position.latitude.toString() : '0'},'
             ' Longitude: ${_position != null ? _position.longitude.toString() : '0'}'
          )
      ),
    );
  }
}

Проверьте разрешения

Если вы добавили правильные разрешения, приложению будет дано разрешение для доступа к местоположению устройства, используя GPS. В Андроид 6.0 и выше, он попросит пользователя дать разрешения. Но если вам нужно проверить есть ли у приложения разрешение на доступ к местоположению, вы сможете это сделать программно. Чтобы сделать проверку, используйте метод checkGeolocationPermissionStatus который возвращает значение Future . Как вариант, для iOS, вы можете отдельно выбрать locationAlways и locationWhenInUse , устанавливая locationPermission опциональный параметр, тип которого GeolocationPermission .

void checkPermission() {
  _geolocator.checkGeolocationPermissionStatus().then((status) { print('status: $status'); });
  _geolocator.checkGeolocationPermissionStatus(locationPermission: GeolocationPermission.locationAlways).then((status) { print('always status: $status'); });
  _geolocator.checkGeolocationPermissionStatus(locationPermission: GeolocationPermission.locationWhenInUse)..then((status) { print('whenInUse status: $status'); });
}

Значения:

  • GeolocationStatus.denied - если пользователь отклонил разрешение;
  • GeolocationStatus.granted - если пользователь согласился.

Получить текущее местоположение

Получить текущее местонахождение очень легко. Просто используйте метод getCurrentPosition , который возвращает Future<Location>. Вы можете использовать опцию desiredAccuracy .

await Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high)

Иногда процесс получения текущего местонахождения может быть не выполнен. К примеру, если пользователь выключает сенсор GPS. Если он был выключен с самого начала, это может быть причиной ошибки, поэтому мы должны ее отследить. Есть также вероятность того что GPS выключен когда процесс поиска локации уже идет. Это может привести к прерыванию процесса, и следовательно, лучше добавить задержку его исполнения.

void updateLocation() async {
  try {
    Position newPosition = await Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high).timeout(new Duration(seconds: 5));
   setState(() {
      _position = newPosition;
    });
  } catch (e) {
    print('Error: ${e.toString()}');
  }
}

Ниже приведены описания значений каждого LocationAccuracy .

Название описания lowest (самое низкое). Местонахождение точно в пределах дистанции в 3000м на iOS и 500м на Android.

  • low (низкая) локация точна в пределах 1000м на iOS и 500м на Android.
  • medium (средняя) локация точна в пределах 10м на iOS и между 0м и 100м на Android.
  • high (высокая) локация точна в пределах 10м на iOS и между 0м и 100м на Android.
  • best (лучшая) локация точна в пределах ~0м на iOS и между 0м и 100м на Android. Точность локации bestForNavigation оптимизирована для iOS и соответствует LocationAccuracy.best на Android.

Трансляция обновления местонахождения

Чтобы получить обновленное местоположение, вы фактически можете поместить код выше в тело цикла. Но существует способ лучше и точнее. Вы можете использовать getPositionStream , который возвращает Stream<Subscription>. Вы также можете настроить требуемое значение изменения местоположения перед тем как слушатель будет уведомлен, используя distanceFilter .

LocationOptions locationOptions = LocationOptions(accuracy: LocationAccuracy.high, distanceFilter: 1);

StreamSubscription positionStream = _geolocator.getPositionStream(locationOptions).listen((Position position) {_position = position;});

Листинг программы

Ниже представлен полный код данного руководства:

В оригинальный код добавлен метод setState() для обновления положения в реальном времени

StreamSubscription positionStream = _geolocator.getPositionStream(locationOptions).listen((Position position) { setState(() {
        _position = position;
      });});

Можно скопировать этот код и запустить приложение:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Geolocation',
      home: GeolocationExample(),
    );
  }
}

class GeolocationExampleState extends State {
  Geolocator _geolocator;
  Position _position;

  void checkPermission() {
    _geolocator.checkGeolocationPermissionStatus().then((status) { print('status: $status'); });
    _geolocator.checkGeolocationPermissionStatus(locationPermission: GeolocationPermission.locationAlways).then((status) { print('always status: $status'); });
    _geolocator.checkGeolocationPermissionStatus(locationPermission: GeolocationPermission.locationWhenInUse)..then((status) { print('whenInUse status: $status'); });
  }

  @override
  void initState() {
    super.initState();

    _geolocator = Geolocator();
    LocationOptions locationOptions = LocationOptions(accuracy: LocationAccuracy.high, distanceFilter: 1);

    checkPermission();
    updateLocation();

    StreamSubscription positionStream = _geolocator.getPositionStream(locationOptions).listen((Position position) { setState(() {
      _position = position;
    });});
  }

  void updateLocation() async {
    try {
      Position newPosition = await Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high)
          .timeout(new Duration(seconds: 5));

      setState(() {
        _position = newPosition;
      });
    } catch (e) {
      print('Error: ${e.toString()}');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Startup Name Generator'),
      ),
      body: Center(
          child: Text(
              'Latitude: ${_position != null ? _position.latitude.toString() : '0'},'
                  ' Longitude: ${_position != null ? _position.longitude.toString() : '0'}'
          )
      ),
    );
  }
}

class GeolocationExample extends StatefulWidget {
  @override
  GeolocationExampleState createState() => new GeolocationExampleState();
}

Результат работы программы определение местоположения, GPS координаты, на Flutter.