استرجاع معلومات الجهاز باستخدام device_info_plus
استرجاع معلومات الجهاز باستخدام device_info_plus
كثير من تطبيقات Flutter في الإنتاج تحتاج إلى معرفة الجهاز الذي تعمل عليه — لتخصيص تجربة المستخدم، أو الإبلاغ عن الأعطال بدقة، أو تطبيق ترخيص خاص بالمنصة، أو تسجيل البيانات التشخيصية. توفر حزمة device_info_plus واجهة برمجية نظيفة ومكتوبة بالأنواع تعرض البيانات الوصفية الخاصة بكل منصة مثل طراز الجهاز وإصدار نظام التشغيل واسم النظام والمعرّفات الفريدة على كل من Android وiOS (وكذلك الويب وWindows وmacOS وLinux).
device_info_plus هو الخليفة الذي تحتفظ به المجتمع للإضافة الأصلية device_info. استخدم دائماً device_info_plus في المشاريع الجديدة — الحزمة القديمة متروكة وغير مصانة.إضافة التبعية
أضف device_info_plus إلى ملف pubspec.yaml ونفِّذ flutter pub get:
pubspec.yaml
dependencies:
flutter:
sdk: flutter
device_info_plus: ^10.1.0 # استخدم أحدث إصدار مستقر
لا تحتاج إلى أذونات Android إضافية أو إدخالات في Info.plist لنظام iOS للحقول الأساسية التي يغطيها هذا الدرس.
واجهة برمجة التطبيقات الأساسية — DeviceInfoPlugin
نقطة الدخول للحزمة هي DeviceInfoPlugin. تنشئ نسخة واحدة منه وتستدعي دالة الجلب المناسبة للمنصة، التي تُعيد كائن معلومات بأنواع محددة:
- Android —
androidInfoيُعيد كائنAndroidDeviceInfo - iOS —
iosInfoيُعيد كائنIosDeviceInfo - الويب —
webBrowserInfoيُعيد كائنWebBrowserInfo - Windows / macOS / Linux — كائنات مكتوبة بالأنواع متاحة أيضاً
بما أن كل دالة جلب هي استدعاء غير متزامن لقناة المنصة، يجب عليك استخدام await للنتيجة داخل طريقة async أو تجاوز initState.
جلب معلومات الجهاز (آمن عبر المنصات)
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart' show defaultTargetPlatform, TargetPlatform;
Future<Map<String, String>> fetchDeviceInfo() async {
final plugin = DeviceInfoPlugin();
final info = <String, String>{};
if (defaultTargetPlatform == TargetPlatform.android) {
final android = await plugin.androidInfo;
info['المنصة'] = 'Android';
info['الطراز'] = android.model;
info['العلامة التجارية'] = android.brand;
info['إصدار SDK'] = android.version.sdkInt.toString();
info['إصدار النظام'] = android.version.release;
info['معرّف الجهاز'] = android.id;
info['جهاز حقيقي'] = android.isPhysicalDevice.toString();
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
final ios = await plugin.iosInfo;
info['المنصة'] = 'iOS';
info['الطراز'] = ios.model;
info['اسم الجهاز'] = ios.name;
info['اسم النظام'] = ios.systemName;
info['إصدار النظام'] = ios.systemVersion;
info['معرّف المورّد'] = ios.identifierForVendor ?? 'غير متاح';
info['جهاز حقيقي'] = ios.isPhysicalDevice.toString();
}
return info;
}
defaultTargetPlatform من package:flutter/foundation.dart بدلاً من Platform.isAndroid من dart:io. الأول يعمل على جميع المنصات (بما فيها الويب)؛ الثاني يُطلق استثناءً على الويب.مرجع الحقول الأساسية
فهم معنى كل حقل يساعدك على اختيار الحقل المناسب لحالة الاستخدام الخاصة بك:
- Android —
model: اسم الطراز التسويقي (مثلPixel 8 Pro) - Android —
brand: العلامة التجارية الموجهة للمستهلك (مثلgoogle) - Android —
version.sdkInt: مستوى Android SDK (مثل34لنظام Android 14) - Android —
version.release: سلسلة إصدار نظام التشغيل القابلة للقراءة (مثل14) - Android —
id: سلسلة بصمة البناء؛ ليست معرّف جهاز فريداً ثابتاً - iOS —
identifierForVendor: UUID فريد لكل مورّد تطبيق لكل جهاز؛ يُعاد تعيينه عند إعادة التثبيت إذا لم تكن هناك تطبيقات أخرى من نفس المورّد - iOS —
utsname.machine: معرّف جهاز العتاد (مثلiPhone16,2)
identifierForVendor قد يتغير؛ ANDROID_ID قد يتغير بعد إعادة ضبط المصنع. للتحليلات، يُفضّل استخدام UUID مُنشأ من الخادم ومخزّن في تخزين آمن بدلاً من الاعتماد على معرّفات العتاد.بناء شاشة عرض على طراز الإعدادات
من الأنماط الشائعة عرض البيانات الوصفية للجهاز على شاشة تشخيصية أو شاشة "حول هذا الجهاز". يقوم الودجت بتحميل البيانات بشكل غير متزامن في initState ويخزّنها في Map يقود قائمة ListView من صفوف ListTile.
DeviceInfoScreen — مثال كامل
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class DeviceInfoScreen extends StatefulWidget {
const DeviceInfoScreen({super.key});
@override
State<DeviceInfoScreen> createState() => _DeviceInfoScreenState();
}
class _DeviceInfoScreenState extends State<DeviceInfoScreen> {
Map<String, String> _deviceData = {};
bool _isLoading = true;
@override
void initState() {
super.initState();
_loadDeviceInfo();
}
Future<void> _loadDeviceInfo() async {
final plugin = DeviceInfoPlugin();
final data = <String, String>{};
try {
if (defaultTargetPlatform == TargetPlatform.android) {
final android = await plugin.androidInfo;
data['الطراز'] = android.model;
data['العلامة التجارية'] = android.brand;
data['الشركة المصنّعة'] = android.manufacturer;
data['إصدار Android SDK'] = android.version.sdkInt.toString();
data['إصدار النظام'] = android.version.release;
data['تصحيح الأمان'] = android.version.securityPatch ?? 'غير متاح';
data['جهاز حقيقي'] = android.isPhysicalDevice ? 'نعم' : 'لا';
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
final ios = await plugin.iosInfo;
data['اسم الجهاز'] = ios.name;
data['الطراز'] = ios.model;
data['الجهاز'] = ios.utsname.machine;
data['اسم النظام'] = ios.systemName;
data['إصدار النظام'] = ios.systemVersion;
data['معرّف المورّد'] = ios.identifierForVendor ?? 'غير متاح';
data['جهاز حقيقي'] = ios.isPhysicalDevice ? 'نعم' : 'لا';
}
} catch (e) {
data['خطأ'] = e.toString();
}
if (mounted) {
setState(() {
_deviceData = data;
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('معلومات الجهاز')),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: ListView(
children: _deviceData.entries.map((entry) {
return ListTile(
title: Text(
entry.key,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 13,
color: Colors.grey,
),
),
subtitle: Text(
entry.value,
style: const TextStyle(fontSize: 16),
),
dense: true,
);
}).toList(),
),
);
}
}
أفضل الممارسات
- استدعِ مرة واحدة واحتفظ بالنتيجة — استدعاءات قناة المنصة لها تكلفة أداء؛ خزّن البيانات المُعادة في متغير أو مزوّد إدارة الحالة بدلاً من استدعاء الإضافة بشكل متكرر.
- احمِ باستخدام
mounted— تحقق دائماً منif (mounted)قبل استدعاءsetStateداخل الطرق غير المتزامنة لتجنب ضبط الحالة على ودجت تم التخلص منه. - احتوِ الاستثناءات باستخدام try/catch — على المنصات غير المدعومة أو أثناء الاختبار، قد تُطلق الإضافة استثناءات؛ المعالجة اللطيفة للأخطاء تُبقي تطبيقك مستقراً.
- لا تُشفِّر مستويات SDK بشكل ثابت — قارن
android.version.sdkIntمع ثوابت معروفة أو قيم صحيحة موثّقة جيداً بدلاً من الأرقام السحرية.
device_info_plus واجهة برمجية غير متزامنة بسيطة لقراءة البيانات الوصفية للجهاز الخاصة بالمنصة والمكتوبة بالأنواع. أنشئ نسخة واحدة من DeviceInfoPlugin، وانتظر دالة الجلب الصحيحة للمنصة، وخزّن النتيجة. احمِ تحديثات الحالة غير المتزامنة باستخدام mounted، وفضّل UUID مُنشأ من الخادم على معرّفات العتاد لأغراض التتبع.