الوصول إلى الكاميرا ومُختار الصور
الوصول إلى الكاميرا ومُختار الصور
تحتاج معظم تطبيقات الهاتف إلى القدرة على التقاط الصور أو السماح للمستخدمين باختيار الصور من معرض الجهاز. يجعل Flutter ذلك أمرًا مباشرًا من خلال حزمة image_picker الرسمية التي يشرف عليها فريق Flutter. في هذا الدرس ستتعلم كيفية دمج image_picker، وطلب أذونات المنصة اللازمة، والتقاط الصور أو اختيارها من المعرض، ثم عرض الملف الناتج داخل شجرة الودجات.
إضافة حزمة image_picker
أضف الاعتمادية إلى pubspec.yaml ثم نفّذ flutter pub get:
pubspec.yaml
dependencies:
flutter:
sdk: flutter
image_picker: ^1.1.2
إعداد أذونات المنصة
تتطلب كل منصة تصريحات أذونات صريحة قبل أن يتمكن تطبيقك من الوصول إلى موارد الأجهزة. إغفال هذه الخطوة هو أحد أكثر الأسباب شيوعًا لعودة image_picker بقيمة null بصمت أثناء التشغيل.
- iOS — أضف المدخلات التالية إلى
ios/Runner/Info.plist:NSCameraUsageDescription— يُعرض عند طلب الوصول إلى الكاميراNSPhotoLibraryUsageDescription— يُعرض عند طلب الوصول إلى المعرضNSPhotoLibraryAddUsageDescription— مطلوب إذا كنت ستحفظ الصور في المعرض
- Android — لنظام Android 13 فما فوق (API 33+) صرّح بـ
READ_MEDIA_IMAGESفيAndroidManifest.xml. للإصدارات الأقدم تستخدم الإضافةREAD_EXTERNAL_STORAGEتلقائيًا.
NSPhotoLibraryUsageDescription أو NSCameraUsageDescription غائبة عن Info.plist، سيتعطل تطبيقك عند محاولة عرض المُختار. قدّم دائمًا نصًا واضحًا ومفهومًا يصف السبب — يرفض مراجعو Apple التطبيقات التي تحتوي على أوصاف مبهمة.اختيار صورة من المعرض
تُوفّر فئة ImagePicker طريقة pickImage التي تُعيد XFile? (مُعالج ملف مستقل عن المنصة). الطريقة غير متزامنة وتُعيد null إذا ألغى المستخدم الاختيار.
مثال على اختيار صورة من المعرض
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
class GalleryPickerDemo extends StatefulWidget {
const GalleryPickerDemo({super.key});
@override
State<GalleryPickerDemo> createState() => _GalleryPickerDemoState();
}
class _GalleryPickerDemoState extends State<GalleryPickerDemo> {
File? _selectedImage;
final ImagePicker _picker = ImagePicker();
Future<void> _pickFromGallery() async {
final XFile? file = await _picker.pickImage(
source: ImageSource.gallery,
imageQuality: 85, // ضغط الصورة إلى 85% من الجودة
maxWidth: 1024, // تحديد العرض الأقصى لتقليل حجم الملف
);
if (file != null) {
setState(() {
_selectedImage = File(file.path);
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('مُختار الصور')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (_selectedImage != null)
Image.file(_selectedImage!, height: 250, fit: BoxFit.cover)
else
const Text('لم يتم اختيار صورة.'),
const SizedBox(height: 24),
ElevatedButton.icon(
onPressed: _pickFromGallery,
icon: const Icon(Icons.photo_library),
label: const Text('اختر من المعرض'),
),
],
),
),
);
}
}
XFile هو تجريد ملف متعدد المنصات من حزمة cross_file (يُعاد تصديره من image_picker). حوّله إلى dart:io File باستخدام File(xfile.path) عندما تحتاج إلى واجهات برمجة نظام الملفات، مثل الرفع عبر http.MultipartFile.التقاط صورة بالكاميرا
للتبديل من المعرض إلى الكاميرا المباشرة، كل ما تحتاجه هو تغيير واحد: مرّر ImageSource.camera. تبقى جميع المعاملات الأخرى — الجودة والأبعاد القصوى والكاميرا المفضّلة (الأمامية أو الخلفية) — كما هي.
مثال على التقاط صورة بالكاميرا
Future<void> _captureFromCamera() async {
final XFile? photo = await _picker.pickImage(
source: ImageSource.camera,
preferredCameraDevice: CameraDevice.rear,
imageQuality: 90,
);
if (photo != null) {
setState(() {
_selectedImage = File(photo.path);
});
}
}
// في طريقة build أضف زرًا ثانيًا:
ElevatedButton.icon(
onPressed: _captureFromCamera,
icon: const Icon(Icons.camera_alt),
label: const Text('افتح الكاميرا'),
),
التعامل مع الأذونات بشكل سلس
على نظام Android 13 فما فوق وعلى iOS، قد يرفض النظام الإذن بشكل دائم أو للجلسة الحالية. تتولى إضافة image_picker طلب الإذن الأولي تلقائيًا، لكن ينبغي لك معالجة إعادة القيمة null بعناية والنظر في توجيه المستخدم لفتح إعدادات التطبيق عند رفض الإذن نهائيًا.
- إذا أعادت
pickImageقيمةnull، فقد ألغى المستخدم العملية — لا تفعل شيئًا. - إذا رُفض الإذن، قد يُطلق بعض إصدارات الإضافة
PlatformException— أحِط الاستدعاء بـ try-catch. - استخدم حزمة permission_handler بجانب image_picker عندما تحتاج إلى فحص حالة الأذون بدقة.
imageQuality (مثلًا 80–90) وقيود maxWidth/maxHeight عند الاختيار من المعرض. الصور غير المقيّدة من الهواتف الذكية الحديثة قد يصل حجمها إلى 10–20 ميغابايت، مما سيُبطئ عملية الرفع ويُرهق الذاكرة عند الاستخدام مع Image.file.اختيار مقاطع الفيديو
يُتيح الكائن نفسه من ImagePicker طريقتي pickVideo وpickMultiImage لاختيار عدة صور في آنٍ واحد. أنواع الإعادة مطابقة لتلك الخاصة بـ pickImage:
pickVideo(source: ImageSource.gallery)— تُعيدXFile?pickMultiImage()— تُعيدList<XFile>(لا تُعيد null أبدًا، قد تكون فارغة)
ملخص
توفّر حزمة image_picker واجهة برمجية عالية المستوى وواعية بالأذونات تغطّي أكثر سيناريوهات الوصول إلى الوسائط شيوعًا على الهاتف: الاختيار من المعرض والتقاط الصور بالكاميرا. سير العمل ثابت: أنشئ كائن ImagePicker، انتظر pickImage(source: ...)، تحقق من null، حوّل إلى File عند الحاجة، ثم استدعِ setState لتحديث واجهة المستخدم. التطبيقات القوية تضع دائمًا حدودًا لأبعاد الصور، وتتعامل مع الإلغاء، وتُرشد المستخدمين إلى إعدادات التطبيق عند رفض الإذن نهائيًا.