SQLite is a fast-relational database that can be used to store data offline for mobile applications. CRUD means create, read, update, and delete, the four essential operations of persistent storage.
In this article, we are going to build a small Flutter app that uses SQLite to persist data.In order to fully understand the example, you should have:
SQLite is an open source relational database, it is used to create a database, perform different operation like add, delete, and remove data and one of the most popular ways to store data locally. SQLite does not require a server or backend code. All the data is saved to a text file in the device. For this article, we will be using the package Sqflite to connect with SQLite. Sqflite is packages for connecting to SQLite databases in Flutter.
Sqflite is a Flutter plugin for SQLite, a self-contained, high-reliability, embedded, SQL database engine. SQLite plugin for Flutter. Supports iOS, Android and MacOS. While it’s not a first-party plugin, it’s recommended in the official Flutter cookbook.
A Flutter plugin for finding commonly used locations on the filesystem. Supports Android, iOS, Linux, macOS and Windows. Not all methods are supported on all platforms.
path is a package to manipulate paths: join them, convert to absolute, add prefixes, get path info and so on.
path_provider is dedicated to more specific OS operations, e.g. downloads, temporary files, application documents are stored in different places based on the OS (obviously, file structure in Android is different than in iOS, Windows, Linux, etc.).
To compare these two, packages have different purpose - path to actually "work" with paths while path_provider, well, provide you with the exact path to resources based on OS. Hence, you cannot replace one with the other, if you need to use both, that's it, just use both of them.
In your project go to pubspec.yaml and look for dependencies. Under dependencies, add the latest version of sqflite and path_provider. You can find the latest version of the dependencies in https://pub.dev/. While writing this article the dependencies and its version below.
sqflite
: ^2.0.2path_provider
: ^2.0.9after adding the dependencies then click pub get link to update the dependencies in our flutter project its returns Process finished with exit code 0.
We are going to create an SQLite database called db_crud.db . It has only a single table named users. Below is the structure of the table:
Column | Type | Description |
---|---|---|
Id | INTEGER | id is Primary Key |
Name | TEXT | name is for User Name |
Contact | TEXT | contact is for user Contact Number |
Description | TEXT | description for some sample description |
We should create the directories and file as per the below project structure.
lib
db_helperdatabase_connection.dart
repository.dart
modelUser.dart
screensAddUser.dart
EditUser.dart
ViewUsers.dart
servicesUserService.dart
main.dart
Before creating the CRUD operations, we need to add a model. We can use this class type to transfer data between UI (main.dart) and other Database Class then we have to create a class called User which will contain different fields related to a notes source code below
class User{ int? id; String? name; String? contact; String? description; userMap() { var mapping = Map<String, dynamic>(); mapping['id'] = id ?? null; mapping['name'] = name!; mapping['contact'] = contact!; mapping['description'] = description!; return mapping; } }
In this User Class we are going to use four fields as per the table design and userMap() method is use to convert field to map for data operation.
Now, under the lib folder, create another folder called db_helper and inside of it create a class called Database Connection. This class will take care to create SQLite database db_crud, table users and setDatabase() method is to return database connection.
import 'package:sqflite/sqflite.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; class DatabaseConnection{ Future<Database>setDatabase() async { var directory = await getApplicationDocumentsDirectory(); var path = join(directory.path, 'db_crud'); var database = await openDatabase(path, version: 1, onCreate: _createDatabase); return database; } Future<void>_createDatabase(Database database, int version) async { String sql= "CREATE TABLE users (id INTEGER PRIMARY KEY,nameTEXT,contactText,description TEXT);"; await database.execute(sql); } }
While _createDatabase(database,version) method is use to create users table in db_crud.
Create a new file repository.dart in db_helper Now inside the class Repository , we can create a method insertData to insert data into the database.
for example:insertData(table, data) async { var connection = await database; return await connection?.insert(table, data); }
Here insertData() will take a list of details, then we loop inside the collection and insert each details to the table notes. The insertData() method takes the following parameters String table, Map<String, Object?> values, and that’s why we create a userMap() method in the model class.
Then to retrieve data, we can create another method in the Repository class, for example:
The readData() method to get all the records in the table.The readData() method will take one parameter String table which is table name.
readData(table) async { var connection = await database; return await connection?.query(table); }
While readDataByID() method is to get a particular record by ID.The readDataByID() method will take two parameters String table,int id which is table name and primary key of a record to read.
readDataById(table, itemId) async { var connection = await database; return await connection?.query(table, where: 'id=?', whereArgs: [itemId]); }
So here we use the query () method and give it the string users which is the table name. So, this will select all columns from the table users.
Then we can update data we can create another method in the Repository class, for example The updateData() method is use to update record in the table.
updateData(table, data) async { var connection = await database; return await connection ?.update(table, data, where: 'id=?', whereArgs: [data['id']]); }
The updateData() method will take two parameters String table, int id which is table name and primary key of a record to update.
Then we can delete data we can create another method in the Repository class, for example The deleteData() method is use to delete a record in the table.
deleteDataById(table, itemId) async { var connection = await database; return await connection?.rawDelete("delete from $table where id=$itemId"); }
The deleteData() method will take two parameters String table, int id which is table name and primary key of a record to delete.
import 'package:crud_sqflite_demo/db_helper/database_connection.dart'; import 'package:sqflite/sqflite.dart'; class Repository { late DatabaseConnection_databaseConnection; Repository() { _databaseConnection= DatabaseConnection(); } static Database? _database; Future<Database?>get database async { if (_database != null) { return _database; } else { _database = await _databaseConnection.setDatabase(); return _database; } } insertData(table, data) async { var connection = await database; return await connection?.insert(table, data); } readData(table) async { var connection = await database; return await connection?.query(table); } readDataById(table, itemId) async { var connection = await database; return await connection?.query(table, where: 'id=?', whereArgs: [itemId]); } updateData(table, data) async { var connection = await database; return await connection ?.update(table, data, where: 'id=?', whereArgs: [data['id']]); } deleteDataById(table, itemId) async { var connection = await database; return await connection?.rawDelete("delete from $table where id=$itemId"); } }
import 'package:crud_sqlite_joes/model/User.dart'; import 'package:crud_sqlite_joes/services/userService.dart'; import 'package:flutter/material.dart'; class AddUser extends StatefulWidget { const AddUser({Key? key}) : super(key: key); @override State<AddUser> createState() => _AddUserState(); } class _AddUserState extends State<AddUser> { var _userNameController = TextEditingController(); var _userContactController = TextEditingController(); var _userDescriptionController = TextEditingController(); bool _validateName = false; bool _validateContact = false; bool _validateDescription = false; var _userService=UserService(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("SQLite CRUD"), ), body: SingleChildScrollView( child: Container( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Add New User', style: TextStyle( fontSize: 20, color: Colors.teal, fontWeight: FontWeight.w500), ), const SizedBox( height: 20.0, ), TextField( controller: _userNameController, decoration: InputDecoration( border: const OutlineInputBorder(), hintText: 'Enter Name', labelText: 'Name', errorText: _validateName ? 'Name Value Can\'t Be Empty' : null, )), const SizedBox( height: 20.0, ), TextField( controller: _userContactController, decoration: InputDecoration( border: const OutlineInputBorder(), hintText: 'Enter Contact', labelText: 'Contact', errorText: _validateContact ? 'Contact Value Can\'t Be Empty' : null, )), const SizedBox( height: 20.0, ), TextField( controller: _userDescriptionController, decoration: InputDecoration( border: const OutlineInputBorder(), hintText: 'Enter Description', labelText: 'Description', errorText: _validateDescription ? 'Description Value Can\'t Be Empty' : null, )), const SizedBox( height: 20.0, ), Row( children: [ TextButton( style: TextButton.styleFrom( primary: Colors.white, backgroundColor: Colors.teal, textStyle: const TextStyle(fontSize: 15)), onPressed: () async { setState(() { _userNameController.text.isEmpty ? _validateName = true : _validateName = false; _userContactController.text.isEmpty ? _validateContact = true : _validateContact = false; _userDescriptionController.text.isEmpty ? _validateDescription = true : _validateDescription = false; }); if (_validateName == false && _validateContact == false && _validateDescription == false) { // print("Good Data Can Save"); var _user = User(); _user.name = _userNameController.text; _user.contact = _userContactController.text; _user.description = _userDescriptionController.text; var result=await _userService.SaveUser(_user); Navigator.pop(context,result); } }, child: const Text('Save Details')), const SizedBox( width: 10.0, ), TextButton( style: TextButton.styleFrom( primary: Colors.white, backgroundColor: Colors.red, textStyle: const TextStyle(fontSize: 15)), onPressed: () { _userNameController.text = ''; _userContactController.text = ''; _userDescriptionController.text = ''; }, child: const Text('Clear Details')) ], ) ], ), ), ), ); } }
import 'package:crud_sqlite_joes/model/User.dart'; import 'package:crud_sqlite_joes/services/userService.dart'; import 'package:flutter/material.dart'; class EditUser extends StatefulWidget { final User user; const EditUser({Key? key,required this.user}) : super(key: key); @override State<EditUser> createState() => _EditUserState(); } class _EditUserState extends State<EditUser> { var _userNameController = TextEditingController(); var _userContactController = TextEditingController(); var _userDescriptionController = TextEditingController(); bool _validateName = false; bool _validateContact = false; bool _validateDescription = false; var _userService=UserService(); @override void initState() { setState(() { _userNameController.text=widget.user.name??''; _userContactController.text=widget.user.contact??''; _userDescriptionController.text=widget.user.description??''; }); super.initState(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("SQLite CRUD"), ), body: SingleChildScrollView( child: Container( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Edit New User', style: TextStyle( fontSize: 20, color: Colors.teal, fontWeight: FontWeight.w500), ), const SizedBox( height: 20.0, ), TextField( controller: _userNameController, decoration: InputDecoration( border: const OutlineInputBorder(), hintText: 'Enter Name', labelText: 'Name', errorText: _validateName ? 'Name Value Can\'t Be Empty' : null, )), const SizedBox( height: 20.0, ), TextField( controller: _userContactController, decoration: InputDecoration( border: const OutlineInputBorder(), hintText: 'Enter Contact', labelText: 'Contact', errorText: _validateContact ? 'Contact Value Can\'t Be Empty' : null, )), const SizedBox( height: 20.0, ), TextField( controller: _userDescriptionController, decoration: InputDecoration( border: const OutlineInputBorder(), hintText: 'Enter Description', labelText: 'Description', errorText: _validateDescription ? 'Description Value Can\'t Be Empty' : null, )), const SizedBox( height: 20.0, ), Row( children: [ TextButton( style: TextButton.styleFrom( primary: Colors.white, backgroundColor: Colors.teal, textStyle: const TextStyle(fontSize: 15)), onPressed: () async { setState(() { _userNameController.text.isEmpty ? _validateName = true : _validateName = false; _userContactController.text.isEmpty ? _validateContact = true : _validateContact = false; _userDescriptionController.text.isEmpty ? _validateDescription = true : _validateDescription = false; }); if (_validateName == false && _validateContact == false && _validateDescription == false) { // print("Good Data Can Save"); var _user = User(); _user.id=widget.user.id; _user.name = _userNameController.text; _user.contact = _userContactController.text; _user.description = _userDescriptionController.text; var result=await _userService.UpdateUser(_user); Navigator.pop(context,result); } }, child: const Text('Update Details')), const SizedBox( width: 10.0, ), TextButton( style: TextButton.styleFrom( primary: Colors.white, backgroundColor: Colors.red, textStyle: const TextStyle(fontSize: 15)), onPressed: () { _userNameController.text = ''; _userContactController.text = ''; _userDescriptionController.text = ''; }, child: const Text('Clear Details')) ], ) ], ), ), ), ); } }
import 'package:crud_sqlite_joes/model/User.dart'; import 'package:flutter/material.dart'; class ViewUser extends StatefulWidget { final User user; const ViewUser({Key? key, required this.user}) : super(key: key); @override State<ViewUser> createState() => _ViewUserState(); } class _ViewUserState extends State<ViewUser> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("SQLite CRUD"), ), body: Container( padding: EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( "Full Details", style: TextStyle( fontWeight: FontWeight.w600, color: Colors.blueGrey, fontSize: 20), ), const SizedBox( height: 20, ), Row( children: [ const Text('Name', style: TextStyle( color: Colors.teal, fontSize: 16, fontWeight: FontWeight.w600)), Padding( padding: const EdgeInsets.only(left: 30), child: Text(widget.user.name ?? '', style: TextStyle(fontSize: 16)), ), ], ), const SizedBox( height: 20, ), Row( children: [ const Text('Contact', style: TextStyle( color: Colors.teal, fontSize: 16, fontWeight: FontWeight.w600)), Padding( padding: const EdgeInsets.only(left: 25), child: Text(widget.user.contact ?? '', style: TextStyle(fontSize: 16)), ), ], ), const SizedBox( height: 20, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Description', style: TextStyle( color: Colors.teal, fontSize: 16, fontWeight: FontWeight.w600)), const SizedBox( height: 20, ), Text(widget.user.description ?? '', style: const TextStyle(fontSize: 16)), ], ) ], ), )); } }
import 'dart:async'; import 'package:crud_sqlite_joes/db_helper/repository.dart'; import 'package:crud_sqlite_joes/model/User.dart'; class UserService { late Repository _repository; UserService(){ _repository = Repository(); } //Save User SaveUser(User user) async{ return await _repository.insertData('users', user.userMap()); } //Read All Users readAllUsers() async{ return await _repository.readData('users'); } //Edit User UpdateUser(User user) async{ return await _repository.updateData('users', user.userMap()); } deleteUser(userId) async { return await _repository.deleteDataById('users', userId); } }
import 'package:crud_sqlite_joes/model/User.dart'; import 'package:crud_sqlite_joes/screens/EditUser.dart'; import 'package:crud_sqlite_joes/screens/addUser.dart'; import 'package:crud_sqlite_joes/screens/viewUser.dart'; import 'package:crud_sqlite_joes/services/userService.dart'; import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.teal, ), home: const MyHomePage(), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { late List<User> _userList = <User>[]; final _userService = UserService(); getAllUserDetails() async { var users = await _userService.readAllUsers(); _userList = <User>[]; users.forEach((user) { setState(() { var userModel = User(); userModel.id = user['id']; userModel.name = user['name']; userModel.contact = user['contact']; userModel.description = user['description']; _userList.add(userModel); }); }); } @override void initState() { getAllUserDetails(); super.initState(); } _showSuccessSnackBar(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), ), ); } _deleteFormDialog(BuildContext context, userId) { return showDialog( context: context, builder: (param) { return AlertDialog( title: const Text( 'Are You Sure to Delete', style: TextStyle(color: Colors.teal, fontSize: 20), ), actions: [ TextButton( style: TextButton.styleFrom( primary: Colors.white, // foreground backgroundColor: Colors.red), onPressed: () async{ var result=await _userService.deleteUser(userId); if (result != null) { Navigator.pop(context); getAllUserDetails(); _showSuccessSnackBar( 'User Detail Deleted Success'); } }, child: const Text('Delete')), TextButton( style: TextButton.styleFrom( primary: Colors.white, // foreground backgroundColor: Colors.teal), onPressed: () { Navigator.pop(context); }, child: const Text('Close')) ], ); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("SQLite CRUD"), ), body: ListView.builder( itemCount: _userList.length, itemBuilder: (context, index) { return Card( child: ListTile( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => ViewUser( user: _userList[index], ))); }, leading: const Icon(Icons.person), title: Text(_userList[index].name ?? ''), subtitle: Text(_userList[index].contact ?? ''), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => EditUser( user: _userList[index], ))).then((data) { if (data != null) { getAllUserDetails(); _showSuccessSnackBar( 'User Detail Updated Success'); } }); ; }, icon: const Icon( Icons.edit, color: Colors.teal, )), IconButton( onPressed: () { _deleteFormDialog(context, _userList[index].id); }, icon: const Icon( Icons.delete, color: Colors.red, )) ], ), ), ); }), floatingActionButton: FloatingActionButton( onPressed: () { Navigator.push(context, MaterialPageRoute(builder: (context) => const AddUser())) .then((data) { if (data != null) { getAllUserDetails(); _showSuccessSnackBar('User Detail Added Success'); } }); }, child: const Icon(Icons.add), ), ); } }
You've learned the fundamentals of SQLite and gone through an end-to-end example of using SQLite in a Flutter app. From here, you can build more complex apps that store a lot of data offline.
To download raw file Click HereLearn All in Tamil © Designed & Developed By Tutor Joes | Privacy Policy | Terms & Conditions