How to Store and Access Data Locally in a Flutter Mobile App? — PART II

Talha SEZER
Codimis
Published in
5 min readAug 8, 2022

This article is a continuation of my previous article. As a continuation of the topic of local storage, I will describe two more methods in this article. I’ve explained Shared Preferences, Read and Write Files, and Sqlite before. In this article, I will explain Hive and Objectbox. If you want to read the first article, please click here.

HIVE

Hive is a lightweight and fast key-value database solution that is cross-platform (runs on mobile, desktop, and web) and is written in pure Dart. This gives it an instant advantage over sqflite, which doesn’t support Flutter web — Hive has any native dependencies, so it runs seamlessly on the web.

If you want to use Hive, you have to add dependencies to the pubspec.yaml.

dependencies:
hive: ^2.2.3
hive_flutter: ^1.1.0

Initializing Hive

Before moving on to the CRUD operations of the database, initialize Hive and open a box that will be used for storing the data.

Hive should be initialized before we load any boxes, so it is a smart idea to initialize it inside the main() function of your Flutter app to avoid any errors. Note that if you are using Hive in a non-Flutter, pure Dart app, use Hive.init() to initialize Hive.

main() async {
// Initialize hive
await Hive.initFlutter();
runApp(MyApp());
}

Type Adapters

In general, Hive supports all primitive types like List, Map, DateTime, and Uint8List. But sometimes you may need to store custom model classes that make data management easier.

TypeAdapters can either be written manually or generated automatically. It’s always better to use code generation to generate the required methods because it helps to prevent any mistakes that might occur while writing manually (and also it’s faster).

To generate a type adapter for a class annotate it with @HiveType and fields with @HiveField a HiveType must have a typeid and HiveField has a unique number. These field numbers are used to identify the fields in the Hive binary format, and should not be changed once your class is in use.

import 'package:hive/hive.dart';part 'person.g.dart';@HiveType(typeId: 1)
class Person {
@HiveField(0)
String name;
@HiveField(1)
int age;
@HiveField(2)
List<Person> friends;
}

Hive Boxes

Hive stores its data in boxes containing key-value sets. I like to see boxes as an organizer with files listed and arranged by a numbering framework or as normal maps in the dart. With hive, before you can read/compose data, a box should be opened. Boxes can be opened with await Hive.Openbox(‘name’) can get an instance of an opened box with Hive. Box (‘name’), where ‘name’ is the name of the case (saying the DB name).

Hive stores a reference to all open boxes. If you want to get an already opened box, you can use

var box = Hive.box('myBox');

Hive CRUD

Create

// Create new item  
Future<void> _createItem(Map<String, dynamic> newItem) async { await _shoppingBox.add(newItem);
_refreshItems(); // update the UI
}

Read

Map<String, dynamic> _readItem(int key) { 
final item = _shoppingBox.get(key);
return item; }

Update

Future<void> _updateItem(int itemKey, Map<String, dynamic> item) async {
await _shoppingBox.put(itemKey, item);
_refreshItems(); // Update the UI
}

Delete

Future<void> _deleteItem(int itemKey) async {    
await _shoppingBox.delete(itemKey);
_refreshItems();

OBJECTBOX

Similar to Hive, Objectbox is a Dart-native key-value database. It’s extremely fast, improves response rates, and enables real-time applications. The database supports Android, iOS, macOS, Linux, and Windows and delivers built-in object links/relationships. Objectbox in Flutter is a very fast NoSQL database that is optimized for extremely high performance on mobile devices. It is very lightweight and stores all the objects in it.

Objectbox is quite young and needs time to mature and be battle-tested in large projects.

Initializing Objectbox

If you want to use Objectbox, you have to add dependencies to the pubspec.yaml.

dependencies:

objectbox: ^0.11.0
objectbox_flutter_libs: any
path_provider: ^1.6.27

dev_dependencies:

build_runner: ^1.0.0
objectbox_generator: any

Firstly we should create our model class. The id is not added to the constructor because the value of the id will be assigned dynamically by objectbox.

class ObjectBox {

final String name;
int id;
/// Two Boxes: one for Tasks, one for Tags.
final Box<task> taskBox;
final Box<tag> tagBox;
ObjectBox({this.name,this.taskBox, this.tagBox })
}

Here we can insert the name of the objectbox and all of his details and with the help of his id, we can also delete and update those details.

There are reference codes to create, delete, insert, and update below.

CRUD operations

Create

We define the methods for adding new tasks and tags. Start by creating a task using the input controller. Then, set a tag we want to relate to this task by calling tag.target(). Now we only need to put the newly created Task object in its Box.

Task task = Task(_taskInputController.text);
task.tag.target = tagToAdd;
objectbox.taskBox.put(task);

Read

READ

To get all tag names, we call getAll() on our tagBox. This will return a list with all tags. If you want to read just a single object, call the get(id) method to get only the desired single object back. For a range of objects, use getMany(), passing a list of ids to it.

List<Tag> tags = objectbox.tagBox.getAll();

Update

Our app prints all tasks as a list with checkboxes. Each task’s finished date is initially set to null. We want the finished date of a task to update when the user marks the task as done. The non-null finished date therefore will act as an indicator that the task is finished. Let’s do this inside the setState() method of the onChanged property of our Checkbox class. Set the dateFinished to DateTime.now() if it was null when the checkbox value was changed and set back to null otherwise.

Checkbox(
value: notNull(tasks[index].dateFinished),
onChanged: (bool? value) {
setState(() {
if (!notNull(tasks[index].dateFinished)) {
tasks[index].dateFinished = DateTime.now();
} else {
tasks[index].dateFinished = null;
}
});
}
)

Delete

To remove objects from the database, we add a dismissible Flutter widget. Inside the setState method of the onDismissed property, we simply use the ObjectBox remove() operation with the corresponding task id.

Dismissible(

onDismissed: (direction) {
// Remove the task from the store.
objectbox.taskBox.remove(tasks[index].id);
setState(() {});

},
),

Conclusion

In this article, I have explained the basics of local storage in Flutter. This was a small introduction to local storage. I hope this blog will provide you with sufficient information about local storage.

REFERENCES

--

--