Flutter定位功能插件fl_location的使用
Flutter定位功能插件fl_location的使用
插件简介
fl_location
是一个能够访问各平台位置服务并收集设备位置数据的插件。
支持的平台
- ✅ Android
- ✅ iOS
- ✅ Web
功能特性
- 可请求位置权限。
- 可获取设备当前的位置。
- 可检查位置服务是否启用。
- 可订阅
LocationStream
实时监听位置变化。 - 可订阅
LocationServicesStatusStream
实时监听位置服务状态变化。
开始使用
要使用此插件,请在项目的 pubspec.yaml
文件中添加 fl_location
依赖。例如:
dependencies:
fl_location: ^5.0.0
平台特定配置
🐤 Android
由于插件基于位置工作,需要在 AndroidManifest.xml
文件中添加以下权限:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
如果需要在后台获取位置信息,还需添加以下权限(特别是支持Android 10及以上版本):
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
🐤 iOS
类似地,在 ios/Runner/Info.plist
文件中添加如下描述:
<key>NSLocationWhenInUseUsageDescription</key>
<string>用于收集位置数据。</string>
若需在后台获取位置信息,添加以下描述:
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>用于在后台收集位置数据。</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>用于在后台收集位置数据。</string>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>location</string>
</array>
使用方法
请求位置权限
首先检查位置权限是否已授予,如未授予则请求位置权限:
Future<bool> _requestLocationPermission({bool background = false}) async {
if (!await FlLocation.isLocationServicesEnabled) {
// 位置服务未启用
return false;
}
LocationPermission permission = await FlLocation.checkLocationPermission();
if (permission == LocationPermission.denied) {
permission = await FlLocation.requestLocationPermission();
}
if (permission == LocationPermission.denied ||
permission == LocationPermission.deniedForever) {
// 位置权限已被${permission.name}
return false;
}
// 处理Web端权限逻辑...
if (kIsWeb || kIsWasm) {
return true;
}
// Android需要再次请求后台位置权限...
if (Platform.isAndroid &&
background &&
permission == LocationPermission.whileInUse) {
permission = await FlLocation.requestLocationPermission();
if (permission != LocationPermission.always) {
// 必须始终授予位置权限才能在后台收集位置信息
return false;
}
}
return true;
}
获取当前位置
使用 getLocation
函数获取当前位置:
Future<void> _getLocation() async {
if (await _requestLocationPermission()) {
final Location location = await FlLocation.getLocation();
print('Location: ${location.toJson()}');
}
}
订阅位置流
使用 getLocationStream
函数实时监听位置变化:
StreamSubscription<Location>? _locationSubscription;
Future<void> _subscribeLocationStream() async {
if (await _requestLocationPermission()) {
_locationSubscription = FlLocation.getLocationStream().listen(_onLocation);
}
}
void _onLocation(Location location) {
print('Location: ${location.toJson()}');
}
监听位置服务状态变化
使用 getLocationServicesStatusStream
函数实时监听位置服务状态变化:
StreamSubscription<LocationServicesStatus>? _locationServicesStatusSubscription;
Future<void> _subscribeLocationServicesStatusStream() async {
_locationServicesStatusSubscription = FlLocation.getLocationServicesStatusStream()
.listen(_onLocationServicesStatus);
}
void _onLocationServicesStatus(LocationServicesStatus status) {
print('LocationServicesStatus: $status');
}
示例代码
下面是一个完整的示例应用程序,演示了如何结合上述方法来创建一个简单的Flutter应用以展示位置信息:
import 'dart:async';
import 'dart:io';
import 'package:fl_location/fl_location.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() => runApp(ExampleApp());
enum ButtonState { LOADING, DONE, DISABLED }
class ExampleApp extends StatefulWidget {
@override
State<StatefulWidget> createState() => _ExampleAppState();
}
class _ExampleAppState extends State<ExampleApp> {
StreamSubscription<Location>? _locationSubscription;
final _resultText = ValueNotifier('');
final _getLocationButtonState = ValueNotifier(ButtonState.DONE);
final _subscribeLocationStreamButtonState = ValueNotifier(ButtonState.DONE);
final _isSubscribeLocationStream = ValueNotifier(false);
Future<bool> _requestLocationPermission({bool background = false}) async {
if (!await FlLocation.isLocationServicesEnabled) {
_resultText.value = '位置服务未启用。';
return false;
}
LocationPermission permission = await FlLocation.checkLocationPermission();
if (permission == LocationPermission.denied) {
permission = await FlLocation.requestLocationPermission();
}
if (permission == LocationPermission.denied ||
permission == LocationPermission.deniedForever) {
_resultText.value = '位置权限已被${permission.name}。';
return false;
}
if (kIsWeb || kIsWasm) {
return true;
}
if (Platform.isAndroid &&
background &&
permission == LocationPermission.whileInUse) {
permission = await FlLocation.requestLocationPermission();
if (permission != LocationPermission.always) {
_resultText.value =
'必须始终授予位置权限才能在后台收集位置信息。';
return false;
}
}
return true;
}
Future<void> _getLocation() async {
if (await _requestLocationPermission()) {
_getLocationButtonState.value = ButtonState.LOADING;
_subscribeLocationStreamButtonState.value = ButtonState.DISABLED;
final Duration timeLimit = const Duration(seconds: 10);
await FlLocation.getLocation(timeLimit: timeLimit).then((location) {
_onLocation(location);
}).onError((error, stackTrace) {
_onError(error);
}).whenComplete(() {
_getLocationButtonState.value = ButtonState.DONE;
_subscribeLocationStreamButtonState.value = ButtonState.DONE;
});
}
}
Future<void> _subscribeLocationStream() async {
if (await _requestLocationPermission()) {
if (_locationSubscription != null) {
await _unsubscribeLocationStream();
}
_locationSubscription = FlLocation.getLocationStream()
.handleError(_onError)
.listen(_onLocation);
_getLocationButtonState.value = ButtonState.DISABLED;
_isSubscribeLocationStream.value = true;
}
}
Future<void> _unsubscribeLocationStream() async {
await _locationSubscription?.cancel();
_locationSubscription = null;
_getLocationButtonState.value = ButtonState.DONE;
_isSubscribeLocationStream.value = false;
}
void _onLocation(Location location) {
_resultText.value = location.toJson().toString();
}
void _onError(dynamic error) {
_resultText.value = error.toString();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter Location'),
centerTitle: true,
),
body: _buildContentView(),
),
);
}
Widget _buildContentView() {
final Widget buttonWidget = Column(
children: [
const SizedBox(height: 8.0),
ValueListenableBuilder(
valueListenable: _getLocationButtonState,
builder: (context, state, _) {
return _buildTestButton(
text: 'GET Location',
state: state,
onPressed: _getLocation,
);
},
),
const SizedBox(height: 8.0),
ValueListenableBuilder(
valueListenable: _subscribeLocationStreamButtonState,
builder: (context, state, _) {
return ValueListenableBuilder(
valueListenable: _isSubscribeLocationStream,
builder: (context, isSubscribe, __) {
final String text;
final VoidCallback? onPressed;
if (isSubscribe) {
text = '取消订阅位置流';
onPressed = _unsubscribeLocationStream;
} else {
text = '订阅位置流';
onPressed = _subscribeLocationStream;
}
return _buildTestButton(
text: text,
state: state,
onPressed: onPressed,
);
},
);
},
),
],
);
final Widget resultWidget = ValueListenableBuilder(
valueListenable: _resultText,
builder: (context, resultText, _) {
return Text(resultText);
},
);
return ListView(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
children: [
buttonWidget,
const SizedBox(height: 10.0),
resultWidget,
],
);
}
Widget _buildTestButton({
required String text,
required ButtonState state,
VoidCallback? onPressed,
}) {
return ElevatedButton(
child: (state == ButtonState.LOADING)
? SizedBox.fromSize(
size: const Size.square(20.0),
child: const CircularProgressIndicator(strokeWidth: 2.0))
: Text(text),
onPressed: (state == ButtonState.DONE) ? onPressed : null,
);
}
@override
void dispose() {
_locationSubscription?.cancel();
_resultText.dispose();
_getLocationButtonState.dispose();
_subscribeLocationStreamButtonState.dispose();
_isSubscribeLocationStream.dispose();
super.dispose();
}
}
这个示例展示了如何在Flutter应用中集成 fl_location
插件来实现基本的位置管理功能,包括请求权限、获取当前位置、订阅位置流和监听位置服务状态变化等。
更多关于Flutter定位功能插件fl_location的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter定位功能插件fl_location的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter项目中使用fl_location
插件来实现定位功能的示例代码。fl_location
是一个用于获取设备位置信息的Flutter插件。
首先,确保你的Flutter项目已经设置好,并且已经在pubspec.yaml
文件中添加了fl_location
依赖:
dependencies:
flutter:
sdk: flutter
fl_location: ^4.0.0 # 请检查最新版本号
然后,运行flutter pub get
来安装依赖。
接下来,我们需要在Android和iOS平台上进行一些配置,以确保定位功能能够正常工作。
Android配置
在android/app/src/main/AndroidManifest.xml
文件中添加以下权限:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.yourapp">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 其他配置 -->
</manifest>
iOS配置
在ios/Runner/Info.plist
文件中添加以下权限请求:
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to location when in use.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs access to location always.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs access to location always.</string>
Flutter代码实现
接下来,我们在Flutter项目中实现定位功能。以下是一个完整的示例代码:
import 'package:flutter/material.dart';
import 'package:fl_location/fl_location.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Location Example'),
),
body: LocationScreen(),
),
);
}
}
class LocationScreen extends StatefulWidget {
@override
_LocationScreenState createState() => _LocationScreenState();
}
class _LocationScreenState extends State<LocationScreen> {
FlLocation _flLocation = FlLocation();
LocationData? _locationData;
LocationPermission _currentPermission = LocationPermission.denied;
@override
void initState() {
super.initState();
_getLocationPermission();
}
Future<void> _getLocationPermission() async {
bool serviceEnabled;
LocationPermission permission;
// Test if location services are enabled.
serviceEnabled = await _flLocation.serviceEnabled();
if (!serviceEnabled) {
return Future.error('Location services are disabled.');
}
permission = await _flLocation.requestPermission();
if (permission == LocationPermission.deniedForever) {
// Permissions are permanently denied, we cannot request permissions anymore.
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
}
if (permission == LocationPermission.denied) {
permission = LocationPermission.denied;
}
_currentPermission = permission;
if (_currentPermission == LocationPermission.whileInUse ||
_currentPermission == LocationPermission.always) {
_getLocation();
}
}
Future<void> _getLocation() async {
try {
_locationData = await _flLocation.getCurrentLocation(
accuracy: LocationAccuracy.high,
);
setState(() {});
} catch (e) {
print(e);
}
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Current Permission: $_currentPermission',
style: TextStyle(fontSize: 18),
),
SizedBox(height: 20),
if (_locationData != null)
Text(
'Latitude: ${_locationData!.latitude}, Longitude: ${_locationData!.longitude}',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
)
else
Text(
'Location data is not available.',
style: TextStyle(fontSize: 18),
),
],
),
);
}
}
解释
- 依赖管理:在
pubspec.yaml
文件中添加fl_location
依赖。 - 权限配置:在Android和iOS平台上配置必要的权限。
- Flutter代码:
MyApp
和LocationScreen
是两个主要的Widget。_getLocationPermission
方法用于请求位置权限。_getLocation
方法用于获取当前位置。- 根据权限状态和位置数据更新UI。
通过上述步骤和代码,你可以在Flutter项目中实现定位功能。确保在实际应用中处理更多的错误情况和用户交互,以提升用户体验。