It’s time to create a Flutter app with the sqflite database

Mustufa Ansari
May 19 · 7 min read

Flutter is a popular framework for creating applications. I am exploring Flutter daily as there are lots of widgets and a lot to learn.

So, when we talk about productive apps, we always have some local storage in it.

In Flutter, we have three types of local storage:

  1. Read/write to text file.
  2. SharedPreferences.
  3. sqflite.

Sqflite is a little bit more complicated, compared to 1. and 2., so we are going to focus on the implementation of sqflite.

With the help of this, we’ll create a simple note-taking app and, of course, you will learn a lot of different things as we go through it.

Let’s get straight into it.

If you are new to Flutter, I suggest you go to this link and follow the instructions. Once you’ve gone through it, you’ll be able to set up the environment and Flutter SDK, then you can join me.


Getting Started

I am developing the app in Android Studio because it is easy and it helps me to set up everything according to Flutter’s needs. I am happy with that, but if you are not happy with Android Studio, you are very welcome to use the editor of your choice.

Here are the editors currently supporting Flutter:

Firstly, your project will look like this after creating it. Run your project to make sure everything is working perfectly.

Delete everything, we are starting from scratch.

Your code should look the same as mine:

import 'package:flutter/material.dart';

void main() {
runApp(MaterialApp(
title: "Notes App",
home: NotesList(),
));
}

class NotesList extends StatefulWidget {
@override
_NotesListState createState() => _NotesListState();
}

class _NotesListState extends State<NotesList> {
@override
Widget build(BuildContext context) {
return Container();
}
}

Now, it’s time to add some plugins.

In this app, we will have the following plugins:

  1. Sqflite plugin
  2. path_provider
  3. Intl

Add these plugins in the pubspec.yaml file, under your project folder and click on packages get.

You probably got this message after getting all the packages done (ignore the timing, it will get those according to your internet connection):

Running “flutter packages get” in notes_app… 4.7s
Process finished with exit code 0

Now, you have everything to make a full-fledged notetaking app.

Create some packages under the lib folder as it is a good practice for professional developers.

No wonder, I just moved my main.dart into the UI package.


Model Class

Let’s create our model that will help us to make our actual note.

In your models package, right-click on models package > New > Dart File and name it note.

Your Class should look like this:

class Note {
String _text, _date; //our note contain text,create date and an Id
int _id;

Note.update(this._text, this._date, this._id);

Note(this._text, this._date);

Note.map(dynamic obj) {
this._text = obj['name'];
this._date = obj['date'];
this._id = obj['id'];
}

Note.fromMap(Map<String, dynamic> map) {
this._text = map['name'];
this._id = map['id'];
this._date = map['date'];
}

Map<String, dynamic> toMap() {
Map<String, dynamic> map = new Map<String, dynamic>();

map['name'] = this._text;
map['date'] = this._date;

if (id != null) map['id'] = this._id;

return map;
}

get name => _text;

set name(value) {
_text = value;
}

get date => _date;

set date(value) {
_date = value;
}

int get id => _id;

set id(int value) {
_id = value;
}
}

Don’t worry about all methods, I will define everything as we go further. Because, after implementation, you will have a clear concept of what we did here.


Util Class

Follow the same steps as discussed above and make a class in the util package, named Constants.

This will help us not to write the same variables again and again. So, it’s good practice to add these types of classes in your project.

class Constants {
static final String TABLE_NAME = "noteTable";
static final String COLUM_TEXT = "text";
static final String COLUM_DATE = "date";
static final String COLUM_ID = "id";
static final int DB_VERSION = 1;
static final String DBNAME = "notes.db";
}
  • TABLE_NAME: name of our database gable.
  • COLUMN_TEXT: column of notes text.
  • COLUMN_DATE: date of note created.
  • COLUMN_ID: ID of particular row in db.
  • DB_VERSION: database version.
  • BDNAME: name of our database.

Database Helper Implementation

Now, let me tell you a bit about sqflite.

Sqflite works on the singleton pattern. If we define in a simple way what the singleton is, remember that the singleton class may not be required to instantiate again and again.

