إزاي تبني تطبيق موبايل Offline-First باستخدام Flutter و Drift و Hive
أكيد مريت بالموقف ده، بتطور تطبيق فلاتر (Flutter) وزي الفل، وفجأة المستخدم يدخل في الأسانسير أو شبكة الموبايل تقع، والتطبيق يفتح شاشة "مفيش إنترنت" واليوزر يقفل التطبيق بزهق. النهاردة هنتكلم عن الحل السحري اللي بيخلي تطبيقاتك تعيش وتتنفس بدون إنترنت، وهو معمارية (Offline-First Architecture).
Table of contents [Show]
يعني إيه Offline-First وليه الموبايل محتاجه؟
معمارية (Offline-First) ببساطة هي إن التطبيق بتاعك بيعتمد على قاعدة البيانات المحلية كأنها المصدر الأساسي (Source of Truth)، والإنترنت بيجي دوره عشان "يحدث" البيانات دي في الخلفية. ميزة النهج ده إن التطبيق دايماً شغال، سريع جداً في الاستجابة، والمستخدم مش بيحس بأي تأخير.
أدواتك في الرحلة دي: Hive و Drift
عشان نطبق المبدأ ده في (Flutter)، هنحتاج اتنين من أتقل الأدوات:
- Hive: قاعدة بيانات (Key-Value) خفيفة جداً وسريعة، ممتازة لتخزين الـ (User Settings) أو البيانات اللي حجمها بسيط.
- Drift: هي المكتبة رقم 1 لو عايز تتعامل مع (SQLite) في فلاتر بشكل احترافي، بتديك (Type-safety) رهيب وتسهل عليك التعامل مع الجداول المعقدة.
خطوات تنفيذ الـ Sync Strategy
عشان تعمل (Synchronization) مظبوط، محتاج تتبع المنهج ده:
- Local Storage: أي عملية إضافة أو تعديل بتسمع الأول في (Drift) فوراً.
- Flagging: ضيف عمود (Column) في جدولك اسمه
is_synced، بياخد قيمة (Boolean). لما اليوزر يعدل حاجة والنت قاطع، بتخلي قيمته (false). - Background Sync: استخدم مكتبة زي (Connectivity Plus) عشان تراقب حالة النت. بمجرد ما يرجع، اعمل (Loop) على كل الصفوف اللي
is_synced = falseوابعتهوم للسيرفر.
كود عملي: مثال بسيط لـ Drift
ده شكل مبسط لتعريف جدول في (Drift) بيراعي حالة المزامنة:
class Todos extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get content => text()();
BoolColumn get isSynced => boolean().withDefault(const Constant(false))();
}
بعد ما بتعمل العملية، بتحدث الحالة في قاعدة البيانات المحلية:
Future syncDataToServer() async {
final pendingItems = await select(todos)..where((t) => t.isSynced.equals(false));
for (var item in await pendingItems.get()) {
final response = await api.upload(item);
if (response.success) {
update(todos)..where((t) => t.id.equals(item.id))..write(TodosCompanion(isSynced: Value(true)));
}
}
}
إدارة الـ State مع Hive للبيانات المؤقتة
مش كل حاجة محتاجة (SQLite)، لو عندك بيانات زي "حالة تسجيل الدخول" أو "تفضيلات المستخدم"، استخدم (Hive):
final box = Hive.box('user_settings');
box.put('theme', 'dark');
خلاصة الكلام: نصيحة من أخ
بناء معمارية (Offline-First) هو اللي بيفرق بين المبرمج "الهاوي" والمحترف. اه الموضوع بياخد وقت أطول في التخطيط، لكن تجربة المستخدم (UX) اللي بتقدمها بتبقى خرافية. ابدأ دايماً بتصميم قاعدة البيانات المحلية، وماتخليش الاعتماد على الـ (API) هو أساس التطبيق. اتعلم تقنيات الـ (Data Sync Conflict Resolution) عشان تتفادى المشاكل لما نفس البيانات تتعدل من مكانين في نفس الوقت.