"Building a CRUD (Create, Read, Update, Delete) Operations App in Flutter".

"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),
      ),
    );
  }
}