| Introduction
Today, we know that Internet-powered devices are being used at convenient places at convenient times. Users most significantly feel like using such a service to check how it works, to see how technology produces wonders, to ease themselves from the pressure of going physically to the place to get their work done, to use it as a helping hand when no one else is around to assist them.
The buying of products and services using smartphones, either through native or web applications is increasing. Despite all the other considerations, the fact remains that it is difficult to rely on mobile networks. It is very normal that there are high, medium, or very low coverage areas and sometimes no coverage at all. Besides, when cellular networks are barred, there are emergencies.
To experience such high latencies and network failure rates, modern mobile apps must be well-equipped. With the battery constraints and several other variables that would put a mobile application to the test, networks can be crappy, broken, and low, most often needing offline support. Regardless of server availability, developer skills, or device support, offline app support is required in case of an organization that has a global audience and it is necessary to save and secure data.
Today lost internet connection means missing records, supply chain interruptions, missed orders, customer dissatisfaction and so much more, and to tell there are a lot more and this is the reason the need to keep the systems functioning offline as well as it does online as an efficient business method to be productive all time.
Let’s take an example of warehouses where the manager needs to track the entry and exit of trucks and commodities, in a warehouse management application and the network may be unavailable as warehouses are located in remote areas.
Weak access to the network is directly proportional to a bad user experience. This contributes to resentment and turning the users off. It would be a strategic advantage to build a mobile app on the better side of the range, one that can also be made available offline, requiring some initial investment in the production of the back-end and front-end and making use of device storage to enable offline access. Nevertheless, an offline app that works will save consumer devices with transactional and master data, sync data that has been processed for a prolonged time seamlessly, and easily manage conflicts about data synchronization.
Quarantines around the world have increased people’s reliance on the internet, resulting in a significant decline in download and streaming speed, which is a terrible indicator for the app sector. The specter of decreased connection has shifted app entrepreneurs’ focus to offline apps.
| What are Offline First Applications?
The majority of mobile applications on the market connect to a database on servers and offer desired data to users. Offline applications for Android or iOS are a feature that allows users to access mobile apps without connecting to the Internet.
While offline mobile applications require a server connection, they do not require a constant internet connection. In this situation, data is downloaded to users’ devices and can be accessible while they are not connected to the internet.
On a technological level, when a connection is available, the offline-first applications download updates from the server and simultaneously upload modifications made by users while offline to the server.
The development of an offline mobile application is typically based on the following assumptions:
Offline apps assume that users are offline, which means that networks will be slow and unreliable.
The resources will be slower to obtain through the network than from a local source.
The app must notify users of low network conditions while not preventing them from achieving the mission.
Only data that has changed since the last synchronization must be synchronized, taking into account the network and battery conditions of the users.
How do offline apps benefit users?
Offline mode allows users to utilize the software even when their internet connection is down, flickering, slow, or intermittent. Here are some advantages:
- Gain a Competitive Advantage: In distant areas where internet access is limited or inconsistent, online-only apps can have a domino effect. Offline mode helps users continue with their work.
- Enhanced Customer Loyalty: Users like apps that run smoothly and without glitches. They will be more likely to use your app if they see that it is reliable in locations with bad connectivity and regardless of an internet connection.
- Reduced Loading Time: Because it is not dependent on a network or Internet connection, an app with offline mode loads faster. Users can enjoy your mobile service at their leisure this way.
- Saves battery life on mobile devices: This is one of the most beneficial aspects of developing offline mobile apps. Because offline mode apps promote faster loading times and better data storage management, the phone’s battery is saved.
- No roaming charges: Some apps require additional data/internet to connect to the massive data network. Furthermore, if you travel abroad, the roaming data package might be rather costly.In addition, incorporating offline mode into your software might provide customers with free access to certain functions. This saves both money and data.
Key Features Of Offline Modes In Mobile Apps
You can incorporate several functions in the offline mode, such as
- Data Storage Offline
Consider GPS navigation applications. Data is temporarily kept offline without the ability to change it.
- Offline Data Editing and Syncing
An example of an offline application is a note-taking app. The data can be edited and synchronized when offline.
- A user can edit other users’ data.
Another type of offline app is the ability to edit another user’s data (or data shared with others) while offline. Google Docs is an excellent example of such an offline application. It enables the user to continue with their tasks even if they lose a network connection in the middle, ensuring that they do not miss a beat. When they reconnect to the network, the data online is updated with all of the changes.
| How Can We Make Apps Work Offline?
The offline-first mobile app development process is a method of developing an app that can operate and execute the essential features in the absence of network access.
There is no secret formula for creating an optimal offline mode for your software. All you need to do now is decide on offline functionality for your app. However, here are some pointers to assist you to decide which path to choose.
- Analyze your process, define your business needs, and transform them into application functionality.
- Align these characteristics according to your needs since they become essential as soon as they become crucial to your firm.
- Determine the goal you want to achieve with your application. Share this information with your developers so they can understand your business objects and recommend the best mobile app development technologies for a custom-fit solution.
Once you are done deciding on the offline functions you need to select the right technologies to make your application available in offline mode. The technologies you choose have a significant influence on the performance of your app. Hundreds of mobile app development frameworks are available on the market. However, before picking on technology, keep the following points in mind:
Choosing technologies for offline use
The following are the few factors that should be considered while selecting technology for offline web apps.
Data synchronization frequency: Developers must establish a balance to ensure that data synchronization does not occur too frequently or too seldom. Frequent synchronization may deplete the user’s phone battery, whilst infrequent synchronization may result in the loss of essential updates.
Synchronization time: This is determined by the type and requirements of the company. Depending on the kind of data, certain updates may be performed regularly, while others may be performed annually. Daily updates are appropriate for modest data bundles. It is good to establish a set time for data synchronization.
Sensitive data: To minimize security risks, developers should examine how sensitive data is handled. A cache folder is used to store downloaded information when utilizing offline mode. You should avoid caching sensitive information like usernames and bank account numbers.
Synchronizing method: When syncing data, you should use a suitable technology. In this situation, you may specify whether data should be synchronized manually or automatically.
Once you decide and choose the right technologies, you are all set to go.
In this blog, we will look at how we can have our flutter mobile application work offline in conjunction with SQLite to handle the offline data and sync it with Mysql online database when the network is available.
Suppose we have to send some information from the application to our online DB, and the Internet is not available on the device at a certain time. At that point, we can store the data in local device SQLite DB and automatically send it to the server when the Internet is available in the future, instead of prompting the user with an error that the Internet is not available. We will see how we can achieve this here.
Network | Make API call and store in online DB, also save in SQLite DB.(1) |
No Network | Store in SQLite DB with 0 and sync with the online DB when the network is back.(update its status(1) as it is synched) |
| Getting started with the Flutter Application
Create a new Flutter application, we will call it dbsync_app.
We will add the plugins needed for the app
- Provider: state management plugin: The bloc plugin with Cubit is currently the recommended choice among most developers due to its simplicity and ease of use. It allows for efficient state management and separation of concerns in your application.
- SQLite: SQLite plugin for Flutter, which will handle all DB transactions. Hive: hive plugin for storing data and easy-to-handle DB transactions
- path_provider: plugin to access the file system
- Flutter_offline: plugin to handle offline/online connectivity in a clean way
- http/dio: Using dio in combination with the bloc plugin allows for even greater flexibility and ease of use. Additionally, it’s important to consider dependency injection when making network calls to avoid creating multiple client instances and ensure efficient performance.
We need to follow the two main steps in our application as follows
- Setting up the Sqlite DB with required tables and operations which will handle the offline data.
- Offline builder has the logic to handle the operations depending on the status of network connectivity.
Let’s get started with the creation of a SQLite DB instance as shown in below DatabaseHelper class. We will use a simple table with 2 columns for saving the data in this blog.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
class DatabaseHelper { static final _databaseName = "TestDatabase.db"; static final _databaseVersion = 1; static final table = 'my_table'; static final columnId = 'id'; static final columnName = 'name'; static final status = 'status'; // make this a singleton class DatabaseHelper._privateConstructor(); static final DatabaseHelper instance = DatabaseHelper._privateConstructor(); // only have a single app-wide reference to the database static Database _database; Future<Database> get database async { if (_database != null) return _database; // lazily instantiate the db the first time it is accessed _database = await _initDatabase(); return _database; } // this opens the database (and creates it if it doesn't exist) _initDatabase() async { Directory documentsDirectory = await getApplicationDocumentsDirectory(); String path = join(documentsDirectory.path, _databaseName); print(path); return await openDatabase(path, version: _databaseVersion, onCreate: _onCreate); } // SQL code to create the database table Future _onCreate(Database db, int version) async { await db.execute(''' CREATE TABLE $table ( $columnId INTEGER PRIMARY KEY, $columnName TEXT NOT NULL, $status INT NOT NULL ) '''); } |
Next, we would create the offline builder, which calls a function which fetches unsynced records from SQLite and make an API call to store in an online DB.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
OfflineBuilder( connectivityBuilder: ( BuildContext context, ConnectivityResult connectivity, Widget child, ) { _connectionStatus = connectivity != ConnectivityResult.none; if(_connectionStatus){ //offline builder gives network status continuously,based on that we call syncitnow method //Intent is when network is back,immediately we'll push local record to online server _syncitnow(_connectionStatus); } return Stack( children: [ child]) ; }, builder: (BuildContext context) { return SizedBox(); },), |
Syncitnow function, which will be called when the network is available.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
_syncitnow(_connectionStatus)async{ final nameProvider = Provider.of<SendNameProvider>(context, listen: false); if(_connectionStatus == true){ var allRows = await _getunsynchedrecords(); allRows.forEach((row)async { await nameProvider.sync(row['name'] ,_connectionStatus); await _update(row['id'], row['name']); } ); } } |
Above function gets the unsynced records in SQLite DB with status 0 and makes API calls to store each record in the online DB.
The following HTTP calls are made to store the data into the database:
- addName: makes an API call and stores the data in an online DB as well as a record in the SQLite DB.
- Sync: make API call to store unsynced records in online DB.
In the online call to DB, you will notice a call to saveName.php which is connecting to the MySQL database. We will see how we set that up in the next steps.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
Future<void> addName(text,_connectionStatus) async { print(text.toString()); int a = 1; print(_connectionStatus.toString()); try { //Checking network status,if "true" we'll make a api call,else we'll simply store record in sqlite if(_connectionStatus == true){ Response response = await Dio().post('http://192.168.42.175/SqliteSync/saveName.php', options: Options(headers: { HttpHeaders.contentTypeHeader: "application/json", }), data: jsonEncode(<String, dynamic>{ "name": text.toString(), "status": a }), ); if (response.statusCode == 200) { String body = response.statusMessage; print(body); Map<String, dynamic> row = { DatabaseHelper.columnName : text.toString(), DatabaseHelper.status : 1, }; await dbHelper.insert(row); } else { print('Request failed with status: ${response.statusCode}.'); } } else{ Map<String, dynamic> row = { DatabaseHelper.columnName : text.toString(), DatabaseHelper.status : 0, }; final id = await dbHelper.insert(row); print('inserted row id: $id'); } notifyListeners(); } catch (error) { throw (error); } } Future<void> sync(text,_connectionStatus) async { print(text.toString()); int a = 1; print(_connectionStatus.toString()); try { if(_connectionStatus == true){ Response response = await Dio().post('http://192.168.42.175/SqliteSync/saveName.php', options: Options(headers: { HttpHeaders.contentTypeHeader: "application/json", }), data: jsonEncode(<String, dynamic>{ "name": text.toString(), "status": a }), ); if (response.statusCode == 200) { String body = response.statusMessage; print(body); } else { print('Request failed with status: ${response.statusCode}.'); } }else{ } notifyListeners(); } catch (error) { throw (error); } } |
Sqlite DB queries to handle the DB operations as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Future<int> insert(Map<String, dynamic> row) async { Database db = await instance.database; return await db.insert(table, row); } // All of the rows are returned as a list of maps, where each map is // a key-value list of columns. Future<List<Map<String, dynamic>>> queryAllRows() async { Database db = await instance.database; return await db.query(table); } //Get all records which are unsynced Future<List<Map<String, dynamic>>> queryUnsynchedRecords() async { Database db = await instance.database; return await db.rawQuery('SELECT id,name,status FROM $table WHERE status = 0'); } Future<List<Map<String, dynamic>>> queryAllRecords() async { Database db = await instance.database; return await db.rawQuery('SELECT id,name,status FROM $table'); } |
Setting up the MySQL Database
We have installed a local instance of the MySQL database and have it running.
Next, we will create a PHP script to handle the insertion into the database, which will be called from our mobile app when the network is online. This script will connect with the online database and perform the operations in presence of the network.
| Creating a web service using XAMPP:
We will create a folder in the root directory (in my case it is htdocs eg:C:\xampp\htdocs\SqliteSync\saveName.php).
Now create a PHP file inside the folder named saveName.php with the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
<?php /* * Database Constants * Make sure you are putting the values according to your database here */ define('DB_HOST','127.0.0.1'); //127.0.0.1 define('DB_USERNAME','root'); define('DB_PASSWORD',''); define('DB_NAME', 'userdata'); //Connecting to the database $conn = new mysqli(DB_HOST, DB_USERNAME, DB_PASSWORD, DB_NAME); //checking the successful connection if($conn->connect_error) { die("Connection failed: " . $conn->connect_error); } //making an array to store the response $response = array(); //if there is a post request move ahead if($_SERVER["REQUEST_METHOD"] == "POST") { $_POST = json_decode(file_get_contents('php://input'), true); if(!empty($_POST)) { $name = $_POST['name']; $status = $_POST['status']; }else{ $name = 'empty'; $status = 0; } //getting the name from request //creating a statement to insert to database $stmt = $conn->prepare("INSERT INTO users (name,status) VALUES (?, ?)"); //binding the parameter to statement $stmt->bind_param("si", $name, $status); // if data inserts successfully if($stmt->execute()){ //making success response $response['error'] = false; $response['message'] = 'Name saved successfully'; }else{ //if not making failure response $response['error'] = true; $response['message'] = 'Please try later'; } }else{ $response['error'] = true; $response['message'] = "Invalid request"; } //displaying the data in json format echo json_encode($response); |
Testing Script
Now it’s time to test the PHP script we created. In my case the URL is
http://localhost/SqliteSync/saveName.php
We can test it from POSTMAN by calling the POST method with name and status to be saved in the database.
Now putting it all together, we can test the mobile application.
| Test Application
Run your application. And then try saving the name when the internet is available. Next, turn off the internet and again try saving your name.
When the internet is available again the data will be automatically sent to MySQL.
Below is the screenshot of how the records are rendered on the app, the tick mark indicative of the record being synced with online DB and the cross indicative of an unsynced record.
You can find the complete source code at https://github.com/abhilashahyd/dbsync_app
| Offline data storage in Flutter to ensure offline app functionality
Storing data offline on users’ devices is often necessary for mobile app developers. It can be used for anything from persisting data across app launches to saving language dictionaries for offline use. There are several solutions available for local data storage, including text/CSV/JSON files, SQLite, Hive Database, Shared Preferences, Objectbox, and Realm Database.
- Text/CSV/JSON files – This method is great for storing small amounts of data that aren’t too complicated. It’s easy to read and write these files in Flutter, but they may not be suitable for larger amounts of data or complex data structures.
- SQLite – This is a widely-used database management system that’s lightweight and easy to use. It’s a good choice for storing larger amounts of structured data.
- Hive Database – Hive is another type of database that’s lightweight, fast, and efficient. It’s good for storing complex data structures and can handle a lot of data.
- Shared Preferences – This is a simple key-value store that’s great for storing small amounts of data. It’s easy to use and automatically backed up by the operating system.
- Objectbox – Objectbox is a NoSQL database that’s designed to be fast and efficient. It’s good for storing complex data structures and offers advanced features like indexing and object relations.
- Realm Database – This is another NoSQL database that’s lightweight and designed for mobile devices. It’s great for storing structured data and offers advanced features like indexing and lazy loading.
- File Storage: Flutter provides APIs for working with files, allowing developers to read and write files to the device’s local storage. It is useful for storing data such as images, audio files, and videos.
- Firebase Offline Persistence: Firebase is a cloud-based platform that provides a variety of services, including real-time databases and cloud storage. It also offers offline persistence, which allows data to be stored locally on the device and synchronized with the cloud when an internet connection is available.
Each of these options has advantages and disadvantages, therefore it is critical to assess the volume and complexity of data before selecting a storage solution. These options help you determine which one is best suited for your app’s data storage needs.
| Conclusion
Offline applications inherently make lives simpler, make jobs more productive and time-efficient. Apps must be Offline applications inherently make lives simpler, and make jobs more productive and time-efficient. Apps must be offline-first so that when connectivity is weak, they do not slow down. We live in the ‘high-tech’ age, where smartphone users expect their mobile apps to have a decent performance and user experience and be outstanding. Offline support is an aspect of these standards today and can no longer be overlooked. You will significantly boost the user experience of the mobile app and increase the efficiency of your team by adding support for most of the offline scenarios.