A simple technique when you migrate from mobile to Web.
Flutter is one of the greatest solutions to publish apps to multiple platforms. When you want to migrate Flutter for mobile to Flutter for Web, you typically only need to make a few changes.
But if you use local file storage on Flutter for mobile, there is a little bit vexing problem - How to handle local files on the Web.
I found one of the solutions, IndexedDB. I will explain how I use it as file storage.
dart:io not supported on Flutter Web
If you want to handle local files, you typically use the dart:io package that Dart language provides by default. It is used in the following way.
import 'dart:io';
final file = File(filePath);
if (file.existsSync()) {
data = file.readAsBytesSync();
}
But unfortunately on Flutter Web, you cannot use the dart:io package. So we have to seek an alternative method.
Using IndexedDB
This is what I found as an alternative solution to dart:io.
IndexedDB is a way for you to persistently store data inside a user’s browser. — MDN
IndexedDB is a large-scale, NoSQL storage system. It lets you store just about anything in the user’s browser. — Google Developers
I came up with a class that hides IndexedDB behind it to achieve the same functionality as dart:io’s File class. Like this;
final idbFile = IdbFile(filePath);
if (await idbFile.exists()) {
data = await idbFile.readAsBytes();
}
In the above code, I just change dart:io’s File class to my original IdbFile class. And I add the await keywords because it’s likely hard to implement the function working synchronously.
How about it? Don’t you think such a class would be just as useful as the regular File class?
idb_shim – the Dart Package for using IndexedDB
I found a Dart Package named idb_shim. It is helpful to use IndexedDB easily. Here is an example of how to use it from the official site.
// define the store name
const String storeName = "records";
// open the database
Database db = await idbFactory.open("my_records.db", version: 1,
onUpgradeNeeded: (VersionChangeEvent event) {
Database db = event.database;
// create the store
db.createObjectStore(storeName, autoIncrement: true);
});
// put some data
var txn = db.transaction(storeName, "readwrite");
var store = txn.objectStore(storeName);
var key = await store.put({"some": "data"});
await txn.completed;
// read some data
txn = db.transaction(storeName, "readonly");
store = txn.objectStore(storeName);
Map value = await store.getObject(key);
await txn.completed;
Be careful because there are several packages similar to this. However, some of those packages are working only in memory instead of persistent storage.
Database structure
To use it as file storage, I came up with the following object store structure ( named “files” ) for the IndexedDB. ( an object store is like a table and a property is like a column)
property | isKey | description |
---|---|---|
filePath | YES | The file path of the content. For example, “/directory-a/directory-b/something.txt.” |
contents | NO | Contents of the file. It can be saved as a string or binary. |
It’s a very simple structure! It might be better to have many other attributes for actual use, but this is enough to use for now.
When you store data to this object store, you can see the actual data via Chrome Developer Tools, like this:
Source Code
You can get the sample source code from here.
https://github.com/inclu-cat/use_indexeddb_as_filestorage
Conclusion
I have already migrated my private Flutter app from mobile to Web by using this method. And I’m satisfied that the app runs well.
It’s using PDF files approximately 500KB in size, and I haven’t felt a difference between native and Web in performance.
There is a lot of room for improvement in IdbFile class. For instance, directory management, file searching, etc.
I would be very happy if you make your own improvements to this class and find this article useful.
No responses yet