DEV Community

Cover image for Flutter: Data synchronization from sqflite to firebase and from firebase to sqflite
TAIO Sylvain
TAIO Sylvain

Posted on

Flutter: Data synchronization from sqflite to firebase and from firebase to sqflite

Step 1: Create a Flutter project
If you haven't installed Flutter yet, follow the instructions on the official Flutter website to do so. After that, create a new Flutter project using the following command:
flutter create data_synchronization
cd data_synchronization

Step 2: Set Up dependencies
Add the plugins to your pubspec.yaml file and run flutter pub get to install the dependencies as shown below.

dependencies:
path_provider: ^2.0.9
sqflite: ^2.0.2
firebase_core: ^2.8.0
cloud_firestore:
firebase_storage: ^11.0.0

Step 3: Create a Model

Image description

Step 4: Create your sqflite database management file

import 'dart:io'; import 'package:data_synchronization/person.dart'; import 'package:path_provider/path_provider.dart'; import 'package:sqflite/sqflite.dart'; import 'package:path/path.dart'; class DatabaseHelper { static const _databaseName = 'MyDatabase.db'; // ignore: constant_identifier_names static const int DB_VERSION = 1; //singleton class DatabaseHelper._(); static final DatabaseHelper instance = DatabaseHelper._(); Database? _database; Future<Database> get database async { if (_database != null) return _database!; _database = await _initDatabase(); return _database!; } //init database _initDatabase() async { Directory dataDirectory = await getApplicationDocumentsDirectory(); String dbPath = join(dataDirectory.path, _databaseName); return await openDatabase(dbPath, version: DB_VERSION, onCreate: _onCreateDB); } Future _onCreateDB(Database db, int version) async { //create tables await db.execute(''' CREATE TABLE ${Person.tblTable}( ${Person.colId} INTEGER PRIMARY KEY AUTOINCREMENT, ${Person.colName} TEXT, ${Person.colAge} INTEGER ) '''); } //Data - insert Future<int> insertData(Person blog) async { Database db = await database; return await db.insert(Person.tblTable, blog.toMap()); } //Data - update Future<int> updateDatag(Person blog) async { final db = await database; var result = await db.update(Person.tblTable, blog.toMap(), where: "${Person.colId} = ?", whereArgs: [blog.id]); return result; } //Data - retrieve all Future<List<Person>> fetchDatas() async { Database db = await database; List blogs = await db.query(Person.tblTable); return blogs.isEmpty ? [] : blogs.map((x) => Person.fromMap(x)).toList(); } // ignore: body_might_complete_normally_nullable Future<int?> closeDb() async { var dbClient = await database; await dbClient.close(); } deleteAll() async { final dbClient = await database; dbClient.rawDelete("Delete from ${Person.tblTable}"); } } 
Enter fullscreen mode Exit fullscreen mode

Step 5: Create a AddUser Widget

Nous configurons ici nos bases de données sqflite et mettons en place des requêtes pour ajouter, afficher, modifier et supprimer des données sqflite.

import 'package:data_synchronization/db_helper.dart'; import 'package:data_synchronization/person.dart'; import 'package:flutter/material.dart'; // Widget de formulaire pour saisir les données class AddPersonForm extends StatefulWidget { @override _AddPersonFormState createState() => _AddPersonFormState(); } class _AddPersonFormState extends State<AddPersonForm> { bool isLoad = false; final DatabaseHelper _dbHelper = DatabaseHelper.instance; final _formKey = GlobalKey<FormState>(); late TextEditingController _nameController; late TextEditingController _ageController; @override void initState() { super.initState(); _nameController = TextEditingController(); _ageController = TextEditingController(); } @override void dispose() { _nameController.dispose(); _ageController.dispose(); super.dispose(); } // Méthode pour enregistrer les données dans SQLite Future<void> _saveToSqflite() async { if (_formKey.currentState!.validate()) { await _dbHelper.insertData( Person( name:_nameController.text.trim(), age: int.parse(_ageController.text.trim()) ) ); _nameController.clear(); _ageController.clear(); // ignore: use_build_context_synchronously Navigator.pop(context); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Add User"), centerTitle: true, ), body: Form( key: _formKey, child: Column( children: [ TextFormField( controller: _nameController, decoration: const InputDecoration(labelText: 'Name'), validator: (value) { if (value == null || value.isEmpty) { return 'Please enter a name'; } return null; }, ), TextFormField( controller: _ageController, decoration: const InputDecoration(labelText: 'Age'), validator: (value) { if (value == null || value.isEmpty) { return 'Please enter an age'; } if (int.tryParse(value) == null) { return 'Please enter a valid age'; } return null; }, ), ElevatedButton( onPressed: _saveToSqflite, child: const Text('Save'), ), ], ), ), ); } } 
Enter fullscreen mode Exit fullscreen mode

Step 5: Create a HomeScreen Widget
In this view, we'll then display the data and create two functions to synchronize data from saqflite to Firebase and from Firebase to sqflite.

 import 'package:data_synchronization/db_helper.dart'; import 'package:data_synchronization/person.dart'; import 'package:data_synchronization/add_user.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { DatabaseHelper? _dbHelper; late List<Person> _blogs; bool _isLoading = true; bool _isSynch = false; final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey = GlobalKey<RefreshIndicatorState>(); @override void initState() { super.initState(); _dbHelper = DatabaseHelper.instance; _getBloges(); } Future _getBloges() async { List<Person> _blog = await _dbHelper!.fetchDatas(); setState(() { _blogs = _blog; _isLoading = false; }); } Future<Null> _refresh() async { List<Person> _blog = await _dbHelper!.fetchDatas(); setState(() { _blogs = _blog; }); return null; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Home Page"), actions: [ GestureDetector( onTap: () async{ await _dbHelper!.deleteAll(); setState(() { _refresh(); }); }, child: const Icon(Icons.delete_outline), ), const SizedBox(width: 10), myPopMenu() ], ), body: RefreshIndicator( key: _refreshIndicatorKey, onRefresh: _refresh, child: (_isLoading || _isSynch) ? const Center( child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation<Color>(Colors.pink), ) ) : _blogs.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( "Aucun résultat trouvé", ), GestureDetector( onTap: (){ _refresh(); }, child: const Padding( padding: EdgeInsets.only(top: 15), child: Icon(Icons.refresh,size: 40,color: Colors.black45,), ), ) ], ), ) : ListView.builder( shrinkWrap: true, itemCount: _blogs.length, itemBuilder: (context, index){ return Padding( padding: const EdgeInsets.only(top: 15, left: 2, right: 2), child: ListTile( leading: CircleAvatar( radius: 35, backgroundColor: Colors.pink, child: Center( child: Text("${_blogs[index].id}", style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 15 ,color: Colors.white ) ), ), ), title: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('${_blogs[index].name}', style: const TextStyle(fontWeight: FontWeight.bold) ), const SizedBox(height: 08), Text('${_blogs[index].age}') ], ), ) ); }, ), ), floatingActionButton: FloatingActionButton( onPressed: () => _navigateToAddUser(context), child: const Icon(Icons.add_outlined), ) ); } Widget myPopMenu() { return PopupMenuButton( onSelected: (value) { if(value==1){ syncSqfliteToFirebase(); }else if(value==2){ syncFirebaseToSqflite(); } }, itemBuilder: (context) => [ const PopupMenuItem( value: 1, child: Text('Synchronisation Sqflite => Firebase',maxLines: 2)), const PopupMenuItem( value: 2, child: Text('Synchronisation Firebase => Sqflite',maxLines: 2)), ]); } void _navigateToAddUser(BuildContext context){ Navigator.push(context, MaterialPageRoute( builder: (contex)=> AddPersonForm())).then((value) { setState(() { _refresh(); }); }); } // Method to synchronize data from SQLite to Firebase Future<void> syncSqfliteToFirebase() async { setState(() { _isSynch = true; }); // SQLite data retrieval List<Person> allpersonnes = await _dbHelper!.fetchDatas(); List<Person> newData = []; // Retrieve existing data from Firebase QuerySnapshot firebaseData = await FirebaseFirestore.instance.collection('firebase_tab').get(); // Check for duplicate data List<String> firebaseIds = firebaseData.docs.map((doc) => doc.id).toList(); for (var data in allpersonnes) { if (!firebaseIds.contains(data.id)) { newData.add( Person( id: data.id, name: data.name, age: data.age, ), ); } } // Send new data to Firebase CollectionReference collection = FirebaseFirestore.instance.collection('firebase_tab'); newData.forEach((person) { collection.doc(person.id).set(person.toJson()); }); setState(() { _isSynch = false; }); } // Method to sync data from Firebase to SQLite Future<void> syncFirebaseToSqflite() async { setState(() { _isSynch = true; }); // Retrieving data from Firebase QuerySnapshot firebaseData = await FirebaseFirestore.instance.collection('firebase_tab').get(); List<Person> firebasePeople = firebaseData.docs.map((doc) => Person.fromSnapshot(doc)).toList(); // Recovering existing data in SQLite List<Person> allpersonnes = await _dbHelper!.fetchDatas(); // Check for duplicate data List sqliteIds = allpersonnes.map((data) => data.id).toList(); List<Person> newData = []; for (var person in firebasePeople) { if (!sqliteIds.contains(person.id)) { print("oui oui ${person.name}"); newData.add(person); }else{ print("Nononon ${person.name}"); } } //Inserting new data in SQLite for (var person in newData) { print(person.name); _dbHelper!.insertData(person); } setState(() { _refresh(); _isSynch = false; }); } } 
Enter fullscreen mode Exit fullscreen mode

Don't forget to leave a comment if you enjoyed

Top comments (1)

Collapse
 
mahmoud-a-m-1993 profile image
Mahmoud

Wow. That's really great