In this tutorial, we will develop a fluttering application that shows how to create Crud (created, read, update, delete). Hive For local data storage.
The hive is a lightweight, sharp key value database written in a net dart. Unlike SQ Elite, it does not need a heavy SQL engine. It stores data in boxes, which you can think of containers (like tables, but easy).
For such a small crude app, the hives are a great fit because:
This is offline first, and all data is stored locally on the device-internet.
This type is safe and is well connected with dart models (as our
Item,It is much faster than sqlite for easy tasks.
It has a friendly API (
hive_flutter) For things like reaction refreshments.
Hive is great for various use cases, such as storing app preferences/settings, small -to -medium -sized lists of structural data (such as notes, works, or purchase lists), offline catching for API response, and storing sessions or user profile data locally.
Here, hive items is strengthening a list like do/inventory, which means that everything (title, quantity) is stored locally and remains intact even after the app’s recovery.
By the end of this tutorial, you will have a fully active app that allows you to add, edit, delete items locally. I will provide clear explanations of the code along the way.
Table of contents:
Provisions
Before we start, make sure you have the following:
SDK installed (version 3.0 or more recommended).
Basic knowledge of the flurry: widgets, state/state lace widgets, and navigation.
A code editor such as a VS code or Android studio.
Familiar with dart classes, maps and inoms.
Step 1: Project Setup
Start by creating a new flurry project:
flutter create flutter_hive_crud
cd flutter_hive_crud
Open pubspec.yaml And add the following dependence:
dependencies:
hive: ^2.2.3
hive_flutter: ^1.1.0
fluttertoast: ^8.2.12
equatable: ^2.0.7
Install them:
flutter pub get
hive-Lightweight key value database for rash.hive_flutter– Restrictions for the hive.fluttertoast– Shows toast messages.equatable– Easy to equate value in dart items.
Step 2: Project folder structure
Organize your project like this:
lib/
├── main.dart
├── model/
│ └── item.dart
├── controller/
│ └── controller.dart
├── constants/
│ ├── string_constants.dart
│ └── enums/
│ ├── status.dart
│ └── yes_no.dart
└── screens/
├── main_screen.dart
└── widgets/
├── are_you_sure.dart
├── single_list_tile.dart
├── text_action.dart
└── toast.dart
This structure is modular and able to maintain the app.
Step 3: Application of application
We will pass through the file through the file, and I will explain what each piece does when we go.
1. main.dart
This is an entry point of application. It initiates the hive and launches the app.
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'screens/main_screen.dart';
import 'constants/string_constants.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Hive.initFlutter();
await Hive.openBox(StringConstants.hiveBox);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Hive CRUD',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.brown),
useMaterial3: true,
),
home: const MainScreen(),
);
}
}
What is happening in this code is here:
WidgetsFlutterBinding.ensureInitialized()This ensures that the width widgets are ready.Hive.initFlutter()Starts the hive in the clash.Hive.openBox(...)Opens a permanent storage box.MyAppMaterial theme and main screen sets.
2. item.dart (Model)
Since Hive Stores as a key value couple, we need to decide how to represent each item (such as entry or stock in the purchase list). We will keep your code organized, we will wrap each item in a dart class that will add to a dart class called Item. Thus, when we save the items in the hive, we can easily create, update, and change.
import 'package:equatable/equatable.dart';
class Item extends Equatable {
final String title;
final int quantity;
const Item({required this.title, required this.quantity});
@override
List<Object> get props => (title, quantity);
Map<String, dynamic> toMap() {
return {'title': title, 'quantity': quantity};
}
factory Item.fromMap(Map<String, dynamic> map) {
return Item(title: map('title'), quantity: map('quantity'));
}
}
So whenever we save or bring data, we are just changing between Item (Class example) and Map (The shape of the hive)
What is happening here:
3. controller.dart (A hive on the hive)
This controller handles all HIVE CRUD operations and UI updates.
import 'package:flutter/material.dart';
import 'package:flutter_hive_crud/constants/string_constants.dart';
import 'package:flutter_hive_crud/screens/widgets/toast.dart';
import 'package:hive_flutter/hive_flutter.dart';
import '../constants/enums/status.dart';
import '../model/item.dart';
class HiveController {
final BuildContext context;
final Function fetchDataFunction;
HiveController({required this.context, required this.fetchDataFunction});
final hiveBox = Hive.box(StringConstants.hiveBox);
List<Map<String, dynamic>> fetchData() {
return hiveBox.keys.map((key) {
final item = hiveBox.get(key);
return {
'key': key,
'title': item('title'),
'quantity': item('quantity'),
};
}).toList().reversed.toList();
}
Future<void> createItem({required Item item}) async {
try {
await hiveBox.add(item.toMap());
afterAction('saved');
} catch (e) {
toastInfo(msg: 'Failed to create item', status: Status.error);
}
}
Future<void> editItem({required Item item, required int itemKey}) async {
try {
hiveBox.put(itemKey, item.toMap());
afterAction('edited');
} catch (e) {
toastInfo(msg: 'Failed to edit item', status: Status.error);
}
}
Future<void> deleteItem({required int key}) async {
try {
await hiveBox.delete(key);
afterAction('deleted');
} catch (e) {
toastInfo(msg: 'Failed to delete item', status: Status.error);
}
}
Future<void> clearItems() async {
try {
await hiveBox.clear();
afterAction('cleared');
} catch (e) {
toastInfo(msg: 'Failed to clear items', status: Status.error);
}
}
void afterAction(String keyword) {
toastInfo(msg: 'Item $keyword successfully', status: Status.success);
fetchDataFunction();
Navigator.of(context).pop();
}
}
Let’s break it HiveController Through the code block, explaining every section and explaining why it is necessary.
Imports:
import 'package:flutter/material.dart';
import 'package:flutter_hive_crud/constants/string_constants.dart';
import 'package:flutter_hive_crud/screens/widgets/toast.dart';
import 'package:hive_flutter/hive_flutter.dart';
import '../constants/enums/status.dart';
import '../model/item.dart';
What is happening here:
flutter/material.dart– Provides material design widgets and utility.string_constants.dart-AP contains a wide permanent name, for example the name of the hive box.toast.dart– The usefulness of displaying toast messages for success or error feedback.hive_flutter.dart– Integration of the package with a flurry.status.dart– Types of status while representing Inum (errorOrsuccess) For toast messages.item.dart– Model class that represents an individual (title + quantity).
These imports allow the controller to manage HIVE Data and interact with the UI.
Class Definition and Builder:
class HiveController {
final BuildContext context;
final Function fetchDataFunction;
HiveController({required this.context, required this.fetchDataFunction});
What is happening here:
HiveController– This class manages all crore operations for the hive.context– to flutter the currentBuildContextModels or dialogues are used to navigate and show.fetchDataFunction– A function approved by the UI that refreshes the list after performing any hive operation.
Constructor requires both parameters, ensuring that every instance HiveController UI has access to context and is a way to refresh data.
Hive Box Reference:
final hiveBox = Hive.box(StringConstants.hiveBox);
Here,
hiveBoxThis is a reference to the hive box described inStringConstants.hiveBox.A sixth box is like a key price store where we save our items locally.
This allows the controller to communicate with the hive every time the box is reopened.
Bring data:
List<Map<String, dynamic>> fetchData() {
return hiveBox.keys.map((key) {
final item = hiveBox.get(key);
return {
'key': key,
'title': item('title'),
'quantity': item('quantity'),
};
}).toList().reversed.toList();
}
What is this code doing: Here’s:
hiveBox.keys– Recover all the keys stored in the houses..map((key) => ...)– Repeat through each key and brings the item associated with it.Each item transforms into a map that contains:
.toList().reversed.toList()– turns maps to a list and changes it The latest items appear before.
This method returns a list of items ready for display in UI.
Construction of an item:
Future<void> createItem({required Item item}) async {
try {
await hiveBox.add(item.toMap());
afterAction('saved');
} catch (e) {
toastInfo(msg: 'Failed to create item', status: Status.error);
}
}
In this code,
item.toMap()– changesItemObject to a map so that the hives can store it.hiveBox.add(...)– Adds a new entry to the housewife, which automatically produces a unique key.afterAction('saved')– Shows the toast of success, refreshes the UI, and closes any open modal.catchThe block handles mistakes and if something goes wrong, he shows the toast.
To edit an item:
Future<void> editItem({required Item item, required int itemKey}) async {
try {
hiveBox.put(itemKey, item.toMap());
afterAction('edited');
} catch (e) {
toastInfo(msg: 'Failed to edit item', status: Status.error);
}
}
In this code,
hiveBox.put(itemKey, item.toMap())– Updates the item on a specified key with new data.afterAction('edited')– Hands over feedback and UI updates.catchThe block handles any mistakes during the modification process.
Delete an item:
Future<void> deleteItem({required int key}) async {
try {
await hiveBox.delete(key);
afterAction('deleted');
} catch (e) {
toastInfo(msg: 'Failed to delete item', status: Status.error);
}
}
Here,
hiveBox.delete(key)– Removes the item associated with a specific key from the hive.Calls
afterAction('deleted')To refresh the UI and show a message of success.Mistakes handle with toast.
Cleaning all items:
Future<void> clearItems() async {
try {
await hiveBox.clear();
afterAction('cleared');
} catch (e) {
toastInfo(msg: 'Failed to clear items', status: Status.error);
}
}
What is happening here:
hiveBox.clear()– delete All the items In the hive boxUseful for “clean” functionality in the app.
Success and mistakes are handled in the same way as other actions.
Wizard after action:
void afterAction(String keyword) {
toastInfo(msg: 'Item $keyword successfully', status: Status.success);
fetchDataFunction();
Navigator.of(context).pop();
}
What is happening here:
toastInfo(...)– For example, success shows the toast, “The item has been successfully saved.”fetchDataFunction()– Call UI -approved function to reload the list.Navigator.of(context).pop()– An open modal or dialog (such as item form).
This method avoids repetition, centralizing the logic after any CRUD operation.
Summary of Hivecontroller’s Officials:
Bring items from Hive for UI display.
Create, update, delete and clean.
Provide user feedback through toast messages.
After any data changes, refresh the UI automatically.
Administrate models and dialogues with context.
HiveControllerCleaner UI code summarized ceiling operations.Methods:
createItemFor, for, for,.editItemFor, for, for,.deleteItemFor, for, for,.clearItems.afterActionUpdates UI and shows success messages.
4. string_constants.dart
This string is a central storage for permanent names, such as the name of the hive box.
class StringConstants {
static const hiveBox = 'items';
}
In this code:
class StringConstants– Defines a class that is used only to group permanent values together.static– This means you don’t have to create an exampleStringConstantsTo use it. You can directly access itStringConstants.hiveBox.const-It makes time permanent, so it cannot be edited anywhere in your code.'items'– It’s just a wire price. In this case, it is the name of the hugging box that you will open.
5. status.dart (Enum)
enum Status { error, success }
In this code:
enum– For the short Counting. This is a special type that allows you to explain a fixed set of designated values.Status– Name of Anum.{ error, success }– Possible values that it can take.
So Status Now there is only one custom type Two correct values:
Status.error
Status.success
Why use it here?
Instead of passing through the sidewalks of plain wires "error" Or "success" (Which are easy to spell in invalid), can use the code Status.error Or Status.success.
For example, when showing toasts:
toastInfo(msg: 'Item deleted', status: Status.success);
Or ::
toastInfo(msg: 'Failed to delete item', status: Status.error);
This makes the code more secure (you can’t go through the error "sucess" And break things), clear (you can see the intention immediately), and ease to maintain (if you add more status later. warning Or infoIt’s just one place to update).
6. yes_no.dart (Enum)
enum YesNo { yes, no }
Why use it?
Instead of passing through the bolsters (true ).). false) Or strings ("yes" ).). "no"), You can use this anoma to make your intention more clear in the code.
For example:
YesNo userAccepted = YesNo.yes;
if (userAccepted == YesNo.yes) {
print("User agreed!");
} else {
print("User declined!");
}
It is more described by using a field bool Where you have to guess true Or false Meaning in context.
Common use cases:
Confirmation (for example, “Do you want to save this file?”,
Settings Toggle (for example, “Enable notifications?”,
API answers that return
"yes").)."no"You can make them a map of this inom for a safe handling as a wire.
7. toast.dart
import 'package:fluttertoast/fluttertoast.dart';
import '../../../constants/enums/status.dart';
void toastInfo({required String msg, required Status status}) {
Fluttertoast.showToast(
msg: msg,
backgroundColor: status == Status.error ? Colors.red : Colors.green,
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.TOP,
);
}
This is a helpful function to display toast messages in your fluttering app.
A Toast There is a small, temporary popup message (usually below the screen or top) to inform the user about something immediately. For example, you may have “Item successfully saved” Or “Error to delete the item”.
8. are_you_sure.dart (Confirmation dialog)
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
Future<void> areYouSureDialog({
required String title,
required String content,
required BuildContext context,
required Function action,
bool isKeyInvolved = false,
int key = 0,
}) {
return showDialog(
context: context,
builder: (context) => Platform.isIOS
? CupertinoAlertDialog(
title: Text(title),
content: Text(content),
actions: (
CupertinoDialogAction(
onPressed: () =>
isKeyInvolved ? action(key: key) : action(),
child: const Text('Yes')),
CupertinoDialogAction(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Dismiss')),
),
)
: AlertDialog(
title: Text(title),
content: Text(content),
actions: (
ElevatedButton(
onPressed: () =>
isKeyInvolved ? action(key: key) : action(),
child: const Text('Yes')),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Dismiss')),
),
),
);
}
This code handles the user’s confirmation for actions such as deletion or clear.
This function shows the verified dialog from the platform (Are you sure?) They:
works with one on iOS
CupertinoAlertDialog.Works on Android/others with a content
AlertDialog.A provided calls provided
actionWhen the user presses yes.When the user is suppressed, the dialogue closes.
Optionally passes a
keyIn the action function.
9. single_list_tile.dart (List Item Waget)
import 'package:flutter/material.dart';
import '../../model/item.dart';
import 'text_action.dart';
import '../../constants/enums/yes_no.dart';
class SingleListItem extends StatelessWidget {
final Item item;
final int itemKey;
final Function editHandle;
final Function deleteDialog;
final Function deleteItem;
const SingleListItem({
super.key,
required this.item,
required this.itemKey,
required this.editHandle,
required this.deleteDialog,
required this.deleteItem,
});
@override
Widget build(BuildContext context) {
return Dismissible(
key: ValueKey(itemKey),
confirmDismiss: (_) => showDialog(
context: context,
builder: (_) => AlertDialog(
title: const Text('Are you sure?'),
content: Text('Delete ${item.title}?'),
actions: (
textAction('Yes', YesNo.yes, context),
textAction('No', YesNo.no, context),
),
),
),
onDismissed: (_) => deleteItem(key: itemKey),
background: Container(
color: Colors.red,
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20),
child: const Icon(Icons.delete, color: Colors.white),
),
child: ListTile(
title: Text(item.title),
subtitle: Text(item.quantity.toString()),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: (
IconButton(onPressed: () => editHandle(item: item, key: itemKey), icon: const Icon(Icons.edit)),
IconButton(onPressed: () => deleteDialog(key: itemKey), icon: const Icon(Icons.delete)),
),
),
),
);
}
}
This code represents each list item that has edited/deleting options and swipe -to -delete functionality.
It represents the widget An item (Like a shopping list, Todo List, Inventory app, and a row like that):
Swipe -to -delete functionality.
Edit and delete the buttons.
A confirmation dialogue before dismissal
10. main_screen.dart (UI + State Management)
This is the main screen that puts everything together, including forms, lists and models.
Due to the length, the entire explanation is already well developed in the original code, which is covered:
itemModal()– Sheet form below for creation/editing.fetchData()– Load the items from the hive.editHandle()– Load the item for editing.deleteDialog()– Confirm deletion.clearAllDialog()– Confirm all items cleaned.
Screenshots







Conclusion
Now you have a fully active fluttering app with Hive for local data perseverance. May your app:
Create, read, update, delete.
Show toast messages for feedback.
Confirm actions with the Android and iOS dialogue.
Clean, modular architecture with controllers, models, and widgets.
You can find the hive more Hive package documents If you want to get more information.