فلاتر

ذخیره کردن اطلاعات در فلاتر با دیتابیس Hive

راه های مختلفی برای ذخیره سازی اطلاعات در یک اپلیکیشن وجود دارد که در مطالب قبلی با آنها نیز آشنا شده ایم مثل استفاده از دیتابیس SQLite و یا ذخیره سازی به وسیله SharedPreferences اما در این مطلب ابزار جدیدی به اسم Hive این کار انجام خواهیم داد.

Hive یک دیتابیس سبک, سریع و از نوع NoSQL در زبان دارت و فلاتر می باشد.

در این مقاله انجام عملیات CRUD را با استفاده از این پکیج قرار است انجام دهیم.

آماده سازی پروژه

در این پروژه از Provider استفاده میکنیم که اگر با آن آشنا نیستید بهتر است اول درباره آن مطالعه کنید تا به مشکل نخورید همچنین با طراحی رابط کاربری نیز باید آشنا باشید.

برای اینکه سرعت بیشتری در کار داشته باشیم پروژه ابتدایی آموزش را از انتهای صفحه دانلود کنید. در این پروژه بخش Provider و رابط کاربری ایجاد شده است و مانند تصویر زیر در اختیار شماست.

بعد از دانلود کردن پروژه نیاز به اضافه کردن پکیج های مختلفی داریم که در بخش زیر میتونید مشاهده کنید.

hive: ^1.4.1+1
provider: ^4.3.1
path_provider: ^1.6.5
hive_generator: ^0.5.0

پکیج hive_generator را در بخش dev dependencies قرار دهید.

پیاده سازی Hive

قبل از اینکه از hive استفاده کنید باید در بخش main آن را تنظیم کنید.

import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart' as pathProvider;

Future<void> main() async {
  Directory directory = await pathProvider.getApplicationDocumentsDirectory();
  Hive.init(directory.path);

  runApp(MyApp());
}

با این کار مسیر ذخیره سازی اطلاعات را مشخص میکنیم.

Hive Boxes

زمانی که با Hive کار میکنید اطلاعات به شکل جدول و ستون ذخیره نمیشوند چون گفتیم که دیتابیس ما از نوع No SQL است.

در اینجا مفهومی داریم به نام Box که اطلاعات درون این Box یا جعبه ها ذخیره می شوند.

این جعبه را میتونید مانند یک پوشه در کامپیوتر تصور کنید.

قبل از اینکه بتونید اطلاعات را بخوانید یا بنویسید باید ابتدا این Box را باز کنید که این کار با دستور await Hive.openBox(‘name’) قابل انجام است.

برای دسترسی به نمونه ای از جعبه ای که قبلا باز شده است هم میتونید از دستور Hive.box(‘name’) استفاده کنید.

معرفی TypeAdapters

در این نوع دیتابیس امکان ذخیره سازی انواع داده های معمول مثل String, int, Map, List, DateTime را دارید.

اما ممکن است بخواهید یک کلاس مدل از داده های خود داشته باشید و سریعتر اطلاعات را ذخیره و بازیابی کنید. در این حالت باید از TypeAdapters استفاده کنید که کار دیکد و انکد کردن اشیای کلاس را انجام میدهد.

در مسیری که مایل هستید یک فایل جدید برای کلاس مدل ایجاد کنید, در اینجا اسم فایل را inventory_model قرار داده ایم.

در این فایل کلاس خودمون و با استفاده از annotations های مخصوص TypeAdapters  ایجاد میکنیم تا فایل های متناسب با کلاس ما به شکل خودکار ساخته شود.

import 'package:hive/hive.dart';

part 'inventory_model.g.dart';

@HiveType()
class Inventory {
  @HiveField(0)
  final String name;

  @HiveField(1)
  final String description;

  Inventory({this.name, this.description});
}

برای اینکه از کلاس خودتون بتونید در دیتابیس استفاده کنید باید قبل از اسم کلاس از برچسب یا انوتیشن @HiveType() استفاده کنید.

سپس هر فیلد از کلاس را که قصد دارید داخل دیتابیس قرار بگیرد با انوتیشن @HiveField(index) مشخص میکنید و یک ایندکس که عددی صحیح و منحصر به فرد است را برای آن قرار میدهید.

حالا برای ساخته شدن ابزارهای موردنیاز دستور زیر را در ترمینال وارد کنید.

$ flutter packages pub run build_runner build

در این قسمت شاید با خطایی مثل Missing concrete implementation of ‘getter TypeAdapter.typeId’ روبرو شوید.

برای حل این مشکل کافیست یک متد get برای فیلد typeId  بنویسید.

@override
int get typeId => 0;

بعد از رفع خطا زمان رجیستر کردن اداپتر در متد main فرا رسیده.

