أذونات الموقع باستخدام permission_handler
أذونات الموقع باستخدام permission_handler
يجب على التطبيقات المحمولة التي تصل إلى موقع الجهاز أن تطلب إذنًا صريحًا من المستخدم أثناء التشغيل. يعتمد كل من Android وiOS نموذج أذونات وقت التشغيل، مما يعني أن المستخدم يرى مربع حوار النظام ويقرر ما إذا كان سيمنح الوصول أو يرفضه — ويمكن تغيير هذا القرار في أي وقت من تطبيق الإعدادات. توفر حزمة permission_handler من Flutter Community واجهة برمجة Dart موحدة للتحقق من الأذونات وطلبها عبر كلا المنصتين دون كتابة كود خاص بكل منصة.
Info.plist. على Android، يجب الإعلان عن الأذونات ذات الصلة في AndroidManifest.xml. كود Dart وحده غير كافٍ — يجب أن يكون الإعداد الأصلي موجودًا وإلا سيتجاهل النظام طلبك بصمت.إضافة التبعية
أضف permission_handler إلى pubspec.yaml:
dependencies:
flutter:
sdk: flutter
permission_handler: ^11.3.1
ثم شغّل flutter pub get. بعد ذلك، أكمل إعداد المنصة:
- Android — أضف إلى
AndroidManifest.xml(داخل<manifest>، قبل<application>):
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> - iOS — أضف إلى
ios/Runner/Info.plist:
NSLocationWhenInUseUsageDescriptionمع نص تفسيري يقرأه المستخدم
فهم حالات الإذن
تمثل الحزمة كل نتيجة ممكنة كقيمة من تعداد PermissionStatus. معرفة جميع الحالات ضروري لكتابة منطق إذن صحيح وسهل الاستخدام:
- granted — وافق المستخدم على الإذن؛ تابع بشكل طبيعي.
- denied — نقر المستخدم على "لا تسمح" لكن يمكن سؤاله مجددًا (Android) أو ببساطة لم يُسأل بعد في التثبيت الحالي.
- permanentlyDenied — اختار المستخدم "عدم السؤال مجددًا" (Android) أو رفض مطالبة iOS مرتين. لا يمكنك إظهار مربع حوار النظام مجددًا؛ يجب إعادة توجيهه إلى الإعدادات.
- restricted — iOS فقط؛ ضوابط الأبوين أو ملف MDM يمنع منح هذا الإذن.
- limited — iOS 14+ فقط؛ منح المستخدم الوصول لمجموعة فرعية فقط.
- provisional — إشعارات iOS فقط؛ غير ذي صلة بالموقع.
التحقق من الأذونات وطلبها
استخدم Permission.location.status للتحقق من الحالة الحالية دون المطالبة، وPermission.location.request() لإظهار مربع حوار النظام. تحقق دائمًا قبل الطلب لتجنب مربعات الحوار المكررة:
import 'package:permission_handler/permission_handler.dart';
Future<void> handleLocationPermission() async {
// 1. التحقق من الحالة الحالية (لا يظهر مربع حوار)
PermissionStatus status = await Permission.location.status;
if (status.isGranted) {
// ممنوح بالفعل — تابع مباشرة
_startLocationTracking();
return;
}
if (status.isPermanentlyDenied) {
// لا يمكن إظهار مربع حوار النظام؛ وجّه المستخدم للإعدادات
_showSettingsDialog();
return;
}
// 2. طلب الإذن (يظهر مربع حوار النظام مرة واحدة)
status = await Permission.location.request();
if (status.isGranted) {
_startLocationTracking();
} else if (status.isPermanentlyDenied) {
_showSettingsDialog();
} else {
// مرفوض لكن يمكن السؤال لاحقًا
_showPermissionRationale();
}
}
void _startLocationTracking() {
// TODO: استخدم geolocator أو google_maps_flutter
print('تم منح إذن الموقع — بدء التتبع');
}
void _showPermissionRationale() {
print('الإذن مرفوض. يرجى منح الوصول للموقع لاستخدام هذه الميزة.');
}
void _showSettingsDialog() {
// يفتح صفحة الإعدادات الخاصة بالتطبيق على الجهاز
openAppSettings();
}
فتح إعدادات التطبيق باستخدام openAppSettings()
عندما يكون الإذن مرفوضًا نهائيًا، فإن المسار الوحيد للأمام هو إرسال المستخدم إلى شاشة إعدادات نظام التشغيل حتى يتمكن من تبديل الإذن يدويًا. تشحن حزمة permission_handler دالة openAppSettings() لهذا الغرض بالضبط. تُرجع Future<bool> يشير إلى ما إذا كانت شاشة الإعدادات قد فُتحت بنجاح.
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
Future<void> requestLocationWithFallback(BuildContext context) async {
final status = await Permission.location.request();
if (status.isGranted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('تم منح الوصول للموقع!')),
);
return;
}
if (status.isPermanentlyDenied) {
final opened = await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('الموقع مطلوب'),
content: const Text(
'تم رفض إذن الموقع نهائيًا. '
'يرجى تمكينه في الإعدادات للمتابعة.',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx, false),
child: const Text('إلغاء'),
),
ElevatedButton(
onPressed: () => Navigator.pop(ctx, true),
child: const Text('فتح الإعدادات'),
),
],
),
) ?? false;
if (opened) {
await openAppSettings();
}
}
}
shouldShowRequestPermissionRationale القيمة false ويتجاهل النظام الطلبات اللاحقة بصمت. يتعامل التحقق من isPermanentlyDenied مع هذه الحالة تلقائيًا — تحقق منه دائمًا قبل استدعاء request().الموقع الدقيق مقابل التقريبي (Android 12+)
قدّم Android 12 نموذجًا ثنائي المستويات للموقع. استخدم Permission.locationWhenInUse للوصول في المقدمة وPermission.locationAlways للخلفية. على Android 12+، يمكن للمستخدم أيضًا تخفيض الموقع الدقيق إلى تقريبي. تعرض حزمة permission_handler كلتا الدقتين:
Permission.location— يُحل إلىACCESS_FINE_LOCATION(دقيق)Permission.locationWhenInUse— الموقع فقط أثناء وجود التطبيق في المقدمةPermission.locationAlways— موقع الخلفية؛ يتطلب طلبًا ثانيًا بعد منحlocationWhenInUse؛ تتدقق متاجر التطبيقات في هذا الأمر بشدة
Permission.locationAlways دون الحصول أولًا على locationWhenInUse. على iOS 13+ يفرض النظام هذا الترتيب بصرامة. طلب موقع الخلفية دون منح صالح للمقدمة يتسبب في رفض صامت.ملخص
تتطلب إدارة أذونات الموقع بشكل صحيح أربع خطوات رئيسية: الإعلان عن الأذونات في ملفات النظام الأصلية، والتحقق من الحالة الحالية قبل الطلب، ومعالجة جميع قيم PermissionStatus بما في ذلك permanentlyDenied، واستخدام openAppSettings() كمسار أخير. تجعل حزمة permission_handler كل هذا ممكنًا بلغة Dart الخالصة مع واجهة برمجية نظيفة وتعبيرية تعمل بنفس الطريقة على Android وiOS.