التخزين المحلي للبيانات

مقدمة إلى تخزين البيانات المحلي في فلاتر

15 دقيقة الدرس 1 من 12

مقدمة إلى تخزين البيانات المحلي في فلاتر

تحتاج كل تطبيقات Flutter الحقيقية تقريباً إلى الاحتفاظ بالبيانات بين الجلسات. سواء كنت تحفظ تفضيلات المستخدم، أو تخزن مؤقتاً استجابات الـ API، أو تدير سلة تسوق، أو تعمل على قاعدة بيانات محلية من السجلات، فإن تخزين البيانات المحلي مهارة أساسية. لا تمتلك Flutter نظام تخزين واحداً مدمجاً — بدلاً من ذلك، توفر نظاماً بيئياً غنياً من الحزم المناسبة لأشكال البيانات وأحجامها ومتطلبات الأداء المختلفة.

في هذا الدرس ستستعرض المشهد الكامل لخيارات التخزين المتاحة لمطوري Flutter، وتفهم المقايضات بينها، وتتعلم كيفية اختيار الأداة المناسبة لأي متطلب معين.

ملاحظة: "التخزين المحلي" يعني البيانات التي تعيش على الجهاز، بصرف النظر عن أي اتصال شبكي. تبقى بعد إعادة تشغيل التطبيق، وفي معظم الحالات بعد إعادة تشغيل نظام التشغيل. وهو مختلف عن الحالة في الذاكرة (التي تُفقد عند إغلاق التطبيق) والتخزين البعيد (قاعدة بيانات خلفية أو سحابية).

خيارات التخزين المحلي الرئيسية

يختار مطورو Flutter عادةً من ست فئات من التخزين المحلي:

  • SharedPreferences — أزواج مفتاح-قيمة خفيفة الوزن للإعدادات البسيطة
  • SQLite (عبر sqflite) — محرك قاعدة بيانات علائقية كاملة للبيانات المنظمة
  • Hive — مخزن مفتاح-قيمة / صندوق NoSQL سريع مع محولات الأنواع الاختيارية
  • Drift (المعروف سابقاً بـ Moor) — غلاف SQLite تفاعلي آمن من حيث الأنواع مع DSL بلغة Dart
  • File / Path Provider — إدخال/إخراج ملفات خام لأي بيانات ثنائية أو نصية
  • Flutter Secure Storage — تخزين مفتاح-قيمة مشفر مدعوم بسلسلة مفاتيح المنصة

SharedPreferences

SharedPreferences (الحزمة: shared_preferences) تغلف SharedPreferences الخاصة بنظام Android وNSUserDefaults الخاصة بنظام Apple. تخزن خريطة مسطحة من الأنواع البدائية (bool، int، double، String، List<String>) مُسلسلة على القرص كـ XML أو قائمة خصائص.

SharedPreferences — قراءة وكتابة

import 'package:shared_preferences/shared_preferences.dart';

Future<void> saveTheme(String theme) async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString('theme', theme);
}

Future<String> loadTheme() async {
  final prefs = await SharedPreferences.getInstance();
  return prefs.getString('theme') ?? 'light';
}

الأفضل لـ: تفضيلات المستخدم (السمة، اللغة، علامات التأهيل، الإصدار الأخير المشاهد). تجنب تخزين الكائنات الكبيرة أو المجموعات أو البيانات الحساسة هنا — فهو غير مشفر وغير مصمم للاستعلام.

SQLite عبر sqflite

تضع حزمة sqflite محرك SQLite مباشرةً داخل حزمة التطبيق. تكتب SQL خاماً أو تستخدم أساليب مساعدة لإدارة الجداول والفهارس والمعاملات. إنه الخيار الصحيح عندما تكون بياناتك علائقية بطبيعتها — مثل جهات الاتصال والمهام والسجلات المالية ذات علاقات المفاتيح الخارجية.

sqflite — فتح قاعدة بيانات وإدراج صف

import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

Future<Database> openAppDatabase() async {
  final dbPath = await getDatabasesPath();
  return openDatabase(
    join(dbPath, 'app.db'),
    version: 1,
    onCreate: (db, version) async {
      await db.execute('''
        CREATE TABLE tasks (
          id      INTEGER PRIMARY KEY AUTOINCREMENT,
          title   TEXT    NOT NULL,
          done    INTEGER NOT NULL DEFAULT 0
        )
      ''');
    },
  );
}

Future<void> insertTask(Database db, String title) async {
  await db.insert('tasks', {'title': title, 'done': 0});
}

الأفضل لـ: البيانات العلائقية المنظمة، والاستعلامات المعقدة باستخدام JOIN أو GROUP BY، والبيانات التي تحتاج إلى فهرسة للبحث السريع. المقايضة: تكتب سلاسل SQL خاماً (عرضة للأخطاء) وتدير عمليات الترحيل يدوياً.

Hive

Hive (الحزمة: hive_flutter) مخزن NoSQL خفيف الوزن مكتوب بلغة Dart الخالصة. تعيش البيانات في صناديق مكتوبة (فكر فيها كمجموعات). باستخدام TypeAdapter يمكنك تسلسل فئات Dart الخاصة بك مباشرةً. يُعدّ Hive أسرع بكثير من SQLite لعمليات القراءة والكتابة البسيطة لأنه يعمل كمخزن في الذاكرة مدعوم بملفات ثنائية — بدون عبء تحليل SQL.

الأفضل لـ: تخزين الكائنات مؤقتاً، والتطبيقات التي تعمل أولاً دون اتصال، والتفضيلات الأغنى من الأنواع البدائية. المقايضة: لا توجد استعلامات علائقية؛ ربط البيانات عبر الصناديق يتم في كود Dart.

Drift (المعروف سابقاً بـ Moor)

Drift يجلس فوق SQLite لكنه يستبدل سلاسل SQL الخاماً بـ DSL Dart آمن من حيث الأنواع يُولَّد وقت البناء. تُكتشف التغييرات في المخطط وقت الترجمة، لا في وقت التشغيل. كما يدعم الاستعلامات التفاعلية — Stream يُصدر قائمة جديدة في كل مرة يتغير فيها الجدول الأساسي — مما يتكامل بشكل طبيعي مع StreamBuilder في Flutter.

الأفضل لـ: التطبيقات المتوسطة إلى الكبيرة التي تحتاج قوة SQLite لكنها تريد الأمان وقت الترجمة والتفاعلية. المقايضة: يضيف توليد الكود إلى خط أنابيب البناء (build_runner).

File / Path Provider

للبيانات التي لا تناسب نموذج المفتاح-القيمة أو الجداول — الصور وملفات PDF والتسجيلات الصوتية وتصدير JSON — تكتب مباشرةً إلى نظام الملفات باستخدام dart:io وتحدد المجلدات باستخدام path_provider. توفر الحزمة مواقع صحيحة للمنصة مثل مجلد المستندات ومجلد ذاكرة التخزين المؤقت المؤقتة.

الأفضل لـ: الملفات الثنائية والكتل النصية الكبيرة والمحتوى الذي ينشئه المستخدم والتقارير التي يولدها التطبيق. المقايضة: أنت مسؤول عن كل منطق التسلسل والإصدار والتنظيف.

Flutter Secure Storage

Flutter Secure Storage (الحزمة: flutter_secure_storage) تخزن أزواج المفتاح-القيمة مشفرةً بـ AES على نظام Android (عبر EncryptedSharedPreferences أو Android Keystore) ومخزنة في iOS/macOS Keychain. لا تُكتب القيم أبداً على القرص بنص عادي.

الأفضل لـ: الرموز المميزة وكلمات المرور وأرقام PIN ومفاتيح API ورموز التحديث — أي شيء سيكون حادثة أمنية إذا استُخرج من الجهاز. المقايضة: أبطأ من SharedPreferences؛ يتعامل فقط مع قيم String.

اختيار خيار التخزين المناسب

استخدم دليل القرار هذا عند اختيار خلفية التخزين:

  • إعدادات بسيطة (bool، int، String)؟ → SharedPreferences
  • بيانات اعتماد أو رموز مميزة حساسة؟ → Flutter Secure Storage
  • بيانات علائقية مع استعلامات SQL؟ تحتاج أماناً وقت الترجمة؟ → Drift
  • بيانات علائقية، مرتاح مع SQL الخام؟ → sqflite
  • رسم بياني للكائنات، تحتاج سرعة، لا استعلامات معقدة؟ → Hive
  • ملفات ثنائية، صور، كتل عشوائية؟ → File + path_provider
نصيحة: هذه الخيارات ليست متنافية. غالباً ما يجمع التطبيق الاحترافي عدة خيارات: SharedPreferences لإعدادات واجهة المستخدم، وFlutter Secure Storage لرمز المصادقة، وDrift أو Hive لبيانات API المخزنة مؤقتاً، وpath_provider لملفات الوسائط المنزلة.

مقارنة سريعة

يلخص الجدول أدناه الأبعاد الرئيسية عبر جميع الخيارات الستة:

الخيارنموذج البياناتمشفرتفاعليالتعقيد
SharedPreferencesمفتاح-قيمة (أنواع بدائية)لالامنخفض
Secure Storageمفتاح-قيمة (String)نعملامنخفض
Hiveمفتاح-قيمة / كائناتاختياريValueListenableمتوسط
sqfliteعلائقي (SQL)لالامتوسط
Driftعلائقي (Dart DSL)لاStreamمتوسط-عالي
File / path_providerبايتات خام / نصيدويلامتفاوت
تحذير: لا تخزن أبداً كلمات المرور أو الرموز المميزة أو المفاتيح الخاصة في SharedPreferences أو الملفات العادية. على جهاز Android المُعطّل أو iPhone المكسور، يمكن قراءة التخزين غير المشفر بسهولة. استخدم دائماً Flutter Secure Storage للقيم الحساسة.

الخلاصة

يمنحك النظام البيئي للتخزين المحلي في Flutter أداةً لكل سيناريو. SharedPreferences يتعامل مع الإعدادات البسيطة بسطر واحد من الكود. Flutter Secure Storage يؤمن القيم الحساسة باستخدام سلاسل مفاتيح المنصة. Hive يوفر استمرارية كائنات سريعة وبدون مخطط ثابت. sqflite يجلب كامل قوة SQL إلى جهازك. Drift يضيف أماناً وقت الترجمة والتفاعلية فوق SQLite. path_provider + dart:io يغطي أي احتياج قائم على الملفات. في الدروس القادمة ستنفذ كل خيار بكود حقيقي وتتعلم الأنماط المستخدمة في تطبيقات Flutter الاحترافية.