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

Егор Долгов · 06.06.2019
Статья написана для старой версии Flutter и сейчас обновляется. Часть кода может не компилироваться на актуальной версии.

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.