make your Contact Manager app store data permanently.
That means, even if you close the app and reopen, contacts will still be there.
Flutter has two common storage options:
- SQLite → Relational database (like MySQL/PostgreSQL).
- Best for structured data (tables, queries).
- Needs
sqflite
package.
- Hive → NoSQL, very fast, stores key–value pairs/objects.
- Best for simple apps.
- No SQL queries needed.
Since you’re just starting, I’ll teach you Hive first (easier). Later we can do SQLite.
Step 1: Add Hive to Your Project
Open pubspec.yaml
and add dependencies:
dependencies:
flutter:
sdk: flutter
hive: ^2.2.3
hive_flutter: ^1.1.0
dev_dependencies:
hive_generator: ^2.0.0
build_runner: ^2.4.8
Run:
flutter pub get
Step 2: Initialize Hive in main.dart
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Hive
await Hive.initFlutter();
// Open a box (like a database table)
await Hive.openBox('contactsBox');
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const ContactManager(),
);
}
}
Step 3: Use Hive Box for CRUD
class ContactManager extends StatefulWidget {
const ContactManager({super.key});
@override
State<ContactManager> createState() => _ContactManagerState();
}
class _ContactManagerState extends State<ContactManager> {
final TextEditingController nameController = TextEditingController();
final TextEditingController emailController = TextEditingController();
final contactsBox = Hive.box('contactsBox');
// Add Contact
void addContact() {
contactsBox.add({
"name": nameController.text,
"email": emailController.text,
});
nameController.clear();
emailController.clear();
}
// Update Contact
void updateContact(int index, Map contact) {
nameController.text = contact["name"];
emailController.text = contact["email"];
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text("Update Contact"),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(controller: nameController, decoration: const InputDecoration(labelText: "Name")),
TextField(controller: emailController, decoration: const InputDecoration(labelText: "Email")),
],
),
actions: [
TextButton(
onPressed: () {
contactsBox.putAt(index, {
"name": nameController.text,
"email": emailController.text,
});
nameController.clear();
emailController.clear();
Navigator.pop(context);
},
child: const Text("Update"),
),
],
),
);
}
// Delete Contact
void deleteContact(int index) {
contactsBox.deleteAt(index);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Contact Manager")),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(controller: nameController, decoration: const InputDecoration(labelText: "Name")),
TextField(controller: emailController, decoration: const InputDecoration(labelText: "Email")),
const SizedBox(height: 10),
ElevatedButton(
onPressed: addContact,
child: const Text("Add Contact"),
),
const SizedBox(height: 20),
// Contacts List (auto updates when box changes)
Expanded(
child: ValueListenableBuilder(
valueListenable: contactsBox.listenable(),
builder: (context, box, _) {
return ListView.builder(
itemCount: box.length,
itemBuilder: (context, index) {
final contact = box.getAt(index) as Map;
return Card(
child: ListTile(
title: Text(contact["name"]),
subtitle: Text(contact["email"]),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.edit, color: Colors.blue),
onPressed: () => updateContact(index, contact),
),
IconButton(
icon: const Icon(Icons.delete, color: Colors.red),
onPressed: () => deleteContact(index),
),
],
),
),
);
},
);
},
),
)
],
),
),
);
}
}
What Changed?
- Instead of using a
List
, we use Hive Box (contactsBox
). add()
→ inserts new contact.putAt()
→ updates contact at index.deleteAt()
→ deletes contact.ValueListenableBuilder
→ auto-refresh UI when data changes.- Data is saved permanently in your device storage.