Introduction to Hive in Flutter and CRUD Operation

adnan minhanadnan minhan
15 min read

Table of contents

πŸ”° 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) using path_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 stores NoteModel 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?

StepWhat It Does
Import packagesAdds necessary libraries to use Hive and Flutter
main() functionInitializes 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

TaskWhy It's Important
Annotate model with HiveTypeTells Hive how to store your custom class
Add @HiveFieldsMaps object fields to Hive binary storage
Generate adapterAllows Hive to read/write NoteModel objects
Use HiveObjectProvides 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 your main() method.

  • Type <NoteModel> ensures Hive knows this box contains only NoteModel 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

MethodWhat 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
noteCountGets total count of notes
noteBoxRefers 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 controller

  • Shows 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

FeaturePurpose
ValueListenableBuilderListens 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
FloatingActionButtonAdds 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 injected HiveNoteController (from Get.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 your TextEditingController

  • 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

ElementRole
TextEditingControllerGrabs input from user
Get.find()Fetches the controller already in memory
controller.addNote()Adds a new note to Hive
NoteModelStores 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 Hive

  • Uses 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 box

  • note: The actual NoteModel object passed from the home screen

  • controller: 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 content

  • Uses 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 screen

  • Get.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

FeaturePurpose
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

StepDone?
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βœ…
0
Subscribe to my newsletter

Read articles from adnan minhan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

adnan minhan
adnan minhan