Unlock the Power of Flutter with Isar. An Interesting Guide

As Flutter is heading toward its maximum potential, many third-party service providers related to various domains launch their stuff to become compatible with Flutter, so a developer may find it easy to build Apps supporting multiple platforms following these services. With this, today let’s talk about Isar Database, a superfast, cross-platform NOSQL Database designed for Flutter. Moreover, Isar comes up with a variety of other features that make it unique compared to other ones. So let’s Unlock the Power of Flutter with Isar. An Interesting Guide.


Before we move on to the index section, there were some relatable database blog posts I thought I should mention to make them more accessible to my audience.

Drift Database
Hive Database
SQLite Database

Table of Contents

– Features of Isar Database

Isar has some stunning features that include:

Awesome in scalability – Make a quick query efficient, as it is highly optimized, no matter how much the data count is

Feature-Rich – Support for multi-level indexes and JSON support

Open-Source – It’s completely open-source and free

It's asynchronous – Multi-isolate support out of the box.

However, there is one more thing that needs to be mentioned:

Isar is specifically made for Flutter, which means it plays a potential role in reducing boilerplate code, making it easy to use.

Now, let’s add a project scenario that would best fit Isar.

– Project Scenario

We shall be creating a short book application containing the following entities:

book_id

book_title

book_author

book_publish_year

book_isbn

Let’s now move on with the actual implementation of Isar.

– Setup & Implementation

Create a new Flutter project, and add the following packages inside pubspec.yaml under

dependencies section:

isar: (Core package)

isar_flutter_libs: (A core binary for Isar, vis-à-vis a must for Flutter Apps)

dev_dependencies section:

isar_generator: (A core generator class for Isar, identifies annotated classes)

build_runner: (A code generation tool)

path_provider: To get the device directory

Furthermore, there are two modes of package installation.

Manual Installation:

Head over to the pub.dev and install the suitable one.

——————- OR ——————-

Just pin your project’s terminal and add the following command:

flutter pub add package_name.....

Note: It will install the appropriate and best-suited version of packages specifically depending on the version of your Dart-SDK.

After installing all the packages, let us first add a book model class or collection in terms of Isar.

Create a new file like books.dart and add the following code snippet:

import 'package:isar/isar.dart';

part 'books.g.dart';

@collection
class Book {
  Id id = Isar.autoIncrement;

  String? title, author, publishYear, isbn;
}

Note: You will see an error in this line books.g.dart, because we haven’t generated the actual code stuff that will be used to initialize the database.

Also, be sure the file name and name with extension .g must be the same, to generate the runner’s command successfully.

Now, again pin the terminal and jot down this command:

flutter pub run build_runner build

This will install the necessary entities required and will auto-resolve the issues about it.

We’re done with the database core files. It’s time to add the UI snippets to keep our database up and running.

Create a new directory as screens and attach add_book.dart.

To this file, add the following code snippet:

import 'package:flutter/material.dart';
import 'package:isar_database_overview_ii/database/books.dart';
import 'package:isar_database_overview_ii/main.dart';

class AddBook extends StatefulWidget {
  const AddBook({Key? key}) : super(key: key);

  @override
  State<AddBook> createState() => _AddBookState();
}

class _AddBookState extends State<AddBook> {

  late final TextEditingController _title, _author, _publishYear, _isbn;

  @override
  void initState() {
    // TODO: implement initState
    _title = TextEditingController();
    _author = TextEditingController();
    _publishYear = TextEditingController();
    _isbn = TextEditingController();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Add a new Book"),
      ),
      body: ListView(
        children: [

          formField(_title, 'Book Title'),
          formField(_author, 'Author'),
          formField(_publishYear, 'Year of Publication'),
          formField(_isbn, 'ISBN'),
        ],
      ),
      bottomNavigationBar: Padding(
        padding: const EdgeInsets.all(8.0),
        child: ElevatedButton(
          style: ElevatedButton.styleFrom(
            fixedSize: const Size(double.infinity, 50),
          ),
          child: const Text("Save"),
          onPressed: () {},
        ),
      ),
    );
  }

  Widget formField(TextEditingController controller, String hint) {
    return Container(
      margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
      child: TextFormField(
        controller: controller,
        decoration: InputDecoration(
          hintText: hint,
        ),
        textInputAction: controller == _isbn ? TextInputAction.done : 
        TextInputAction.next,
      ),
    );
  }
}

Note: I have used StatefulWidget to not let any controller initialization hinder the code.

UI View

add_book.dart

Let’s modify the Button’s onPressed entity with the Isar code.

