Flutter地理位置获取插件geolocation的使用

Flutter地理位置获取插件geolocation的使用

geolocation

Flutter geolocation 插件 适用于 Android API 16+ 和 iOS 9+。

特性:

  • 手动和自动位置权限管理
  • 获取当前位置(单次)
  • 带有前台和后台选项的连续位置更新

该插件正在积极开发中,以下功能计划尽快推出:

  • 地理编码
  • 地理围栏
  • 地点建议
  • 活动识别
  • 暴露 iOS/Android 特定的 API(如 iOS 上的重要位置更新)
Android iOS

安装

按照以下说明进行安装: https://pub.dev/packages/geolocation#-installing-tab-

iOS

Objective-C 兼容性

对于使用 Objective-C 模板创建的 Flutter 项目,可能需要在 ios/Podfile 文件顶部添加 use_frameworks!。 更多细节可以查看以下问题:https://github.com/flutter/flutter/issues/16049#issuecomment-552060349

Android

AndroidX

geolocation 依赖于 AndroidX。确保在 android/gradle.properties 中包含以下设置:

android.useAndroidX=true
android.enableJetifier=true
R8/Proguard 代码混淆

如果启用了 R8 或 ProGuard 的代码混淆,请添加以下规则。

android/app/build.gradle 文件中:

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

android/app/proguard-rules.pro 文件中:

# Geolocation - start

-keep class app.loup.geolocation.** { *; }

    # Moshi - start
    # https://github.com/square/moshi/blob/master/moshi/src/main/resources/META-INF/proguard/moshi.pro

    # JSR 305 annotations are for embedding nullability information.
    -dontwarn javax.annotation.**

    -keepclasseswithmembers class * {
        @com.squareup.moshi.* <methods>;
    }

    -keep @com.squareup.moshi.JsonQualifier interface *

    # Enum field names are used by the integrated EnumJsonAdapter.
    # values() is synthesized by the Kotlin compiler and is used by EnumJsonAdapter indirectly
    # Annotate enums with @JsonClass(generateAdapter = false) to use them with Moshi.
    -keepclassmembers @com.squareup.moshi.JsonClass class * extends java.lang.Enum {
        <fields>;
        **[] values();
    }

    # Moshi - end

# Geolocation - end

权限

Android 和 iOS 都需要在配置文件中声明位置权限。

对于 iOS

iOS 提供了两种位置权限:“当应用使用时” 和 “始终”。

如果你不确定哪种权限适合你的用途,请查看以下链接: https://developer.apple.com/documentation/corelocation/choosing_the_authorization_level_for_location_services

你需要在 ios/Runner/Info.plist 文件中声明所需的权限描述:

<dict>
  <!-- for iOS 11 + -->
  <key>NSLocationWhenInUseUsageDescription</key>
  <string>Reason why app needs location</string>
  <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
  <string>Reason why app needs location</string>

  <!-- additionally for iOS 9/10, if you need always permission -->
  <key>NSLocationAlwaysUsageDescription</key>
  <string>Reason why app needs location</string>
  ...
</dict>

对于 Android

Android 提供了两种位置权限:“粗略” 和 “精确”。 粗略位置将允许基于 Wi-Fi 等传感器获取近似位置,而精确位置会使用 GPS 返回最准确的位置(包括粗略)。

你可以在 android/app/src/main/AndroidManifest.xml 文件中声明其中一个权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <!-- or -->
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>

注意,ACCESS_FINE_LOCATION 权限包含了 ACCESS_COARSE_LOCATION


API

有关所有用法的更完整文档,请参阅 API 文档: https://pub.dartlang.org/documentation/geolocation/latest/geolocation/geolocation-library.html

你还可以检查示例项目,它展示了 Geolocation 插件的全面使用。

检查位置服务是否可用

API 文档:https://pub.dartlang.org/documentation/geolocation/latest/geolocation/Geolocation/isLocationOperational.html

