How to Use SQLite in Flutter: A Step-by-Step Guide

    When building mobile applications, data storage is crucial. One of the most common ways to store data locally on a device is through SQLite, a lightweight database. In this blog post, we will guide you through how to use SQLite in Flutter, enabling you to create efficient, offline-friendly apps.

What is SQLite?

SQLite is a popular database engine that's embedded into mobile devices. It's perfect for applications that need to store small to medium amounts of structured data locally. With SQLite in Flutter, you can easily save and retrieve data without an internet connection.

Why Use SQLite in Flutter?

  • Local storage: Store data on the user's device, ensuring accessibility even when offline.
  • Lightweight: Efficient and fast for small to medium datasets.
  • Easy to use: With the sqflite plugin, Flutter integrates smoothly with SQLite.

Step 1: Add the Required Libraries

To use SQLite in Flutter, you need to add the sqflite library, which provides SQLite database functionality, and the pathpackage, which helps manage file paths.

  1. Open your pubspec.yaml file.
  2. Add the following dependencies:




dependencies:
  flutter:
    sdk: flutter
  sqflite: ^2.0.0+4
  path: ^1.8.0


Step 2: Create the Database Helper Class

Next, we'll create a helper class to manage the database. This class will handle the creation, connection, and CRUD operations.

import 'dart:async';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

class DatabaseHelper {
// Singleton pattern to ensure only one instance of DatabaseHelper is created
static final DatabaseHelper _instance = DatabaseHelper._internal();
factory DatabaseHelper() => _instance;

// Variable to hold the database instance
static Database? _database;

// Private constructor for singleton implementation
DatabaseHelper._internal();

// Getter to access the database instance, ensuring it is initialized before use
Future<Database> get database async {
// Initialize the database if it is not already initialized
_database ??= await _initDB();
return _database!;
}

// Method to initialize the SQLite database
Future<Database> _initDB() async {
// Get the path where the database will be stored on the device
String path = join(await getDatabasesPath(), 'example.db');

// Open or create the database with version 1 and execute onCreate callback to set up the schema
return await openDatabase(
path,
version: 1,
onCreate: (db, version) async {
// SQL query to create a table named 'items' with columns 'id' and 'name'
await db.execute('''
CREATE TABLE items(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT
)
''');
},
);
}

// Method to insert a new item into the 'items' table
Future<void> insertItem(String name) async {
// Access the database
final db = await database;
try {
// Insert the provided 'name' into the 'items' table
await db.insert('items', {'name': name});
} catch (e) {
// Handle any errors during the insert operation
print("Error inserting item: $e");
}
}

// Method to retrieve all items from the 'items' table
Future<List<Map<String, dynamic>>> getItems() async {
// Access the database
final db = await database;
// Query the 'items' table and return the list of rows
return await db.query('items');
}

// Method to update an existing item in the 'items' table by its ID
Future<void> updateItem(int id, String name) async {
// Access the database
final db = await database;
try {
// Update the 'name' of the item where the 'id' matches the provided ID
await db.update(
'items',
{'name': name},
where: 'id = ?',
whereArgs: [id],
);
} catch (e) {
// Handle any errors during the update operation
print("Error updating item: $e");
}
}

// Method to delete an item from the 'items' table by its ID
Future<void> deleteItem(int id) async {
// Access the database
final db = await database;
try {
// Delete the item where the 'id' matches the provided ID
await db.delete('items', where: 'id = ?', whereArgs: [id]);
} catch (e) {
// Handle any errors during the delete operation
print("Error deleting item: $e");
}
}
}

Explanation of the code:

  1. Singleton Pattern: This ensures that only one instance of the DatabaseHelper class is created throughout the app, saving resources.
  2. Database Initialization: The _initDB() method is responsible for creating the SQLite database (if it doesn't exist) and defining the structure of the items table.
  3. CRUD Operations:
    • Insert: The insertItem() method inserts new data into the database.
    • Query: The getItems() method retrieves all data from the items table.
    • Update: The updateItem() method modifies an existing record based on the id.
    • Delete: The deleteItem() method deletes a record from the database based on its id.

Step 3: Build the UI to Interact with SQLite

Now, let’s build a Flutter app that allows users to interact with the SQLite database. The app will have features to add, update, display, and delete items from the SQLite database.

import 'package:flutter/material.dart';
import 'database_helper.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sqflite Example',
home: MyHomePage(),
);
}
}

class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
final DatabaseHelper _dbHelper = DatabaseHelper();
final TextEditingController _controller = TextEditingController();
List<Map<String, dynamic>> _items = [];
int? _selectedItemId;

void _loadItems() async {
_items = await _dbHelper.getItems();
setState(() {});
}

void _addItem() {
if (_controller.text.isNotEmpty) {
_dbHelper.insertItem(_controller.text);
_controller.clear();
_loadItems();
}
}

void _updateItem() {
if (_selectedItemId != null && _controller.text.isNotEmpty) {
_dbHelper.updateItem(_selectedItemId!, _controller.text);
_controller.clear();
_selectedItemId = null;
_loadItems();
}
}

void _deleteItem(int id) {
_dbHelper.deleteItem(id);
_loadItems();
}

void _selectItem(Map<String, dynamic> item) {
_controller.text = item['name'];
_selectedItemId = item['id'];
}

@override
void initState() {
super.initState();
_loadItems();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Sqflite Example')),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _controller,
decoration: InputDecoration(
labelText: 'Enter item name',
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: _addItem,
child: Text('Add Item'),
),
ElevatedButton(
onPressed: _updateItem,
child: Text('Update Item'),
),
],
),
Expanded(
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_items[index]['name']),
onTap: () => _selectItem(_items[index]),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => _deleteItem(_items[index]['id']),
),
);
},
),
),
],
),
);
}
}


Key Points:

  1. TextEditingController: Manages input from the user to add and update items.
  2. ListView.builder: Dynamically generates a list of items from the database.
  3. CRUD Operations: The app handles database operations like adding, updating, retrieving, and deleting items using the SQLite database.
  4. State ManagementsetState() ensures the UI is refreshed whenever the item list is updated.






Conclusion

In this blog post, we demonstrated how to integrate SQLite into a Flutter application using the sqflite package. We covered:

  1. Database Setup: We created a singleton DatabaseHelper class for managing SQLite database initialization and schema.

  2. CRUD Operations: We implemented essential methods to create, read, update, and delete items in the database.

  3. User Interface: We designed a simple UI that allows users to interact with the database, making it easy to manage items.

By utilizing SQLite in your Flutter apps, you can effectively handle local data storage, enhancing user experience and performance. This foundational setup can be expanded with more advanced features as needed. Happy coding!




Comments

Popular posts from this blog

Unlocking the Power of OOP: A Beginner's Guide to Objects, Encapsulation, Inheritance, Abstraction, and Polymorphism

HTTP GET Response in Flutter

Building a Flutter Firebase Firestore CRUD App