How to integrate Hive Database in Flutter?

Introduction

Hive is a compact key-value database that may be used to store data locally in mobile, desktop, and web applications. It is entirely Dart-based and functions flawlessly with Flutter. CRUD stands for create, read, update, and delete, the four fundamental activities of persistent storage.

In this article, we’ll utilise Hive and Flutter to create a feature-rich and surprisingly practical app.

Overview

Hive has a lot of amazing benefits, including:

  • Simple to use, similar to Dart maps. Data may be accessed without the need for lengthy, intricate searches.
  • high adaptability Due to the fact that Hive doesn’t employ tables like SQLite does, you can easily change your data structure.
  • Devoid of native dependencies
  • swift and capable of handling a lot of data.
  • supports Dart objects and primitives (string, number, list, map, etc) (with the help of adapters)

You must set up both the hive and hive flutter plugins in order to use Hive with Flutter.
In your app’s main() function, you can initialise Hive:

void main() async { 
 await Hive.initFlutter(); 
}

Hive organises all of its data into boxes. In SQL, a box is comparable to a table, however unlike a table, a box can hold anything. This is how a box can be opened:

await Hive.openBox('shop_box');

When a box is opened, all of its data is loaded into memory for quick access from the local storage. Data retrieval is possible synchronously without the use of async/await:

final myBox = Hive.box('shop_box');
final something = myBox.get('my_key');

Filling the box with a new item:

await myBox.put('some_key', someValue);

Changing an existing box item:

await myBox.put('some_key', someValue);

The removal of a box item:

await myBox.delete('some_key');

Utilising auto-increment keys to store a list of objects

Hive’s add() method makes it simple to save a list of items. The keys in this instance start at 0 and automatically increase by 1, 2, 3, and so forth. For instance:

final listBox = Hive.box('my_list');
int newKey = await listBox.add(someData); 

All keys and values can be retrieved from the box in the following way:

final keys = listBox.keys;
final values = listBox.values; 

Keys and values can be used in loops as needed. In situations when you wish to generate and set your own keys (by utilising the put() function as previously indicated), using DateTime.now().toString() is a good idea.

The Code

Running: Installs hive and hive flutter.

dart pub add hive
flutter pub add hive_flutter

The entire source code is in main.dart and includes thorough explanations:

import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Hive.initFlutter();
  // await Hive.deleteBoxFromDisk('shop_box');
  await Hive.openBox('shop_box');

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'KindaCode.com',
      theme: ThemeData(
        primarySwatch: Colors.green,
      ),
      home: const HomePage(),
    );
  }
}

// Home Page
class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  List<Map<String, dynamic>> _items = [];

  final _shopBox = Hive.box('shop_box');

  @override
  void initState() {
    super.initState();
    _refreshItems(); // Load data when app starts
  }

  // Get all items from the database
  void _refreshItems() {
    final data = _shopBox.keys.map((key) {
      final value = _shopBox.get(key);
      return {"key": key, "name": value["name"], "quantity": value['quantity']};
    }).toList();

    setState(() {
      _items = data.reversed.toList();
      // we use "reversed" to sort items in order from the latest to the oldest
    });
  }

  // Create new item
  Future<void> _createItem(Map<String, dynamic> newItem) async {
    await _shopBox.add(newItem);
    _refreshItems(); // update the UI
  }

  // Retrieve a single item from the database by using its key
  // Our app won't use this function but I put it here for your reference
  Map<String, dynamic> _readItem(int key) {
    final item = _shopBox.get(key);
    return item;
  }

  // Update a single item
  Future<void> _updateItem(int itemKey, Map<String, dynamic> item) async {
    await _shopBox.put(itemKey, item);
    _refreshItems(); // Update the UI
  }

  // Delete a single item
  Future<void> _deleteItem(int itemKey) async {
    await _shopBox.delete(itemKey);
    _refreshItems(); // update the UI

    // Display a snackbar
    ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('An item has been deleted')));
  }

  // TextFields' controllers
  final TextEditingController _nameController = TextEditingController();
  final TextEditingController _quantityController = TextEditingController();

  // This function will be triggered when the floating button is pressed
  // It will also be triggered when you want to update an item
  void _showForm(BuildContext ctx, int? itemKey) async {
    // itemKey == null -> create new item
    // itemKey != null -> update an existing item

    if (itemKey != null) {
      final existingItem =
          _items.firstWhere((element) => element['key'] == itemKey);
      _nameController.text = existingItem['name'];
      _quantityController.text = existingItem['quantity'];
    }

    showModalBottomSheet(
        context: ctx,
        elevation: 5,
        isScrollControlled: true,
        builder: (_) => Container(
              padding: EdgeInsets.only(
                  bottom: MediaQuery.of(ctx).viewInsets.bottom,
                  top: 15,
                  left: 15,
                  right: 15),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.end,
                children: [
                  TextField(
                    controller: _nameController,
                    decoration: const InputDecoration(hintText: 'Name'),
                  ),
                  const SizedBox(
                    height: 10,
                  ),
                  TextField(
                    controller: _quantityController,
                    keyboardType: TextInputType.number,
                    decoration: const InputDecoration(hintText: 'Quantity'),
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  ElevatedButton(
                    onPressed: () async {
                      // Save new item
                      if (itemKey == null) {
                        _createItem({
                          "name": _nameController.text,
                          "quantity": _quantityController.text
                        });
                      }

                      // update an existing item
                      if (itemKey != null) {
                        _updateItem(itemKey, {
                          'name': _nameController.text.trim(),
                          'quantity': _quantityController.text.trim()
                        });
                      }

                      // Clear the text fields
                      _nameController.text = '';
                      _quantityController.text = '';

                      Navigator.of(context).pop(); // Close the bottom sheet
                    },
                    child: Text(itemKey == null ? 'Create New' : 'Update'),
                  ),
                  const SizedBox(
                    height: 15,
                  )
                ],
              ),
            ));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('KindaCode.com'),
      ),
      body: _items.isEmpty
          ? const Center(
              child: Text(
                'No Data',
                style: TextStyle(fontSize: 30),
              ),
            )
          : ListView.builder(
              // the list of items
              itemCount: _items.length,
              itemBuilder: (_, index) {
                final currentItem = _items[index];
                return Card(
                  color: Colors.orange.shade100,
                  margin: const EdgeInsets.all(10),
                  elevation: 3,
                  child: ListTile(
                      title: Text(currentItem['name']),
                      subtitle: Text(currentItem['quantity'].toString()),
                      trailing: Row(
                        mainAxisSize: MainAxisSize.min,
                        children: [
                          // Edit button
                          IconButton(
                              icon: const Icon(Icons.edit),
                              onPressed: () =>
                                  _showForm(context, currentItem['key'])),
                          // Delete button
                          IconButton(
                            icon: const Icon(Icons.delete),
                            onPressed: () => _deleteItem(currentItem['key']),
                          ),
                        ],
                      )),
                );
              }),
      // Add new item button
      floatingActionButton: FloatingActionButton(
        onPressed: () => _showForm(context, null),
        child: const Icon(Icons.add),
      ),
    );
  }
}

Conclusion

The basics of the Hive database were taught to you in Flutter. You’ve also looked at a compact yet feature-rich app that uses Hive to keep data offline. Take a look at the following articles if you want to learn more about persistent data and other cool features of Flutter:

Submit a Comment

Your email address will not be published. Required fields are marked *

Subscribe

Select Categories