Pinpoint SDK

Introduction

The Pinpoint SDK is a cross-platform (Android/iOS) Flutter plugin for Ultra-Wideband (UWB) positioning with Pinpoint's technology.

This plugin is based on version 13.0.3 of geolocator.

The integration of navigation based on Pinpoint's technology will be included in the next release.

Features

  • Get the last known location
  • Get the current location of the device
  • Get continuous location updates
  • Check if location services are enabled on the device
  • Check if Bluetooth is enabled on the device
  • Calculate the distance (in meters) between two geocoordinates
  • Calculate the bearing between two geocoordinates

Getting started

Requirements

  • Flutter (minimum version: 3.27.0)
  • Visual Studio Code (minimum version: 1.92.2) or Android Studio (minimum version: Android Studio Giraffe | 2022.3.1)

Supported Platforms

  • Android (minimum Android 10, API level 29)
  • iOS (minimum 11)

Setup Environment

To add the pinpoint_sdk to your Flutter application read the install instructions.

Platform specific requirements

Below are some Android and iOS specifics that are required for the plugin to work correctly.

Android

AndroidX

The pinpoint_sdk plugin requires the AndroidX version of the Android Support Libraries. This means you need to make sure your Android project supports AndroidX. Detailed instructions can be found here.

The TL;DR version is:

  1. Add the following to your "gradle.properties" file:
android.useAndroidX=true
android.enableJetifier=true
  1. Make sure you set the compileSdkVersion in your "android/app/build.gradle" file to 35:

in android/app/build.gradle:

android {
  compileSdkVersion 35
}

or if applicable in android/app/build.gradle.kts

android {
  compileSdk = 35
}
  1. Make sure you replace all the android. dependencies to their AndroidX counterparts (a full list can be found here: Migrating to AndroidX).

Minimum SDK

Make sure to set the minimum supported Android version to Android 10 (API level 29):

in android/app/build.gradle:

defaultConfig {
  minSdkVersion 29
}

or if applicable in android/app/build.gradle.kts:

defaultConfig {
  minSdk = 29
}

Proguard

Add the following lines to your project/android/app/proguard-rules.pro file:

-keep class com.lib.flutter_blue_plus.* { *; }
-keep public class de.IPinpointPositionListener$Stub.** { *; }
-keep public class de.IPinpointPositionListener.** { *; }
-keep public class de.pinpoint.ServiceTest.** { *; }
-ignorewarnings

If the file does not exist yet, create it. Also make sure that your proguard file is used:

in android/app/build.gradle

android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile(
                    'proguard-android-optimize.txt'),
                    'proguard-rules.pro'

        }
    }
}

or if applicable in android/app/build.gradle.kts:

android {
    buildTypes{
        release {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
  }
}

Permissions

You'll need to add the following permissions to your Android Manifest. Open the AndroidManifest.xml file (located under android/app/src/main) and add the following lines as direct children of the <manifest> tag:

    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

    <!-- legacy for Android 11 or lower -->
    <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />

WARNING: You cannot use the permission ACCESS_COARSE_LOCATION due to compatibility reasons with the Pinpoint SDK. If you use it, this might cause the app to crash.

iOS

On iOS you'll need to add the following entry to your Info.plist file (located under ios/Runner) in order to access the device's location. Simply open your Info.plist file and add the following (make sure you update the description so it is meaningful in the context of your App):

    <key>NSLocationWhenInUseUsageDescription</key>
    <string>This app needs access to location when open.</string>
    <key>NSBluetoothAlwaysUsageDescription</key>
    <string>This app needs Bluetooth to scan for and connect to Pinpoint Devices.</string>
    <!-- legacy for iOS 12 and lower -->
    <key>NSBluetoothPeripheralUsageDescription</key>
    <string>This app needs Bluetooth to scan for and connect to Pinpoint Devices.</string>

Add the following to the ios/Podfile of your application:

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    target.build_configurations.each do |config|
      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
        '$(inherited)', 'PERMISSION_BLUETOOTH=1',]
    end
    if target.name == "geolocator_apple"
          target.build_configurations.each do |config|
            config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'BYPASS_PERMISSION_LOCATION_ALWAYS=1',]
          end
    end
  end
end

If you do want to receive updates when your App is in the background (or if you don't bypass the permission request as described above) then you'll need to:

  • Add the Background Modes capability to your XCode project (Project > Signing and Capabilities > "+ Capability" button) and select Location Updates. Be careful with this, you will need to explain in detail to Apple why your App needs this when submitting your App to the AppStore. If Apple isn't satisfied with the explanation your App will be rejected.
  • Add an NSLocationAlwaysAndWhenInUseUsageDescription entry to your Info.plist (use NSLocationAlwaysUsageDescription if you're targeting iOS <11.0)

When using the requestTemporaryFullAccuracy({purposeKey: "YourPurposeKey"}) method, a dictionary should be added to the Info.plist file.

<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
  <key>YourPurposeKey</key>
  <string>The example App requires temporary access to the device&apos;s precise location.</string>
</dict>

The second key (in this example called YourPurposeKey) should match the purposeKey that is passed in the requestTemporaryFullAccuracy() method. It is possible to define multiple keys for different features in your app. More information can be found in Apple's documentation.

NOTE: the first time requesting temporary full accuracy access it might take several seconds for the pop-up to show. This is due to the fact that iOS is determining the exact user location which may take several seconds. Unfortunately this is out of our hands.

Usage

  1. Install the Pinpoint SDK by following the instructions from Getting started. Pay attention to the platform specific requirements.

  2. Import it:

    import 'package:pinpoint_sdk/pinpoint_sdk.dart';
    
  3. Call initGeolocator with you API key. This function needs to be called before using any Geolocator functionality. It only needs to be called once in a central place, e.g. in the main of your app.

    import 'package:pinpoint_sdk/pinpoint_sdk.dart';
       
    void main () {
     // initializes the Geolocator class
     initGeolocator(apiKey: 'myApiKey');
     runApp(const GeolocatorApp());
    }
    

    Note: As of now, the API key is still optional, but it will become mandatory in a future relase. In any case an Exception (MissingInitializationException) will be thrown if you do not call this function.

  4. Call Geolocator.updateGeoreference with reference longitude, latitude and azimuth of the site where you want to use indoor positioning before calling either getPositionStream or getCurrentPosition for the first time. You get these Parameters from the section Georeferencing in Pinpoint's application EasyPlan.

    import 'package:pinpoint_sdk/pinpoint_sdk.dart';
    // updates Geolocator with the georeference to enable indoor positioning
    Geolocator.updateGeoreference(latitude: 50.83592701149315, longitude: 12.923277343972849, azimuth: 100)
    

    Note This function needs to be called to enable positioning. If you did not call this function, an exception (MissingWgs84ReferenceException) will be thrown when you call getPositionStream or getCurrentPositionand you will not receive any indoor positions.

Migration from geolocator

  1. Remove geolocator from the dependencies of your app by deleting it from the dependencies in your pubspec.yaml or by calling flutter pub remove geolocator

  2. Follow the steps from above (Usage). Pay attention that under Android you can only use the permission ACCESS_FINE_LOCATION.ACCESS_COARSE_LOCATIONis not supported. Also pay attention to the new permissions that are necessary for using Bluetooth. Make sure that you call the function Geolocator.updateGeoreference before calling getPositionStream or getCurrentPosition for the first time.

  3. Replace the original imports of package:geolocator/geolocator.dart with package:pinpoint_sdk/pinpoint_sdk.dart

  4. If you are using the function getServiceStatusStream, pay attention that the return type changed: ServiceStatus now has four values: bluetoothEnabled, bluetoothDisabled, locationEnabledand locationDisabled.

  5. If you are using location settings with a distance filter: Make sure to convert it from meters to centimeters. Keep in mind that the function getCurrentPosition ignores any distance filter.

  6. If you are using the function distanceBetween: Make sure to convert the values returned by this function from centimeters back to meters.

Example

The code below shows an example on how to acquire the current position of the device, including checking if the location services are enabled and checking / requesting permission to access the position of the device:

import 'package:pinpoint_sdk/pinpoint_sdk.dart';

/// Determine the current position of the device.
///
/// When the location services or Bluetooth are not enabled 
/// or permissions are denied the `Future` will return an error.
Future<Position> _determinePosition() async {

  // Test if location services are enabled.
  final locationServiceEnabled = await Geolocator.isLocationServiceEnabled();
  final bluetoothEnabled = await Geolocator.isBluetoothEnabled();

  if (!locationServiceEnabled) {
    // Location services are not enabled don't continue
    // accessing the position and request users of the 
    // App to enable the location services.
    return Future.error('Location services are disabled.');
  }

  if (!bluetoothEnabled) {
    // Location services are not enabled don't continue
    // accessing the position and request users of the 
    // App to enable the location services.
    return Future.error('Bluetooth is disabled.');
  }

  LocationPermission permission = await Geolocator.checkPermission();
  if (permission == LocationPermission.denied) {
    permission = await Geolocator.requestPermission();
    if (permission == LocationPermission.denied) {
      // Permissions are denied, next time you could try
      // requesting permissions again (this is also where
      // Android's shouldShowRequestPermissionRationale 
      // returned true. According to Android guidelines
      // your App should show an explanatory UI now.)
      return Future.error('Location permissions are denied');
    }
  }
  
  if (permission == LocationPermission.deniedForever) {
    // Permissions are denied forever, handle appropriately. 
    return Future.error(
      'Location permissions are permanently denied, we cannot request permissions.');
  } 

  // Update the  georeference that should be used for indoor positioning
  // This only needs to be done once before starting the positioning for the first time
  Geolocator.updateGeoreference(latitude: 50.83592701149315, longitude: 12.923277343972849, azimuth: 100);


  // When we reach here, permissions are granted and we can
  // continue accessing the position of the device.
  return await Geolocator.getCurrentPosition(); 
}

API

Initialize Geolocator

Before being able to use any of the API functions, you need to call the function initGeolocator with your API key. You only need to call this function once in a central place, e.g. in the main of your app.

import 'package:pinpoint_sdk/pinpoint_sdk.dart';
void main () {
  // Initialize Geolocator
  initGeolocator(apiKey: 'MyApiKey');
  runApp(const GeolocatorApp());
}  

Note that the API key is still optional, but will become mandatory in one of the future releases.

Geolocation

Update georeferencing parameters

Before you start positioning by calling either getCurrentPosition or getPositionStream you need to call the function updateGeoreference. The function takes the latitude, longitude and azimuth of the Georeferencing parameters of the site where indoor positioning is available. You can get these parameters from the Georeferencing section in EasyPlan.

import 'package:pinpoint_sdk/pinpoint_sdk.dart'; 
// Update georeferencing parameters
Geolocator.updateGeoreference(latitude: 50.83592701149315, longitude: 12.923277343972849, azimuth: 100);

You need to call this function again whenever you enter a new site. Otherwise the positions that you receive will not match the actual location.

Last known location

To query the last known location retrieved stored on the device you can use the getLastKnownPosition method (note that this can result in a null value when no location details are available).

import 'package:pinpoint_sdk/pinpoint_sdk.dart';

Position? position = await Geolocator.getLastKnownPosition();

Current location

To query the current location of the device simply make a call to the getCurrentPosition method. You can finetune the results by specifying location settings.

import 'package:pinpoint_sdk/pinpoint_sdk.dart';

// Update georeference that should be used for indoor positioning
// This only needs to be done once before starting the positioning for the first time
Geolocator.updateGeoreference(latitude: 50.83592701149315, longitude: 12.923277343972849, azimuth: 100);

final LocationSettings locationSettings = LocationSettings(
  // only applies for outdoor positions
  accuracy: LocationAccuracy.high,
  // applies for both indoor and outdoor positions
  timeLimit: const Duration(seconds 15),
);

Position position = await Geolocator.getCurrentPosition(locationSettings: locationSettings);

Listen to location updates

To listen for location changes you can call the getPositionStream to receive a stream with position updates. Make sure that you updated the georeferencing parameters before calling this function.

You can control the settings used for retrieving the location by supplying location settings.

import 'package:pinpoint_sdk/pinpoint_sdk.dart';

// Update georeference that should be used for indoor positioning
// This only needs to be done once before starting the positioning for the first time
Geolocator.updateGeoreference(latitude: 50.83592701149315, longitude: 12.923277343972849, azimuth: 100);
final LocationSettings locationSettings = LocationSettings(
  // only applies for outdoor positions
  accuracy: LocationAccuracy.high,
  // applies for both indoor and outdoor positions
  distanceFilter: 30,
);
StreamSubscription<Position> positionStream = Geolocator.getPositionStream(locationSettings: locationSettings).listen(
    (Position position) {
        print('Position: ${position.latitude.toString()}, ${position.longitude.toString()}');
    });

Location Settings

You can finetune the results from getPositionStream and getCurrentPosition by specifying the following parameters:

  • accuracy: the accuracy of the location data that your app wants to receive (applies only for outdoor positions)

  • distanceFilter: the minimum distance (measured in centimeters) a device must move horizontally before an update event is generated:

    Note: The distance filter in geolocator is measured in meters. This package uses centimeters to allow smaller distance filters for indoor positions.

  • timeLimit: the maximum amount of time allowed between location updates. When the time limit is passed a TimeoutException will be thrown and the stream will be cancelled. By default no limit is configured.

    Note: If there is not any active subscription to the stream returned by getPositionStream, the timeout will always be increased to the minimum time it takes to start the indoor position services.

import 'package:pinpoint_sdk/pinpoint_sdk.dart';

// Update georeference that should be used for indoor positioning
// This only needs to be done once before starting the positioning for the first time
Geolocator.updateGeoreference(latitude: 50.83592701149315, longitude: 12.923277343972849, azimuth: 100);
final LocationSettings locationSettings = LocationSettings(
  // only applies for outdoor positions
  accuracy: LocationAccuracy.high,
  // applies for both indoor and outdoor positions
  distanceFilter: 30,
  // applies for both indoor and outdoor positions, but only after the indoor positioning service was initialized
  timeLimit: const Duration(seconds 15)
);

StreamSubscription<Position> positionStream = Geolocator.getPositionStream(locationSettings: locationSettings).listen(
    (Position position) {
        print('Position: ${position.latitude.toString()}, ${position.longitude.toString()}');
    });
Restrictions of the location settings
  • If you set a distance filter in the location settings, it will be ignored in getCurrentPosition.
  • When calling getCurrentPosition with location settings, time limits are always increased to the minimum time it takes to start the indoor position service (10 seconds).
  • When calling getPositionStream with location settings, the time limit is ignored until the time to start the indoor position (10 seconds) has passed.
  • If you called getPositionStream before and have not cancelled all subscriptions to the stream, the location settings will not be updated when calling getCurrentPosition. The same applies if you call getPositionStream a second time.
Notes on the location accuracy

The location accuracy for indoor position is always LocationAccuracy.highest. For outdoor positions the following applies:

Android On Android, the `LocationAccuracy` enum controls the accuracy of the location data the app wants to receive. It also provides control over the [priority given to the location stream](https://developers.google.com/android/reference/com/google/android/gms/location/Priority). This can be confusing, as a priority of **lowest** might not return any location, while one might expect it to give the quickest responses. The table below outlines the priority and its meaning per accuracy option:
Location accuracy Android priority Description
lowest PRIORITY_PASSIVE Ensures that no extra power will be used to derive locations. This enforces that the request will act as a passive listener that will only receive "free" locations calculated on behalf of other clients, and no locations will be calculated on behalf of only this request.
low PRIORITY_LOW_POWER Requests a tradeoff that favors low power usage at the possible expense of location accuracy.
medium PRIORITY_BALANCED_POWER_ACCURACY Requests a tradeoff that is balanced between location accuracy and power usage.
high+ PRIORITY_HIGH_ACCURACY Requests a tradeoff that favors highly accurate locations at the possible expense of additional power usage.
iOS On iOS, the `LocationAccuracy` enum controls the accuracy of the location data the app wants to receive. It also provides control on the battery consumption of the device: the more detailed data is requested, the larger the impact on the battery consumption. More details can be found on [Apple's documentation](https://developer.apple.com/documentation/corelocation/cllocationmanager/1423836-desiredaccuracy?language=objc). The table below shows how the `LocationAccuracy` values map to the native iOS accuracy settings.
Location accuracy iOS accuracy Description
lowest kCLLocationAccuracyThreeKilometers Accurate to the nearest three kilometers.
low kCLLocationAccuracyKilometer Accurate to the nearest kilometer.
medium kCLLocationAccuracyHundredMeters Accurate to within one hundred meters.
high kCLLocationAccuracyNearestTenMeters Accurate to within ten meters of the desired target.
best kCLLocationAccuracyBest The best level of accuracy available.
bestForNavigation kCLLocationAccuracyBestForNavigation The highest possible accuracy that uses additional sensor data to facilitate navigation apps.
Platform specific location settings (Outdoor positioning only)

In certain situation it is necessary to specify some platform specific settings. This can be accomplished using the platform specific AndroidSettings or AppleSettings classes. For example:

import 'package:pinpoint_sdk/pinpoint_sdk.dart';

late LocationSettings locationSettings;

if (defaultTargetPlatform == TargetPlatform.android) {
  locationSettings = AndroidSettings(
      accuracy: LocationAccuracy.high,
      forceLocationManager: true,
      intervalDuration: const Duration(seconds: 10),
      //(Optional) Set foreground notification config to keep the app alive 
      //when going to the background
      foregroundNotificationConfig: const ForegroundNotificationConfig(
        notificationText:
        "Example app will continue to receive your location even when you aren't using it",
        notificationTitle: "Running in Background",
        enableWakeLock: true,
      )
  );
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
  locationSettings = AppleSettings(
    accuracy: LocationAccuracy.high,
    activityType: ActivityType.fitness,
    pauseLocationUpdatesAutomatically: true,
    // Only set to true if our app will be started up in the background.
    showBackgroundLocationIndicator: false,
  );
} else {
  locationSettings = LocationSettings(
    accuracy: LocationAccuracy.high,
    distanceFilter: 30,
  );
}

// supply location settings to getCurrentPosition
Position position = await Geolocator.getCurrentPosition(locationSettings: locationSettings);

// supply location settings to getPositionStream
StreamSubscription<Position> positionStream = Geolocator.getPositionStream(locationSettings: locationSettings).listen(
        (Position position) {
      print('${position.latitude.toString()}, ${position.longitude.toString()}');
    });

Location accuracy for outdoor positions (Android and iOS 14+ only)

To query if a user enabled Approximate location fetching or Precise location fetching, you can call the Geolocator().getLocationAccuracy() method. This will return a Future<LocationAccuracyStatus>, which when completed contains a LocationAccuracyStatus.reduced if the user has enabled Approximate location fetching or LocationAccuracyStatus.precise if the user has enabled Precise location fetching. When calling getLocationAccuracy before the user has given permission, the method will return LocationAccuracyStatus.reduced by default. On iOS 13 or below, the method getLocationAccuracy will always return LocationAccuracyStatus.precise, since that is the default value for iOS 13 and below.

import 'package:pinpoint_sdk/pinpoint_sdk.dart';
var accuracy = await Geolocator.getLocationAccuracy();

Location service information

To check if location services are enabled you can call the isLocationServiceEnabled method:

import 'package:pinpoint_sdk/pinpoint_sdk.dart';
bool isLocationServiceEnabled  = await Geolocator.isLocationServiceEnabled();

Bluetooth information

To check if Bluetooth is enabled you can call the isBluetoothEnabled method:

import 'package:pinpoint_sdk/pinpoint_sdk.dart';
bool isBluetoothEnabled  = await Geolocator.isBluetoothEnabled();

Service status updates

To listen for service status changes you can call the getServiceStatusStream. This will return a Stream<ServiceStatus> which can be listened to, to receive location service status updates. Note: the ServiceStatus in the Pinpoint SDK differs from the one in Geolocator. It includes the statuses for Bluetooth as well and therefor has four possible values: bluetoothEnabled, bluetoothDisabled, locationEnabled and locationDisbled.

import 'package:pinpoint_sdk/pinpoint_sdk.dart';
StreamSubscription<ServiceStatus> serviceStatusStream = Geolocator.getServiceStatusStream().listen(
    (ServiceStatus status) {
        print(status);
    });

Permissions

Note: The required permission include the permissions for both indoor and outdoor positioning, ie. access to location and Bluetooth scanning and connecting. If the permission results differ for indoor and outdoor, the permission with lower rights is returned. E.g. if the permission for indoor positioning is LocationPermission.denied and the one for outdoor positioning is LocationPermission.always, the result is LocationPermission.denied.

If you want to check if the user already granted the necessary permissions to enable positioning, you can make a call to the checkPermission method:

import 'package:pinpoint_sdk/pinpoint_sdk.dart';

LocationPermission permission = await Geolocator.checkPermission();

If you want to request permission for positioning you can call the requestPermission method:

import 'package:pinpoint_sdk/pinpoint_sdk.dart';
LocationPermission permission = await Geolocator.requestPermission();

Possible results from the checkPermission and requestPermission methods are:

Permission Description
denied Permission to access the device's location or Bluetooth is denied by the user. You are free to request permission again (this is also the initial permission state).
deniedForever Permission to access the device's location or Bluetooth is permanently denied. When requesting permissions the permission dialog will not be shown until the user updates the permission in the App settings.
whileInUse Permission to access the device's location or Bluetooth is allowed only while the App is in use.
always Permission to access the device's location or Bluetooth is allowed even when the App is running in the background.

Note: Android can only return whileInUse, always or denied when checking permissions. Due to limitations on the Android OS it is not possible to determine if permissions are denied permanently when checking permissions. Using a workaround the geolocator is only able to do so as a result of the requestPermission method. More information can be found in geolocator's wiki.

Device Settings

In some cases it is necessary to ask the user and update their device settings. For example when the user initially permanently denied permissions to access the device's location or if the location services are not enabled (and, on Android, automatic resolution didn't work). In these cases you can use the openAppSettings or openLocationSettings methods to immediately redirect the user to the device's settings page.

On Android the openAppSettings method will redirect the user to the App specific settings where the user can update necessary permissions. The openLocationSettings method will redirect the user to the location settings where the user can enable/ disable the location services.

On iOS we are not allowed to open specific setting pages so both methods will redirect the user to the Settings App from where the user can navigate to the correct settings category to update permissions or enable/ disable the location services.

import 'package:pinpoint_sdk/pinpoint_sdk.dart';

await Geolocator.openAppSettings();
await Geolocator.openLocationSettings();

Utility methods

To calculate the distance (in centimeters) between two geocoordinates you can use the distanceBetween method. The distanceBetween method takes four parameters:

Parameter Type Description
startLatitude double Latitude of the start position
startLongitude double Longitude of the start position
endLatitude double Latitude of the destination position
endLongitude double Longitude of the destination position
import 'package:pinpoint_sdk/pinpoint_sdk.dart';

double distanceInCentimeters = Geolocator.distanceBetween(52.2165157, 6.9437819, 52.3546274, 4.8285838);

If you want to calculate the bearing between two geocoordinates you can use the bearingBetween method. The bearingBetween method also takes four parameters:

Parameter Type Description
startLatitude double Latitude of the start position
startLongitude double Longitude of the start position
endLatitude double Latitude of the destination position
endLongitude double Longitude of the destination position
import 'package:pinpoint_sdk/pinpoint_sdk.dart';

double bearing = Geolocator.bearingBetween(52.2165157, 6.9437819, 52.3546274, 4.8285838);

Logging

The Pinpoint SDK uses the Dart logging framework. All logs from the SDK start with the prefix pinpoint_sdk.

import 'package:logging/logging.dart';

hierarchicalLoggingEnabled = true;
// listen only to logs by the Pinpoint SDK
Logger('pinpoint_sdk').onRecord.listen(
  (record) {
    print('${record.time} [${record.loggerName}] ${record.message}');
  },
);

Troubleshooting

"I do not get any positions"

  • Make sure that you have enabled both Bluetooth and Location service.
  • Note that it might take up to 10 seconds before you receive the first position.
  • Make sure that you have called the function updateGeoreference.
  • Make sure that your app has all the necessary permissions.
  • If you are using an external TRACElet, hold it close to your phone.

Android: Geolocator.checkPermission always returns LocationPermission.denied

  • Make sure that you choose "always" and not "while in use" when the Location permission is requested. If you choose "while in use", the permissions will always be handled as denied.
  • Make sure that your manifest includes all the required permissions (cf. Platform specific requirements).

License

This package is licensed under a proprietary license with MIT licensed components. Please refer to the LICENSE file for more details.

Libraries

pinpoint_sdk