"Building a CRUD (Create, Read, Update, Delete) Operations App in Flutter".
In the following article, we'll start on a journey to build a Flutter app that handles CRUD (Create, Read, Update, Delete) operations on data. Flutter is a powerful UI framework for creating natively built mobile, web, and desktop applications. For many real-world applications, the essential functionality of creating, reading, updating, and deleting data entries will be shown by our program.
Step 1: Setting Up the Flutter Project
import 'package:crud_operation/home_screen.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const HomeScreen(),
);
}
}
Step: 2 Create a Database Helper Page
import 'package:sqflite/sqflite.dart' as sql;
class SQLHelper {
static Future<void> createTables(sql.Database database) async {
await database.execute(""" CREATE TABLE data(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
title TEXT,
desc TEXT,
createdAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)""");
}
static Future<sql.Database> db() async {
return sql.openDatabase("demo.db", version: 1,
onCreate: (sql.Database database, int version) async {
await createTables(database);
});
}
static Future<int> createData(String title, String? desc) async {
final db = await SQLHelper.db();
final data = {'title': title, 'desc': desc};
final id = await db.insert('data', data,
conflictAlgorithm: sql.ConflictAlgorithm.replace);
return id;
}
static Future<List<Map<String, dynamic>>> getDataAll() async {
final db = await SQLHelper.db();
return db.query('data', orderBy: 'id');
}
static Future<List<Map<String, dynamic>>> getSingalData(int id) async {
final db = await SQLHelper.db();
return db.query('data', where: "id = ?", whereArgs: [id], limit: 1);
}
static Future<int> updateData(int id, String title, String? desc) async {
final db = await SQLHelper.db();
final data = {
'title': title,
'desc': desc,
'createdAt': DateTime.now().toString()
};
final result =
await db.update('data', data, where: 'id == ?', whereArgs: [id]);
return result;
}
static Future<void> deleteData(int id) async {
final db = await SQLHelper.db();
try {
await db.delete('data', where: "id = ?", whereArgs: [id]);
} catch (e) {}
}
}
Step: 3 UI screen
import 'package:crud_operation/db_helper.dart';
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
List<Map<String, dynamic>> _allData = [];
bool _isLoading = true;
List<int> selectedItemsIndices = [];
void _refreshData() async {
final data = await SQLHelper.getDataAll();
setState(() {
_allData = data;
_isLoading = false;
});
}
@override
void initState() {
super.initState();
_refreshData();
}
Future<void> _addData() async {
await SQLHelper.createData(_titleController.text, _descController.text);
_refreshData();
}
Future<void> _updateData(
int id,
) async {
await SQLHelper.updateData(id, _titleController.text, _descController.text);
_refreshData();
}
void _deleteData(int id) async {
await SQLHelper.deleteData(id);
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
backgroundColor: Colors.redAccent,
content: Text("Data Delete"),
));
_refreshData();
}
final TextEditingController _titleController = TextEditingController();
final TextEditingController _descController = TextEditingController();
void _showCustomBottomSheet(int? id) async {
if (id != null) {
final existingData =
_allData.firstWhere((element) => element['id'] == id);
_titleController.text = existingData['title'];
_descController.text = existingData['desc'];
}
showModalBottomSheet(
elevation: 5.0,
isScrollControlled: true,
context: context,
builder: (_) => Container(
padding: EdgeInsets.only(
top: 20,
left: 15,
right: 15,
bottom: MediaQuery.of(context).viewInsets.bottom + 20,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
TextField(
controller: _titleController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: " Title ",
),
),
const SizedBox(
height: 10,
),
TextField(
controller: _descController,
maxLines: 3,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: " Desc ",
),
),
const SizedBox(
height: 20,
),
Center(
child: ElevatedButton(
onPressed: () async {
if (id == null) {
await _addData();
}
if (id != null) {
await _updateData(id);
}
_titleController.text = " ";
_descController.text = " ";
Navigator.of(context).pop();
},
child: Padding(
padding: const EdgeInsets.all(0.8),
child: Text(
id == null ? "Add Data " : "Update ",
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
),
),
),
),
)
],
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.purple[50],
appBar: AppBar(
title: const Text(
"CRUD OPERATIONS",
style: TextStyle(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
backgroundColor: Colors.purple[100],
actions: [
if(selectedItemsIndices.isNotEmpty)
IconButton(
icon: Icon(Icons.delete_forever,color: Colors.red.shade500,),
onPressed: () {
for(int index in selectedItemsIndices){
_deleteData(_allData[index]['id']);
}
selectedItemsIndices.clear();
},
),
],
),
body: _isLoading
? const Center(
child: CircularProgressIndicator(),
)
: ListView.builder(
itemCount: _allData.length,
itemBuilder: (context, index) {
final isSelected = selectedItemsIndices.contains(index);
final isLongPressed = selectedItemsIndices.isNotEmpty;
return GestureDetector(
onLongPress: () {
setState(() {
if (!isLongPressed) {
selectedItemsIndices.add(index);
}
});
},
onTap: () {
setState(() {
if (isLongPressed) {
if (isSelected) {
selectedItemsIndices.remove(index);
} else {
selectedItemsIndices.add(index);
}
}
});
},
child: Card(
color: isSelected ? Colors.purple.shade200 : Colors.white,
child: ListTile(
title: Text(
_allData[index]['title'],
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
),
subtitle: Text(
_allData[index]['desc'],
style: const TextStyle(
fontSize: 16,
),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () {
_showCustomBottomSheet(_allData[index]['id']);
},
icon: const Icon(
Icons.edit,
color: Colors.indigo,
)),
IconButton(
onPressed: () {
_deleteData(_allData[index]['id']);
},
icon: const Icon(
Icons.delete_forever,
color: Colors.red,
))
],
),
),
),
);
}),
floatingActionButton: FloatingActionButton(
onPressed: () => _showCustomBottomSheet(null),
child: const Icon(Icons.add),
),
);
}
}