Hive.registerAdapter(InventoryAdapter());
runApp(MyApp());

ذخیره اطلاعات

برای ذخیره کردن اطلاعات یک متد برای اینکار در کلاس ChangeNotifier مینویسیم.

addItem(Inventory item) async {
    var box = await Hive.openBox<Inventory>('inventory');

    box.add(item);

    notifyListeners();
  }

در اینجا ابتدا همانطور که قبل تر گفتیم ابتدا یک box  را باز میکنیم و سپس از متد add استفاده میکنیم تا اطلاعات را به آن اضافه کنیم.

برای اینکار میتونید از متد put() هم استفاده کنید اما تفاوت این روش با متد قبلی استفاده از یک کلید برای ذخیره سازی است.

box.put(‘key’, item) در حالی که متد add فقط یک ایندکس به شکل خودکار برای هر سری از اطلاعات در نظر میگیرد.

برای ذخیره سازی همچنین میتونید از متد box.putAll({‘key’: value}) نیز استفاده کنید که یک مپ را به عنوان ورودی دریافت میکند و یا متد box.addAll( ) که پارامتر ورودی آن لیست است.

برای استفاده عملی از کد زیر میتوانید استفاده کنید.

inputItemDialog(BuildContext context, String action, [int index]) {
    var inventoryDb = Provider.of<HomeModel>(context, listen: false);

    showDialog(
      context: context,
      builder: (context) {
        return Dialog(
          child: Container(
            padding: EdgeInsets.only(
              left: 15,
              right: 15,
              top: 40,
            ),
            height: 45.height,
            child: SingleChildScrollView(
              child: Form(
                key: _formKey,
                child: Column(
                  children: <Widget>[
                    Text(
                      action == 'add' ? 'Add Item' : 'Update Item',
                      style: TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 20,
                      ),
                    ),
                    SizedBox(
                      height: 10,
                    ),
                    TextFormField(
                      controller: nameController,
                      validator: (value) {
                        if (value.isEmpty) {
                          return 'Item name cannot be empty';
                        }
                        return null;
                      },
                      decoration: InputDecoration(
                        labelText: 'Item name',
                      ),
                    ),
                    SizedBox(
                      height: 40,
                    ),
                    TextFormField(
                      controller: descriptionController,
                      validator: (value) {
                        if (value.isEmpty) {
                          return 'Item description cannot be empty';
                        }
                        return null;
                      },
                      decoration: InputDecoration(
                        labelText: 'Item description',
                      ),
                    ),
                    SizedBox(
                      height: 40,
                    ),
                    RaisedButton(
                      onPressed: () async {
                        if (_formKey.currentState.validate()) {
                          if (action == 'add') {
                            await inventoryDb.addItem(Inventory(
                              name: nameController.text,
                              description: descriptionController.text,
                            ));
                          } else {
                            
                          }

                          nameController.clear();
                          descriptionController.clear();

                          inventoryDb.getInventory();

                          Navigator.pop(context);
                        }
                      },
                      color: Colors.green[600],
                      child: Text(
                        action == 'add' ? 'Add' : 'update',
                        style: TextStyle(
                          fontSize: 16,
                          color: Colors.white,
                        ),
                      ),
                    ),
                    SizedBox(
                      height: 20,
                    )
                  ],
                ),
              ),
            ),
          ),
        );
      },
    );
  }

خواندن اطلاعات

سه روش مختلف برای خواندن اطلاعات ذخیره شده وجود دارد.

  • box.get(‘key): دریافت اطلاعات مربوط به یک کلید
  • box.getAt(index): دریافت اطلاعات مربوط به یک ایندکس
  • box.values: شامل تمام اطلاعات موجود در باکس

متد جدیدی به کلاسمون اضافه میکنیم برای خواندن اطلاعات به شکل زیر:

List _inventoryList = <Inventory>[];
List get inventoryList => _inventoryList;

getItem() async {
    final box = await Hive.openBox<Inventory>('inventory');

    _inventoryList = box.values.toList();

    notifyListeners();
 }

برای استفاده از این متد در رابط کاربری پروژمون کد زیر را در بالای متد build قرار دهید.

