تصميم حفظ سجلات Hive في Flutter وفهرسة العمر مع التحقق من الإدخال وحفظ اتساق المفاتيح، وتوضيح كلفة الإدراج والبحث الثنائي.
البحث اللوغاريتمي لا يجعل الإدراج لوغاريتميًا
الوصول إلى موضع الإدراج بالبحث الثنائي شيء، وتحريك عناصر List.insert وكتابة قائمة المفاتيح كلها شيء آخر. لا تكرر كتابة الفهرس بعد كل سجل في تجربة مئة ألف عنصر داخل callback واجهة. قِس حجم بيانات واقعيًا واكتب دفعات مدروسة، وراجع بديلًا يدعم الاستعلام والفهارس إذا كانت متطلبات البيانات أكبر.
ملكية الصناديق والبيانات
افتح الصندوق وسجل adapter في طبقة تهيئة واضحة، ولا تنشئ controllers داخل build. تحقق من الاسم والعمر عبر int.tryParse ثم احفظ العمر المدخل لا قيمة Random. افصل مصدر السجلات عن الفهرس وأعد بناء الفهرس إن فُقد مفتاح أو فشل حفظ جزء؛ لا تفترض معاملة واحدة عبر صندوقين مستقلين. يجب أن يكون Person وTypeAdapter معرّفين أو توضح أن الكود مخطط وليس مثالًا كاملًا.
نموذج سجل صغير وصريح
المثال المصحح يعرض عقدًا بسيطًا لسجل اسم وعمر في Box<Map> مفتوح من طبقة تهيئة المشروع. التحقق يرفض اسمًا فارغًا وعمرًا غير صحيح أو خارج مجال المثال قبل الكتابة. في تطبيق إنتاجي يفضل نموذج مكتوب النوع وadapter أو طبقة تحويل تختبر البيانات القديمة. لا تستخدم عمرًا عشوائيًا بدل القيمة التي أدخلها المستخدم، ولا تعرض index الصف بدل اسم الشخص وعمره لأن ذلك يخفي أخطاء البيانات.
ترتيب لقطة مقابل فهرس دائم
peopleByAge ينسخ القيم إلى قائمة ثم يفرزها؛ هذه طريقة واضحة لكمية محلية صغيرة وليست وعدًا باستعلام مفهرس. البحث الثنائي يجد موضعًا بسرعة في قائمة مرتبة لكن List.insert ما زالت تحرك عناصر، وكتابة قائمة المفاتيح بعد كل سجل تضيف كلفة. إذا احتجت فهرسًا دائمًا فحدد مصدر الحقيقة وكيف تعيد بناءه وكيف تمنع عدم اتساقه عند فشل كتابة. اختبر نفس الأعمار وتغير العمر والحذف والمفاتيح المفقودة.
دورة الحياة والفشل
افتح الصندوق مرة في المالك المناسب، واعرض تحميلًا أو فشلًا بدل قراءة late مورد لم يجهز. TextEditingController ينشأ خارج build ويغلق في dispose إن كانت الصفحة تملكه. لا تغلق صندوقًا تشترك فيه ميزات أخرى عند خروج صفحة واحدة؛ ملكية المورد تحدد مكان إغلاقه. عطّل زر الحفظ أثناء الطلب إذا كان تكراره غير مقصود وأبق مسودة المستخدم عند فشل الكتابة، ثم حدث العرض بعد نجاح العملية بدل افتراض أن كل await ستنجح.
اختبار بيانات واقعية
ابدأ بصندوق اختبار معزول وسجلات قليلة، واختبر الإدخال غير الصالح والفرز وقيم schema قديمة قبل تجربة كميات كبيرة. لا تُنشئ مئة ألف سجل بضغط زر واجهة على قاعدة بيانات المستخدم. سجل زمن الكتابة والقراءة وحجم الملف واستهلاك الذاكرة إن كانت ميزة منتجك تحتاج أحجامًا كبيرة، ثم قيّم قاعدة تدعم الاستعلامات المطلوبة. هذا يفصل تعلم Hive عن تجربة ضغط قد تجمد الواجهة أو تملأ التخزين دون أن تعلمك سبب المشكلة.
عرض نتيجة الحفظ
استعمل المفتاح العائد من add كهوية للسجل عند الحاجة، ولا تستبدله بفهرس القائمة بعد الفرز؛ كلاهما قد يمثل شيئًا مختلفًا.
مثال الشفرة بعد المراجعة
اقرأ التعليقات ومتطلبات الإعداد قبل النقل إلى مشروعك. أمثلة الواجهات ذات main مستقلة، أما العقود والمقاطع الأصلية وملفات الإعداد فموضح نطاقها داخل المثال.
// Repository operations, not a standalone application.
// Requires Hive initialized and a Box<Map> opened by the owner.
import 'package:hive/hive.dart';
Future<int> addPerson(Box<Map> box, String name, String ageText) async {
final age = int.tryParse(ageText.trim());
if (name.trim().isEmpty || age == null || age < 0 || age > 130) {
throw const FormatException('Valid name and age required');
}
return box.add({'name': name.trim(), 'age': age});
}
List<Map> peopleByAge(Box<Map> box) {
final people = box.values.map((x) => Map.of(x)).toList();
people.sort((a,b) => (a['age'] as int).compareTo(b['age'] as int));
return people;
}
// Small trusted local records only. Validate/migrate older data before reading.
// This sorts a snapshot; it does not claim a transactional database index.مرجع للتنفيذ
راجع التوثيق الخاص بهذا الموضوع وطابق إعداداته مع إصدار أدوات مشروعك. اختبر السلوك المستهدف وحالة الفشل قبل إدماج المثال في تطبيق منشور.






