In the dynamic realm of mobile application development, efficient data management is crucial for delivering a seamless user experience. Flutter, with its rich ecosystem of libraries, offers developers powerful tools to tackle various challenges. One such gem is the Floor library, a SQLite abstraction that simplifies data persistence in Flutter apps.
Introduction
When building Flutter applications, one often encounters the need to store and retrieve data efficiently. Whether it's user preferences, application settings, or complex datasets, the ability to persistently store information is fundamental. The Floor library, inspired by the Room Persistence Library in Android development, provides a robust and developer-friendly solution for handling SQLite databases in Flutter.
Importance of persistance in mobile apps.
Offline Functionality: Mobile apps are expected to function seamlessly, even in offline mode. Whether users are traveling through areas with poor connectivity or intentionally disconnecting, having locally stored data ensures uninterrupted access to critical information.
Improved Performance: By storing frequently accessed data locally, Flutter apps can significantly enhance performance. Local storage reduces the need for continuous network requests, resulting in faster load times and a smoother user experience.
User Preferences and Settings: Persistent storage is vital for maintaining user preferences and application settings. Whether it's remembering theme choices, language preferences, or personalized settings, Flutter's data persistence capabilities empower developers to create personalized and user-friendly experiences.
Secure Data Handling: Storing sensitive information securely is paramount. The Floor library, through its integration with SQLite, provides a secure environment for handling data, ensuring that confidential information remains protected.
Scalability and Manageability: As Flutter apps evolve, so does the complexity of data management. A reliable data persistence solution simplifies the process of scaling an application, making it easier to manage and maintain as the project grows
Use case
Let's get our hands dirty with code and implement local data persistence in our flutter app using floor library. Let us create a flutter app which would locally store student's data. Below is the step by step guide to carry out the same
1. Setting up dependencies.
Add the runtime dependency floor as well as the generator floor_generator
to your pubspec.yaml
. The third dependency is build_runner
which has to be included as a dev dependency just like the generator.
floor holds all the code you are going to use in your application.
floor_generator
includes the code for generating the database classes.build_runner
enables a concrete way of generating source code files.
This is how your pubspec.yaml
should look like:
dependencies:flutter:sdk: flutter# The following adds the Cupertino Icons font to your application.# Use with the CupertinoIcons class for iOS style icons.cupertino_icons: ^1.0.2floor: ^1.4.2sqflite: ^2.3.0dev_dependencies:flutter_test:sdk: flutterfloor_generator: ^1.4.2build_runner: ^2.1.2
2. Creating an entity.
It will represent a database table as well as the scaffold of your business object. @entity
marks the class as a persistent class. It's required to add a primary key to your table. You can do so by adding the @primaryKey
annotation to an int
property. There is no restriction on where you put the file containing the entity.
import 'package:floor/floor.dart';@Entity(tableName: 'students')class Student {@PrimaryKey(autoGenerate: true)final int id;@ColumnInfo(name: 'name')final String name;@ColumnInfo(name: 'age')final int age;Student(this.id, this.name, this.age);}
@ColumnInfo
entity gives more information about the variable which is added as a column in the table. Followed by this is a constructor to create a Student.
3. Create database
Create a class that extends FloorDatabase and annotate it with @Database.
This class will represent your database and contain methods for accessing the database:
import 'dart:async';import 'package:floor/floor.dart';import 'package:sqflite/sqflite.dart' as sqflite;import '../dao/dao.dart';import '../entity/student_entity.dart';// This is generated by the floor generatorpart 'student_db.g.dart';@Database(version: 1, entities: [Student])abstract class AppDatabase extends FloorDatabase {StudentDao get studentDao;}
4. Create DAO
Create an interface with methods for database operations (e.g., insert, query). Annotate it with @dao
:
import 'package:floor/floor.dart';import 'package:student_app/floor/entity/student_entity.dart';@daoabstract class StudentDao {@Query('SELECT * FROM students')Future<List<Student>> findAllStudents();@Insert(onConflict: OnConflictStrategy.replace)Future<void> insertStudent(Student student);}
Creating an abstract class with @dao
annotation provides a level of abstraction thus, you do not have to manually write the functions to insert or get data from the database. You can use various other queries such as @Query
to run any SQL query. Floor also provides you with annotations to carry out basic database functions such as @Insert
.
onConflictStrategy
is the strategy followed by the database in case of a conflict. Replace
will replace the student entry with the incoming entry in case of conflict.
5. Run code generator
Run the code generation to generate the required files for the database. Use the above provided command.
flutter packages pub run build_runner build
flutter packages pub run build_runner buildThis command will generate a student_db.g file which must be added as a part file to the student_db file, like this
import 'dart:async';import 'package:floor/floor.dart';import 'package:sqflite/sqflite.dart' as sqflite;import '../dao/dao.dart';import '../entity/student_entity.dart';// This is generated by the floor generatorpart 'student_db.g.dart';
6. Creating app
Now, for the mobile application, we create 2 screens i.e. HomeScreen where we utilize the findAllStudents() function to get data of all the students in the database.
We declare a database variable like this , and then we create a function to initialize this variable
var database;Future<List<Student>> getDatabase() async {database = await $FloorAppDatabase.databaseBuilder('student_database.db').build();return database.studentDao.findAllStudents();}
The above function initializes the database variable and returns a list of all students added to the database.
FutureBuilder(future: getDatabase(),builder: (BuildContext context, AsyncSnapshot snapshot) {if (snapshot.hasData) {final List<Student> students = snapshot.data;if (students.isNotEmpty) {return ListView.separated(shrinkWrap: true,itemBuilder: (ctx, index) {return ListTile(leading: Text('${index+1}'),title: Text('Name : ${students[index].name}'),subtitle: Text('Age: ${students[index].age}'),);},separatorBuilder: (ctx, index){return const Divider(color: Colors.black12,);},itemCount: students.length);}else {return const Center(child: Text('No students added yet!'),);}} else if (snapshot.hasError) {return Center(child: Text(snapshot.error.toString()));} else {return const Center(child: CircularProgressIndicator());}}),
We use the FutureBuilder(
) widget in flutter to call a future function, our getDatabase( )
function, and then the snapshot data retrieved is utilized in the screen.
Further, using the FloatingActionButton
widget in the Scaffold widget in flutter, we pass this database object in the constructor of the AddStudentScreen( )
.
Using this object, we utilize the insertStudent( )
function in the database to add the Student object created in the screen.
OutlinedButton(onPressed: () {Student student = Student(0, nameController.text, int.parse(ageController.text));widget.database.studentDao.insertStudent(student);Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (ctx) => const HomeScreen()), (route) => false);},child: const Text('Save'))
After adding the student object to the database, we navigate to the home screen where our added student is visible.
Advantages of floor
null-safe
typesafe
reactive
lightweight
SQL centric