Introduction to Hive in Flutter and CRUD Operation

Table of contents
- π° Step 1: Introduction to Hive in Flutter
- π Explain main.dart Code
- π· Your Root Widget β MyApp
- β Summary of Step 1: What Did We Do?
- π Step 2: Creating Your Hive Model (NoteModel)
- π Generate the TypeAdapter
- β Summary
- π§ Step 3: HiveNoteController β Managing Notes with GetX and Hive
- π§ Summary Table
- NoteHomeView β Display and Manage Notes
- β Summary Table
- π§Ύ AddNoteForm β Add a New Note
- β Summary Table
- π οΈ EditNote β Editing an Existing Note
- β Summary Table
- π¦ Project Overview
- π Folder Structure
- 1οΈβ£ note_model.dart
- 2οΈβ£ note_controller.dart
- 3οΈβ£ note_home_view.dart
- 4οΈβ£ add_note_form.dart
- 5οΈβ£ edit_note_view.dart
- 6οΈβ£ main.dart
- β Done! Final Checklist
π° Step 1: Introduction to Hive in Flutter
β What is Hive?
Hive is a lightweight, fast, and NoSQL database written in pure Dart. It's great for local data storage in Flutter apps and works offline without the need for an internet connection.
β Why use Hive?
No native dependencies (works on Android, iOS, Web, Desktop)
Fast and lightweight
Supports TypeAdapters to store custom objects like
NoteModel
Works with
ValueListenableBuilder
for real-time UI updates
π Explain main.dart
Code
π· Setup and Initialization
void main() async {
WidgetsFlutterBinding.ensureInitialized();
β Explanation:
Ensures all bindings are initialized before any async operations, especially before using Hive or platform channels.
π· Setup Local Path for Hive
final dir = await getApplicationDocumentsDirectory();
Hive.init(dir.path);
β Explanation:
Gets the application's document directory (e.g.,
/data/user/0/
your.app/files
) usingpath_provider
.Initializes Hive to store data in that directory.
π§ Note: This is only needed for Android/iOS/Desktop. If you're on Flutter Web, use Hive.initFlutter()
instead.
π· Register Adapter
Hive.registerAdapter(NoteModelAdapter());
β Explanation:
Hive doesn't understand your custom class NoteModel
by default. You must register a TypeAdapter so Hive knows how to save/load that object.
This adapter is generated using:
flutter pub run build_runner build
π· Open a Hive Box
await Hive.openBox<NoteModel>('notes');
β Explanation:
A Box in Hive is like a table or a local storage container.
You opened a box named
'notes'
that storesNoteModel
items.All your app data (notes) will be stored in this box.
π· Your Root Widget β MyApp
dartCopyEditclass MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: NoteHomeView(),
);
}
}
β Explanation:
Defines the base Material app.
Sets a global theme (colors, fonts, etc.).
Sets
NoteHomeView()
as the home screen of the app.
β Summary of Step 1: What Did We Do?
Step | What It Does |
Import packages | Adds necessary libraries to use Hive and Flutter |
main() function | Initializes Hive and opens the storage box |
registerAdapter() | Registers how to save/load your custom data class |
openBox() | Opens the notes storage container |
runApp() | Starts your Flutter UI |
π Step 2: Creating Your Hive Model (NoteModel
)
This model class allows Hive to store custom objects (notes with title, description, and ID) inside a Hive Box.
β Your Code:
import 'package:hive/hive.dart';
part 'note_model.g.dart'; // This will be generated automatically
@HiveType(typeId: 0)
class NoteModel extends HiveObject {
@HiveField(0)
final int? id;
@HiveField(1)
final String title;
@HiveField(2)
final String description;
NoteModel(this.id, this.title, this.description);
}
π§ What Does This Mean?
β
import 'package:hive/hive.dart';
Imports Hiveβs annotations and base classes.
β
part 'note_model.g.dart';
Tells Dart that this file will be connected to a generated file. You donβt write this file β itβs created automatically by build_runner
.
β
@HiveType(typeId: 0)
Marks the class as a Hive-compatible object. Every class must have a unique typeId
.
β
@HiveField(index)
Each property you want to save in Hive must be annotated with @HiveField
. The index (0, 1, 2) must be unique and never change, or your app could crash.
β
extends HiveObject
This gives you helper methods like save()
, delete()
, and key
.
π Generate the TypeAdapter
Hive canβt save NoteModel
unless you create the adapter for it.
β Follow these steps:
1. Add dependencies in pubspec.yaml
:
yamlCopyEditdependencies:
hive: ^2.2.3
hive_flutter: ^1.1.0
path_provider: ^2.1.1
dev_dependencies:
hive_generator: ^2.0.1
build_runner: ^2.4.6
Then run:
flutter pub get
2. Run build_runner to generate the adapter:
Run this command in the terminal:
flutter pub run build_runner build
This will create the file:
note_model.g.dart
β It contains the automatically generated adapter code.
π Result: note_model.g.dart
Hive now understands how to convert your NoteModel
class to/from binary data and store it in a box.
π¦ Bonus: What the Adapter Does (internally)
The adapter will look something like this (generated for you):
dartCopyEditclass NoteModelAdapter extends TypeAdapter<NoteModel> {
@override
final int typeId = 0;
@override
NoteModel read(BinaryReader reader) {
return NoteModel(
reader.read() as int?,
reader.read() as String,
reader.read() as String,
);
}
@override
void write(BinaryWriter writer, NoteModel obj) {
writer.write(obj.id);
writer.write(obj.title);
writer.write(obj.description);
}
}
β Summary
Task | Why It's Important |
Annotate model with HiveType | Tells Hive how to store your custom class |
Add @HiveField s | Maps object fields to Hive binary storage |
Generate adapter | Allows Hive to read/write NoteModel objects |
Use HiveObject | Provides handy functions like .save() |
β You are now ready to store and retrieve notes using Hive!
π§ Step 3: HiveNoteController
β Managing Notes with GetX and Hive
This class acts as the controller between your UI and data (Hive box). It follows the GetX architecture, which keeps your logic separate from your UI.
π§ Main Class
class HiveNoteController extends GetxController {
HiveNoteController
: A GetX controller.extends GetxController
: Gives lifecycle methods (onInit
,onClose
) and state management features.
β Opening the Hive Box
final Box<NoteModel> noteBox = Hive.box('notes');
You open the
notes
box that was initialized in yourmain()
method.Type
<NoteModel>
ensures Hive knows this box contains onlyNoteModel
objects.
β Adding a Note
void addNote(NoteModel note) {
noteBox.add(note);
}
Adds a new note to the Hive box.
It automatically gets a unique key.
β Deleting a Note
void deleteNote(int index) {
noteBox.deleteAt(index);
}
Deletes the note at the given index.
deleteAt()
is used when you work with indexes in Hive boxes.
β Updating a Note
void updateNote(int index, NoteModel updatedNote) {
noteBox.putAt(index, updatedNote);
}
- Replaces the note at a specific index with new values.
β Reading a Note
NoteModel getNote(int index) {
return noteBox.getAt(index)!;
}
Retrieves a note at a specific index.
The
!
tells Dart that the result won't be null.
β Getting Count of Notes
int get noteCount => noteBox.length;
- Returns the total number of notes in the box.
π§ Summary Table
Method | What It Does |
addNote() | Adds a new note to the box |
deleteNote() | Deletes a note at a given index |
updateNote() | Updates a note at a given index |
getNote() | Fetches a note from a specific index |
noteCount | Gets total count of notes |
noteBox | Refers to the Hive box where notes are stored |
NoteHomeView
β Display and Manage Notes
This view displays all saved notes from Hive using ValueListenableBuilder
, and integrates GetX for navigation and controller usage.
β Controller Initialization
final HiveNoteController controller = Get.put(HiveNoteController());
Injects the
HiveNoteController
using GetX.Get.put()
ensures itβs available to the widget tree.
β 3. Scaffold Layout
return Scaffold(
appBar: AppBar(...),
body: ValueListenableBuilder(...),
floatingActionButton: ...
);
This creates a full-screen app structure with:
App bar
List of notes (in
body
)Add-note FAB button
β
Displaying Notes β ValueListenableBuilder
body: ValueListenableBuilder<Box<NoteModel>>(
valueListenable: controller.noteBox.listenable(),
builder: (context, box, _) {
final notes = box.values.toList();
ValueListenableBuilder
rebuilds UI whenever Hive box changes (add/delete/update).noteBox.listenable()
makes the UI reactive.
β Empty State
if (notes.isEmpty) {
return const Center(
child: Text("No notes available.\nTap + to add one!"),
);
}
- Shows a friendly message when there are no notes.
β 7. ListView Builder β Showing All Notes
dartCopyEditreturn ListView.builder(
itemCount: notes.length,
itemBuilder: (context, index) {
final note = notes[index];
Iterates over all notes
For each note, shows a custom
ListTile
inside a styled container
β 8. Note Card UI
Each note looks like a card with title, description, and icons:
Container(
decoration: BoxDecoration(...),
child: ListTile(
title: Text(note.title),
subtitle: Text(note.description),
leading: Icon(Icons.sticky_note_2_outlined),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [IconButton(edit), IconButton(delete)],
),
),
)
β 9. Edit & Delete Actions
π Edit Note
onPressed: () {
Get.to(() => EditNote(index: index, note: note));
}
Navigates to the edit screen
Passes the selected note and index
π Delete Note
onPressed: () {
controller.deleteNote(index);
Get.snackbar("Deleted", "${note.title} has been deleted");
}
Calls
deleteNote()
from the controllerShows a toast using
Get.snackbar
β 10. Add Note Button (FAB)
floatingActionButton: FloatingActionButton(
onPressed: () => Get.to(() => AddNoteForm()),
child: const Icon(Icons.add),
)
- On press, navigates to the add-note form using
Get.to
()
β Summary Table
Feature | Purpose |
ValueListenableBuilder | Listens for real-time changes in Hive data |
controller.noteBox.listenable() | Makes Hive reactive without manual refresh |
Get.put() | Injects the controller using GetX |
Get.to () | Navigates to another screen |
FloatingActionButton | Adds new note |
IconButton (edit/delete) | Updates or removes a note |
Get.snackbar() | Displays toast message |
π§Ύ AddNoteForm
β Add a New Note
This screen contains:
Text input fields for title and description
A save button to store the note in Hive
GetX for controller access and navigation
β Controller and Text Fields
dartCopyEditfinal controller = Get.find<HiveNoteController>();
final titleController = TextEditingController();
final descriptionController = TextEditingController();
Get.find()
finds the already injectedHiveNoteController
(fromGet.put()
earlier)TextEditingController
is used to get and manage user input from the text fields
β Note Input Fields
TextField(
controller: titleController,
decoration: const InputDecoration(labelText: "Title"),
),
TextField(
controller: descriptionController,
decoration: const InputDecoration(labelText: "Description"),
maxLines: 4,
),
Users enter note title and description
controller:
links the text input with yourTextEditingController
maxLines: 4
allows multiline input for the description
β Save Note Button
dartCopyEditElevatedButton(
onPressed: () {
if (titleController.text.isNotEmpty &&
descriptionController.text.isNotEmpty) {
- Checks if both fields are not empty before proceeding
β Creating and Adding the Note
final newNote = NoteModel(
DateTime.now().millisecondsSinceEpoch,
titleController.text.trim(),
descriptionController.text.trim(),
);
controller.addNote(newNote);
Creates a
NoteModel
object with:A unique ID based on current timestamp
Title and description text
Calls
addNote()
from the controller to store it in Hive
β Feedback & Navigation
dartCopyEditGet.back();
Get.snackbar("Success", "Note added successfully");
Navigates back to the home screen using
Get.back()
Shows a toast message using
Get.snackbar
If the fields are empty:
dartCopyEditGet.snackbar("Error", "Both fields are required");
β Summary Table
Element | Role |
TextEditingController | Grabs input from user |
Get.find() | Fetches the controller already in memory |
controller.addNote() | Adds a new note to Hive |
NoteModel | Stores title, description, and ID |
Get.back() | Navigates back to previous screen |
Get.snackbar() | Displays feedback toast |
β UX Considerations
π‘ You validate the inputs before saving β
π§ You give real-time user feedback via
Get.snackbar()
βπ Fast navigation using GetX β
π οΈ EditNote
β Editing an Existing Note
This screen:
Loads the existing note into input fields
Lets the user modify and update it
Uses
GetX
to update the note in HiveUses
Get.snackbar
to show feedback
β β Constructor
final int index;
final NoteModel note;
final controller = Get.find<NoteController>();
EditNote({super.key, required this.index, required this.note});
index
: The position of the note in the Hive boxnote
: The actualNoteModel
object passed from the home screencontroller
: Gets the existing controller instance using GetX (Get.find()
)
β Initializing Text Controllers
final titleController = TextEditingController(text: note.title);
final descriptionController = TextEditingController(text: note.description);
Initializes the text fields with the current title and description of the note
So the user can see and edit the existing content
β 4. Scaffold + Form UI
return Scaffold(
appBar: AppBar(
title: const Text("Edit Note"),
backgroundColor: Colors.deepPurple,
),
Standard screen structure
Purple AppBar titled "Edit Note"
β Form Fields
TextField(
controller: titleController,
decoration: const InputDecoration(labelText: "Title"),
),
TextField(
controller: descriptionController,
decoration: const InputDecoration(labelText: "Description"),
maxLines: 4,
),
Two fields pre-filled with the existing note content
maxLines: 4
allows a longer description
β Update Button Logic
ElevatedButton(
onPressed: () {
if (titleController.text.isNotEmpty &&
descriptionController.text.isNotEmpty) {
- Checks that neither input is empty before updating
β Update NoteModel and Save
final updatedNote = NoteModel(
note.id, // keep original ID
titleController.text.trim(),
descriptionController.text.trim(),
);
controller.updateNote(index, updatedNote);
Creates a new
NoteModel
with updated contentUses
controller.updateNote(index, updatedNote)
to replace the old one in Hive
π§ Important: Keeping the original
note.id
ensures consistency in your data.
β Navigation and Snackbar
dartCopyEditGet.back();
Get.snackbar("Updated", "Note updated successfully");
Get.back()
: Returns to the previous screenGet.snackbar
: Shows a confirmation message
β Error Handling
dartCopyEditelse {
Get.snackbar("Error", "Both fields are required");
}
- If either field is empty, a warning is shown
β Summary Table
Feature | Purpose |
Get.find<NoteController>() | Access note controller from memory |
TextEditingController(text: ...) | Pre-fill input with current note values |
updateNote(index, note) | Updates the Hive note at the given index |
Get.back() | Goes back to the note list |
Get.snackbar() | Displays status messages |
πͺ Final Thoughts
This screen mirrors the
AddNoteForm
, but with pre-filled data and update logic.It's good UX to keep the same layout and just change the logic under the hood.
Here's a complete and beginner-friendly tutorial for your Hive + GetX-based Notes App in Flutter, including all source code files, clear structure, and final remarks.
π¦ Project Overview
β Tech Stack:
Flutter
Hive (local DB)
GetX (State management & navigation)
π Folder Structure
cssCopyEditlib/
βββ NoteApp/
β βββ model/
β β βββ note_model.dart
β βββ controllers/
β β βββ note_controller.dart
β βββ views/
β β βββ add_note_form.dart
β β βββ edit_note_view.dart
β β βββ note_home_view.dart
βββ main.dart
1οΈβ£ note_model.dart
dartCopyEdit// lib/NoteApp/model/note_model.dart
import 'package:hive/hive.dart';
part 'note_model.g.dart';
@HiveType(typeId: 0)
class NoteModel extends HiveObject {
@HiveField(0)
final int? id;
@HiveField(1)
final String title;
@HiveField(2)
final String description;
NoteModel(this.id, this.title, this.description);
}
π Run build command to generate the adapter:
bashCopyEditflutter packages pub run build_runner build
2οΈβ£ note_controller.dart
dartCopyEdit// lib/NoteApp/controllers/note_controller.dart
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import '../model/note_model.dart';
class NoteController extends GetxController {
final Box<NoteModel> noteBox = Hive.box<NoteModel>('notes');
void addNote(NoteModel note) {
noteBox.add(note);
}
void deleteNote(int index) {
noteBox.deleteAt(index);
}
void updateNote(int index, NoteModel updatedNote) {
noteBox.putAt(index, updatedNote);
}
NoteModel getNote(int index) {
return noteBox.getAt(index)!;
}
int get noteCount => noteBox.length;
}
3οΈβ£ note_home_view.dart
dartCopyEdit// lib/NoteApp/views/note_home_view.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hive_flutter/hive_flutter.dart';
import '../controllers/note_controller.dart';
import '../model/note_model.dart';
import 'add_note_form.dart';
import 'edit_note_view.dart';
class NoteHomeView extends StatelessWidget {
NoteHomeView({super.key});
final NoteController controller = Get.put(NoteController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("π My Notes", style: TextStyle(fontWeight: FontWeight.bold)),
centerTitle: true,
backgroundColor: Colors.deepPurple,
),
body: ValueListenableBuilder<Box<NoteModel>>(
valueListenable: controller.noteBox.listenable(),
builder: (context, box, _) {
final notes = box.values.toList();
if (notes.isEmpty) {
return const Center(
child: Text(
"No notes available.\nTap + to add one!",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16, color: Colors.grey),
),
);
}
return ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: notes.length,
itemBuilder: (context, index) {
final note = notes[index];
return Container(
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: Colors.deepPurple.shade50,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.deepPurple.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 4),
),
],
),
child: ListTile(
contentPadding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
title: Text(note.title,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Colors.deepPurple)),
subtitle: Padding(
padding: const EdgeInsets.only(top: 6.0),
child: Text(note.description,
style: const TextStyle(fontSize: 14, color: Colors.black87)),
),
leading: const Icon(Icons.sticky_note_2_outlined, color: Colors.deepPurple, size: 28),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.edit),
color: Colors.orange,
onPressed: () {
Get.to(() => EditNote(index: index, note: note));
},
),
IconButton(
icon: const Icon(Icons.delete),
color: Colors.redAccent,
onPressed: () {
controller.deleteNote(index);
Get.snackbar("Deleted", "${note.title} has been deleted",
snackPosition: SnackPosition.BOTTOM);
},
),
],
),
),
);
},
);
},
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.deepPurple,
onPressed: () => Get.to(() => AddNoteForm()),
child: const Icon(Icons.add),
),
);
}
}
4οΈβ£ add_note_form.dart
dartCopyEdit// lib/NoteApp/views/add_note_form.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/note_controller.dart';
import '../model/note_model.dart';
class AddNoteForm extends StatelessWidget {
final controller = Get.find<NoteController>();
final titleController = TextEditingController();
final descriptionController = TextEditingController();
AddNoteForm({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Add Note"), backgroundColor: Colors.deepPurple),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
TextField(controller: titleController, decoration: const InputDecoration(labelText: "Title")),
const SizedBox(height: 12),
TextField(
controller: descriptionController,
decoration: const InputDecoration(labelText: "Description"),
maxLines: 4,
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
if (titleController.text.isNotEmpty && descriptionController.text.isNotEmpty) {
final newNote = NoteModel(
DateTime.now().millisecondsSinceEpoch,
titleController.text.trim(),
descriptionController.text.trim(),
);
controller.addNote(newNote);
Get.back();
Get.snackbar("Success", "Note added successfully");
} else {
Get.snackbar("Error", "Both fields are required");
}
},
child: const Text("Save Note"),
)
],
),
),
);
}
}
5οΈβ£ edit_note_view.dart
dartCopyEdit// lib/NoteApp/views/edit_note_view.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/note_controller.dart';
import '../model/note_model.dart';
class EditNote extends StatelessWidget {
final int index;
final NoteModel note;
final controller = Get.find<NoteController>();
EditNote({super.key, required this.index, required this.note});
@override
Widget build(BuildContext context) {
final titleController = TextEditingController(text: note.title);
final descriptionController = TextEditingController(text: note.description);
return Scaffold(
appBar: AppBar(title: const Text("Edit Note"), backgroundColor: Colors.deepPurple),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
TextField(controller: titleController, decoration: const InputDecoration(labelText: "Title")),
const SizedBox(height: 12),
TextField(
controller: descriptionController,
decoration: const InputDecoration(labelText: "Description"),
maxLines: 4,
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
if (titleController.text.isNotEmpty && descriptionController.text.isNotEmpty) {
final updatedNote = NoteModel(
note.id,
titleController.text.trim(),
descriptionController.text.trim(),
);
controller.updateNote(index, updatedNote);
Get.back();
Get.snackbar("Updated", "Note updated successfully");
} else {
Get.snackbar("Error", "Both fields are required");
}
},
child: const Text("Update Note"),
)
],
),
),
);
}
}
6οΈβ£ main.dart
dartCopyEdit// main.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'NoteApp/model/note_model.dart';
import 'NoteApp/views/note_home_view.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Hive.initFlutter();
Hive.registerAdapter(NoteModelAdapter());
await Hive.openBox<NoteModel>('notes');
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Hive Notes App',
debugShowCheckedModeBanner: false,
home: NoteHomeView(),
);
}
}
β Done! Final Checklist
Step | Done? |
Add hive , hive_flutter , get in pubspec.yaml | β |
Create & register NoteModel | β |
Create NoteController with GetX | β |
Setup Hive with Flutter | β |
Build Add , Edit , List UI with GetX | β |
Subscribe to my newsletter
Read articles from adnan minhan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