You only have one instance and you are good to go.

Navigate to your side project panel and right-click on database package > New > Dart File, name it database_helper, and hit Enter.

Create a class named DatabaseHelper and import some libraries and variables.

import 'dart:async';
import 'dart:io';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart';
import 'package:todo_app/utils/constants.dart';
import 'package:todo_app/models/note.dart';
class DatabaseHelper{static final DatabaseHelper _INSTANCE = new DatabaseHelper.make();

factory DatabaseHelper() => _INSTANCE;

static Database _db;

DatabaseHelper.make();
}
  • _INSTANCE: will be used to instantiate our database helper once.
  • factory: constructor is used to return an instance of databasehelper.
  • DatabaseHelper.make(): is a named constructor to make our class object.
  • Database _db: is our actual database and we will interact with this all the time.

Important:

Database execution in Flutter occurs in the background thread, as Flutter is a single-threaded UI framework, so it has the async, await, and Future keywords to make the things work in the background.

Let’s initialize our db.

Future<Database> get db async {
if (_db != null) return _db;
_db = await initDB();
return _db;
}
------------------------------------------------------------------
initDB() async {
Directory directory = await getApplicationDocumentsDirectory();
String path = join(directory.path, Constants.DBNAME);
var myDb =
openDatabase(path, version: Constants.DB_VERSION, onCreate: _onCreate);

return myDb;
}

The first method is the get method and it will return our actual database.

Now, I know you are wondering what initDB() is, it’s a method that helps to return our created database.

See, every method has the async and await keywords, because every operation in sqflite is performed by flutter in the background...that’s a beauty indeed!


_onCreate Method

This method has some SQL things, such as CREATE TABLE, with this it will create a table that will store our notes.

void _onCreate(Database db, int version) async {
await db.execute(
"CREATE TABLE ${Constants.TABLE_NAME} (${Constants.COLUM_ID} INTEGER PRIMARY KEY, "
"${Constants.COLUM_TEXT} TEXT, ${Constants.COLUM_DATE} TEXT );");
}

Inserting the notes method:

Future<int> insertNote(Note note) async {
var dbClient = await db;
int count = await dbClient.insert(Constants.TABLE_NAME, note.toMap());

return count;
}
  • The insertNote method will take a note object as an argument and that will insert into the database. It will happen in the background, now you are aware of this.
  • dbClient: is our database variable.
  • count: after inserting the values in the database, it will return an integer that will actually ID of that particular row on which your data is stored. We will use it later to get a single note from the database.

Now it’s time to define toMap() that we created earlier in the note.dart, remember?

Map<String, dynamic> toMap() {
Map<String, dynamic> map = new Map<String, dynamic>();

map['name'] = this._text;
map['date'] = this._date;

if (id != null) map['id'] = this._id;

return map;
}

Sqflite saves everything in a key-value pairs format. So, by default it takes the Map<String,dynamic> object as a parameter to inserts values in the db, so toMap() will do this for you. It will convert your object into Map.

The Map<String,dynamic> string is a key that will help Flutter understand which value corresponds to which key.

Dynamic is a keyword which is very powerful and advantageous, you can pass any value as a value in Map, as it could be an integer, string, boolean, double, etc. That’s the beauty of Flutter.

Now it’s time to get all the notes from the database.

We have method for that.

Future<List> getAllNotes() async {
var dbClient = await db;
var result = await dbClient.rawQuery(
"SELECT * FROM ${Constants.TABLE_NAME} ORDER BY ${Constants.COLUM_TEXT} ASC");

return result.toList();
}

We will get all the data from the database in a list of Maps.

Remember that everything in the database is stored as a Map? So, it will return a list of maps. We will extract data from it later. For now, just add this.

That’s enough for part one, I will see you in the next part.


Found this Article useful? Follow me Mustufa Ansari on medium. Check out my most read articles below.

Better Programming

Advice for programmers.

Mustufa Ansari

Written by

📱Mobile Application Developer 🎓Keen Learner 🎓Graduate 🎧Love Music 🎓 Tech Enthusiast!

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade