Build a Basic Note App with Flutter and GetX (CRUD Operations Explained Step-by-Step

β¨ Introduction
In this tutorial, we'll build a simple Note app using Flutter and GetX, one of the most lightweight and powerful state management packages in the Flutter ecosystem.
You will learn how to:
Create a model class
Implement a controller with GetX
Add, read, update, and delete notes (CRUD operations)
Navigate between views using GetX
Build a responsive and clean UI
Perfect for beginners who want to understand basic app architecture and GetX!
π Project Structure
/lib
βββ NoteApp
βββ model
β βββ note_model.dart
βββ controllers
β βββ note_controller.dart
βββ views
β βββ add_note_form.dart
β βββ edit_note_view.dart
β βββ note_home_view.dart
βββ main.dart
π§± Step 1: Creating the Note Model
This class will store each note's data.
class NoteModel {
final int id;
final String title;
final String description;
NoteModel(this.id, this.title, this.description);
}
π§ What This Does:
This defines a NoteModel
class that represents a single note in your app. Each note contains:
An ID (
int
) β a unique identifier for the note.A Title (
String
) β the heading or short label of the note.A Description (
String
) β the detailed content of the note.
Field | Type | Purpose |
id | int | Uniquely identifies each note. |
title | String | The noteβs title or heading. |
description | String | The content/details of the note. |
π― Step 2: Setting Up the Note Controller with GetX
The controller manages the state and logic of the app.
import 'package:allapipractice/NoteApp/model/note_model.dart';
import 'package:flutter/material.dart';
import 'package:get/state_manager.dart';
class NoteController extends GetxController {
final titleController = TextEditingController();
final descriptionController = TextEditingController();
final noteList = <NoteModel>[].obs;
addNewNote(NoteModel noteData) {
noteList.add(noteData);
titleController.clear();
descriptionController.clear();
update();
}
deleteNote(int index) {
noteList.removeAt(index);
update();
}
updateNote(int index, NoteModel updatedNote) {
noteList[index] = updatedNote;
titleController.clear();
descriptionController.clear();
update();
}
}
Here, we use RxList
to make noteList
observable and clear the text controllers after each action.
π‘ Step 3: Home Screen - Listing the Notes
import 'package:allapipractice/NoteApp/controllers/note_controller.dart';
import 'package:allapipractice/NoteApp/views/add_note_form.dart';
import 'package:allapipractice/NoteApp/views/edit_note_view.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class NoteHomeView extends StatelessWidget {
final controller = Get.put(NoteController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("My Notes"),
centerTitle: true,
),
body: Obx(
() => ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: controller.noteList.length,
itemBuilder: (context, index) {
final singleNote = controller.noteList[index];
return Card(
margin: const EdgeInsets.only(bottom: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 2,
child: ListTile(
title: Text(
singleNote.title,
style: const TextStyle(fontSize: 16),
),
subtitle: Text(
singleNote.description,
style: const TextStyle(fontSize: 12),
),
leading: const Icon(Icons.note),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () {
Get.to(() => EditNote(
index: index,
note: singleNote,
));
},
icon: Icon(Icons.edit)),
IconButton(
onPressed: () {
Get.snackbar("Deleted",
"${singleNote.title} has been deleted");
controller.deleteNote(index);
},
icon: Icon(Icons.delete))
],
)),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Get.to(() => AddNoteForm());
},
child: const Icon(Icons.add),
),
);
}
}
β Step 4: Add Note View
import 'package:allapipractice/NoteApp/model/note_model.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/note_controller.dart';
class AddNoteForm extends StatelessWidget {
final controller = Get.find<NoteController>();
AddNoteForm({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Add New Note"),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: controller.titleController,
decoration: InputDecoration(labelText: 'Title'),
),
TextField(
controller: controller.descriptionController,
decoration: InputDecoration(labelText: 'Description'),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () {
controller.addNewNote(NoteModel(
controller.noteList.length + 1,
controller.titleController.text,
controller.descriptionController.text));
Get.snackbar("Success", "Note has been created");
Get.back();
},
child: Text("Save Note"))
],
),
),
);
}
}
βοΈ Step 5: Edit Note View
import 'package:allapipractice/NoteApp/model/note_model.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/note_controller.dart';
class EditNote extends StatelessWidget {
final NoteModel note;
final int index;
final NoteController controller = Get.find();
EditNote({Key? key, required this.note, required this.index})
: super(key: key);
@override
Widget build(BuildContext context) {
controller.titleController.text = note.title;
controller.descriptionController.text = note.description;
return Scaffold(
appBar: AppBar(
title: Text("Edit Note"),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: controller.titleController,
decoration: InputDecoration(labelText: 'Title'),
),
TextField(
controller: controller.descriptionController,
decoration: InputDecoration(labelText: 'Description'),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () {
final updatedNote = NoteModel(
note.id,
controller.titleController.text,
controller.descriptionController.text);
controller.updateNote(index, updatedNote);
Get.snackbar("Success", "Note updated successfully");
Get.back();
},
child: Text("Save Note"))
],
),
),
);
}
}
π§ͺ Final Testing
Run your app.
Try adding, editing, and deleting notes.
Make sure data updates correctly in real time.
π§Ή Improvements You Can Try
Store notes in local database (Hive or Sqflite)
Add input validation
Support dark mode and theming
Add tags or categories
β Conclusion
You now have a fully functional Note app with CRUD operations using Flutter and GetX!
If you're new to state management, this is a perfect first step before moving on to persistent storage, APIs, and more advanced app architecture.
Happy coding! π
Subscribe to my newsletter
Read articles from adnan minhan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