context.watch<HomeModel>().getItem();
ListView.builder(
     itemCount: model.inventoryList.length,
     itemBuilder: (context, index) {
       Inventory inv = model.inventoryList[index];
       return Container(
          margin: EdgeInsets.symmetric(
          vertical: 2.height,
          horizontal: 4.5.width,
         ),
          height: 13.height,
          decoration: BoxDecoration(
          color: Colors.white,
          ),
          child: Row(
            children: <Widget>[
              Container(
                margin: EdgeInsets.only(left: 2.width),
                width: 16.width,
                decoration: BoxDecoration(
                color: Colors.green[600],
                shape: BoxShape.circle,
                ),
                child: Center(
                  child: Icon(
                     Icons.home,
                     color: Colors.white,
                        ),
                      ),
                    ),
                    Padding(
                     padding: EdgeInsets.symmetric(
                     vertical: 3.height,
                     horizontal: 3.width,
                     ),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                         children: <Widget>[
                            Text(
                             inv.name,
                              style: TextStyle(
                               color: Colors.green[600],
                               fontWeight: FontWeight.w600,
                               fontSize: 4.5.text,
                                 ),
                               ),
                               SizedBox(
                                height: 1.height,
                               ),
                                  Text(
                                    inv.description,
                                    style: TextStyle(
                                      color: Colors.grey,
                                      fontSize: 4.text,
                                    ),
                                  ),
                                ],
                              ),
                            ),
                            Padding(
                              padding: EdgeInsets.only(left: 8.width),
                              child: PopupMenuButton(
                                onSelected: (item) {
                                  switch (item) {
                                    case 'update':
                                      nameController.text = inv.name;
                                      descriptionController.text =
                                          inv.description;

                                      inputItemDialog(context, 'update', index);
                                      break;
                                    case 'delete':
                                      model.deleteAt(index);
                                  }
                                },
                                itemBuilder: (context) {
                                  return [
                                    PopupMenuItem(
                                      value: 'update',
                                      child: Text('Update'),
                                    ),
                                    PopupMenuItem(
                                      value: 'delete',
                                      child: Text('Delete'),
                                    ),
                                  ];
                                },
                              ),
                            ),
                          ],
                        ),
                      );
                    },
                  )

آپدیت اطلاعات

در لیستی که داریم که با کلیک روی هر آیتم منوی پاپ آپی باز میشود که گزینه آپدیت دارد.

برای بروزرسانی مقادیر ذخیره شده از دو متد زیر میتونید کمک بگیرید.

  • box.put(‘key’, newValue)
  • box.putAt(index, item)

مثل دفعات قبلی متد مخصوص این کار را نیز به کلاس اضافه میکنیم.

updateItem(int index, Inventory inventory) {
    final box = Hive.box<Inventory>('inventory');

    box.putAt(index, inventory);

    notifyListeners();
  }

حذف آیتم

پاک کردن اطلاعات در Hive تقریبا مثل اضافه کردن اطلاعات می باشد.

  • box.delete(‘key)
  • box.deleteAt(index)
  • box.add()
  • box.deleteAll(keys)

همچنین یک متد deleteFromDisk هم وجود دارد که فایل box را پاک میکند.

deleteItem(int index) {
    final box = Hive.box<Inventory>(_inventoryBox);

    box.deleteAt(index);

    getInventory();

    notifyListeners();
  }

Lazy Boxes

اگر در پروژه موبایل خود نیاز به ذخیره حجم زیاد اطلاعات در دیتابیس دارید بهتر است که از lazyBox ها استفاده کنید. در این نوع از ذخیره سازی تمام اطلاعات همزمان از روی دیسک خوانده نمیشود.

final lazyBox = await Hive.openLazyBox('name');

var value = await lazyBox.get('key');

مابقی موارد در این نوع ذخیره سازی مشابه موارد گفته شده می باشد.

Hesam

View Comments

Recent Posts

گیتهاب اکشن چیست؟ آموزش استفاده از گیتهاب اکشن در برنامه نویسی فلاتر

گیتهاب اکشن GitHub Actions یکی از ابزارهای گیتهاب است که به شما کمک می‌کنه تا…

6 روز ago

آموزش افزایش سرعت اجرای وب اپلیکیشن های فلاتر

اگر یک برنامه نویس فلاتر هستید و با از نسخه وب اپلیکیشن پروژتون استفاده میکنید…

1 ماه ago

آموزش جامع انتشار اپلیکیشن اندروید و فلاتر در فروشگاه گوگل پلی Google play

به عنوان یک برنامه نویس فلاتر یا اندروید بعد از اتمام پروسه طراحی اپلیکیشن نیاز…

2 ماه ago

دانلود سورس کد رابط کاربری اپلیکیشن فلاتر پروژه پادکست

طراحی رابط کاربری اپلیکیشن پادکست خود را با استفاده از این کیت توسعه UI/UX فلاتر…

2 ماه ago

فایربیس چیست؟ معرفی سرویس ابری Firebase و کاربردهای آن

فایربیس، پلتفرمی قدرتمند از شرکت گوگل برای توسعه و مدیریت برنامه‌های موبایل و وب است.…

3 ماه ago

آموزش پیاده سازی Method Channel در فلاتر + فیلم

فلاتر یک فریم ورک برنامه نویسی چندسکویی است که این امکان را برای برنامه نویس…

3 ماه ago