final GeolocationResult result = await Geolocation.isLocationOperational();
if(result.isSuccessful) {
  // 位置服务已启用,并且权限已授予
} else {
  // 位置服务未启用、受限或权限被拒绝
}

请求位置权限

在 Android (API 23+) 和 iOS 上,应用程序需要在运行时请求位置权限。

注意:你不需要手动请求权限。Geolocation 插件会在必要时自动请求权限,当你进行位置请求时。

API 文档:https://pub.dartlang.org/documentation/geolocation/latest/geolocation/Geolocation/requestLocationPermission.html

final GeolocationResult result = await Geolocation.requestLocationPermission(
  const LocationPermission(
    android: LocationPermissionAndroid.fine,
    ios: LocationPermissionIOS.always,
  ),
  openSettingsIfDenied: true,
);

if(result.isSuccessful) {
  // 权限已授予(或之前已经授予)
} else {
  // 权限未授予
  // 用户可能已经拒绝,但也可能是位置服务未启用、受限,用户从未看到权限请求对话框。检查 result.error.type 以获取详细信息。
}

获取当前单次位置

Geolocation 提供了三种方法:

// 获取最后已知位置,这是一个 Future 而不是 Stream(在 Android 上最佳)
LocationResult result = await Geolocation.lastKnownLocation();

// 强制进行单次位置更新(在 iOS 上最佳)
StreamSubscription<LocationResult> subscription = Geolocation.currentLocation(accuracy: LocationAccuracy.best).listen((result) {
  // 处理结果
});

// 对大多数情况来说这是最佳选择
StreamSubscription<LocationResult> subscription = Geolocation.currentLocation(accuracy: LocationAccuracy.best).listen((result) {
  if(result.isSuccessful) {
    double latitude = result.location.latitude;
    // 处理结果
  }
});

连续位置更新

API 文档:https://pub.dartlang.org/documentation/geolocation/latest/geolocation/Geolocation/locationUpdates.html

StreamSubscription<LocationResult> subscription = Geolocation.locationUpdates(
    accuracy: LocationAccuracy.best,
    displacementFilter: 10.0, // 以米为单位
    inBackground: true, // 默认情况下,位置更新将在应用处于非活动状态(后台)时暂停。设置为 `true` 以继续后台更新。
  )
  .listen((result) {
    if(result.isSuccessful) {
      // 处理结果
    }
  });

// 取消订阅也会停止正在进行的位置请求
subscription.cancel();

处理位置结果

位置请求返回一个 LocationResult Future 或 LocationResult 流。

API 文档:https://pub.dartlang.org/documentation/geolocation/latest/geolocation/LocationResult-class.html

LocationResult result = await Geolocation.lastKnownLocation();

if (result.isSuccessful) {
  // 位置请求成功,保证位置不为空
  double lat = result.location.latitude;
  double lng = result.location.longitude;
} else {
  switch (result.error.type) {
    case GeolocationResultErrorType.runtime:
      // 运行时错误,检查 result.error.message
      break;
    case GeolocationResultErrorType.locationNotFound:
      // 位置请求未返回任何结果
      break;
    case GeolocationResultErrorType.serviceDisabled:
      // 设备上的位置服务禁用
      // 可能是 GPS 关闭,或者家长控制(Android)
      break;
    case GeolocationResultErrorType.permissionNotGranted:
      // 尚未请求位置权限
      // 应用必须请求权限才能访问位置
      break;
    case GeolocationResultErrorType.permissionDenied:
      // 用户拒绝了应用程序的位置权限
      // 拒绝在 iOS 上是最终决定,如果用户勾选了“不再询问”,则在 Android 上也是如此
      // 用户需要手动从设置中允许应用程序,参见 requestLocationPermission(openSettingsIfDenied: true)
      break;
    case GeolocationResultErrorType.playServicesUnavailable:
      // 仅适用于 Android
      // result.error.additionalInfo 包含更多关于 Play Services 错误的详细信息
      switch(result.error.additionalInfo as GeolocationAndroidPlayServices) {
        // 做一些事情,比如弹出对话框邀请用户安装/更新 Play Services
        case GeolocationAndroidPlayServices.missing:
        case GeolocationAndroidPlayServices.updating:
        case GeolocationAndroidPlayServices.versionUpdateRequired:
        case GeolocationAndroidPlayServices.disabled:
        case GeolocationAndroidPlayServices.invalid:
      }
    break;
  }
}

示例代码

以下是一个完整的示例代码,展示了如何使用 geolocation 插件获取当前位置并处理结果。

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:geolocation/geolocation.dart';

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  [@override](/user/override)
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  String _latitude = "Unknown";
  String _longitude = "Unknown";

  Future<void> _getLocation() async {
    try {
      final GeolocationResult result = await Geolocation.currentLocation(accuracy: LocationAccuracy.high);

      if (result.isSuccessful) {
        setState(() {
          _latitude = result.location.latitude.toString();
          _longitude = result.location.longitude.toString();
        });
      } else {
        setState(() {
          _latitude = "Location Not Found";
          _longitude = "Location Not Found";
        });
      }
    } catch (e) {
      setState(() {
        _latitude = "Error: $e";
        _longitude = "Error: $e";
      });
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Geolocation Example"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: _getLocation,
              child: Text("Get Location"),
            ),
            SizedBox(height: 20),
            Text("Latitude: $_latitude"),
            Text("Longitude: $_longitude"),
          ],
        ),
      ),
    );
  }
}

更多关于Flutter地理位置获取插件geolocation的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter地理位置获取插件geolocation的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,获取设备的地理位置信息通常使用geolocator插件。geolocator是一个功能强大的插件,允许你获取设备的当前位置、监听位置变化、获取位置权限等。

以下是如何使用geolocator插件获取设备地理位置的步骤:

1. 添加依赖

首先,在pubspec.yaml文件中添加geolocator插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  geolocator: ^9.0.0  # 请使用最新版本

然后运行flutter pub get来安装依赖。

2. 配置权限

在Android和iOS上,你需要配置相应的权限来获取地理位置。

Android

android/app/src/main/AndroidManifest.xml文件中添加以下权限:

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

iOS

ios/Runner/Info.plist文件中添加以下权限:

<key>NSLocationWhenInUseUsageDescription</key>
<string>我们需要您的位置信息来提供更好的服务。</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>我们需要您的位置信息来提供更好的服务。</string>

3. 获取当前位置

以下是一个简单的示例,展示如何使用geolocator插件获取设备的当前位置:

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(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  Position? _currentPosition;

  Future<void> _getCurrentLocation() async {
    // 检查位置服务是否启用
    bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
    if (!serviceEnabled) {
      // 位置服务未启用,提示用户启用
      return;
    }

    // 检查位置权限
    LocationPermission permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied) {
        // 权限被拒绝,提示用户
        return;
      }
    }

    if (permission == LocationPermission.deniedForever) {
      // 权限被永久拒绝,提示用户
      return;
    }

    // 获取当前位置
    Position position = await Geolocator.getCurrentPosition(
      desiredAccuracy: LocationAccuracy.high,
    );

    setState(() {
      _currentPosition = position;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('获取地理位置'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            if (_currentPosition != null)
              Text(
                '纬度: ${_currentPosition!.latitude}, 经度: ${_currentPosition!.longitude}',
              ),
            ElevatedButton(
              onPressed: _getCurrentLocation,
              child: Text('获取当前位置'),
            ),
          ],
        ),
      ),
    );
  }
}

4. 监听位置变化

你还可以使用geolocator插件来监听设备位置的变化:

StreamSubscription<Position>? positionStream;

void startListening() {
  positionStream = Geolocator.getPositionStream(
    desiredAccuracy: LocationAccuracy.high,
    distanceFilter: 10, // 位置变化超过10米时触发
  ).listen((Position position) {
    print('位置变化: ${position.latitude}, ${position.longitude}');
  });
}

void stopListening() {
  positionStream?.cancel();
}
回到顶部