To write the data using Isar, we must have an appropriate format.

Here is a sample code provided.

var model = DemoModel()..entity = "value"..entity2 = "value 2";

await isar.writeTxn(() async {
  await isar.model.put(model);
});

I know it is a bit difficult to understand them at first, so let’s break them into chunks.

Before that, let’s add some pre-requisite code to our root method so that this code would be quite easy to understand.

main()

Future<Isar>? isar; // Declaring an instance of Isar DB

void main() async {

  WidgetsFlutterBinding.ensureInitialized();
  final dir = await getApplicationDocumentsDirectory(); // getting the dir path

  isar = Isar.open(
    [BookSchema],  /// we got this from build_runner - generated code
    directory: dir.path,
  );
  runApp(MyApp());
}

Let’s now get back to our previous code with the modified version:

bottomNavigationBar: Padding(
        padding: const EdgeInsets.all(8.0),
        child: ElevatedButton(
          style: ElevatedButton.styleFrom(
            fixedSize: const Size(double.infinity, 50),
          ),
          child: const Text("Save"),
          onPressed: () async {

            var item = await isar;
            final books = Book()..title = _title.text..
            author = _author.text..publishYear = _publishYear.text..isbn = 
            _isbn.text;

            await item!.writeTxn(() async {
              await item.books.put(books);

              ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: 
              Text("Added successfully")));
            });
          },
        ),
      ),

In the above snippet, we did:

- saved the isar db instance in item
- attached the Book Model with form field controllers
- added insertion code

Note: If we forget to declare an instance, we won’t get the generated schema (books).

With this, we’re done with this file. Let’s write some code for fetching all books under all_books.dart.

import 'package:flutter/material.dart';
import 'package:isar/isar.dart';
import 'package:isar_database_overview_ii/add_book.dart';
import 'package:isar_database_overview_ii/database/books.dart';
import 'package:isar_database_overview_ii/main.dart';
import 'package:isar_database_overview_ii/widgets/book_view.dart';

class AllBooks extends StatefulWidget {
  const AllBooks({Key? key}) : super(key: key);

  @override
  State<AllBooks> createState() => _AllBooksState();
}

class _AllBooksState extends State<AllBooks> {

  List<Book> books = [];

  initBook() async {
    var i = await isar;

    i!.txn(() async {
      final b = i.books;
      books = await b.where().findAll();
    });
  }

  @override
  void initState() {
    // TODO: implement initState
    initBook();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("All Books"),
        actions: [
          InkWell(
            onTap: () => Navigator.of(context).push(MaterialPageRoute(
            builder: (context) => const AddBook())),
            child: const Padding(
              padding: EdgeInsets.all(8.0),
              child: CircleAvatar(
                radius: 30,
                backgroundColor: Color(0xffffffff),
                child: Icon(Icons.add),
              ),
            ),
          ),
        ],
      ),
      body: RefreshIndicator(
        onRefresh: () {
          setState(() {
            initBook();
          });
          return Future.value();
        },
        child: books.isEmpty ? const Center(
          child: Text("No Books"),
        ) : ListView.separated(
          itemCount: books.length,
          itemBuilder: (context, index) {

            Book item = books[index];
            return BookView(book: item);
          },
          separatorBuilder: (context, i) => const Divider(),
        ),
      ),
    );
  }
}

In the above snippet, we:

- declared an empty list of Books -> []
- added a function to retrieve  all records from DB
- initialized it under initState()

UI View

all_books.dart

With this, let’s add a delete functionality to our existing widget.

Here’s the code snippet:

Book item = books[index];
return BookView(book: item, onDelete: () async {

  var i = await isar;
  await i!.writeTxn(() async {
  final success = await i.books.delete(index + 1);
  debugPrint('Book deleted: $success');
 });

setState(() {});
},);

This time, let’s have a video preview.

Video Preview

This concludes the blog post.

Hope you enjoyed reading this one!

Wrapping Up

Unlock the Power of Flutter with Isar. An Interesting Guide was all about exploring the excellent insights regarding the Isar Database, vis-à-vis understanding the features and functions. Apart from this, how Flutter App performed effectively, with various code snippets, etc.

However, if you think something was left out or incomplete, you can jot it down in the comments section below. I would love to respond to that.

Meanwhile, you can check out the complete repository by clicking here.

Also, check out my YouTube handle, as it contains tons of content regarding Flutter itself & GitHub.

Link to other blog posts

Also read – Effective Routing with Fluro – A Flutter’s Interesting Prospect

P.S. Thanks for your precious time!

Leave a Comment