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:
- How to integrate Firestore Database in Flutter?
- How to integrate Firebase Real-time Database in Flutter?
- Apple Sign-in With Flutter
- Native Ad Integration in Flutter(Using Kotlin and Swift)
- Phone Authentication Using Firebase
You can also find the most recent tutorials and examples on our my pages.