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.
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
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
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.
Also read – Effective Routing with Fluro – A Flutter’s Interesting Prospect
P.S. Thanks for your precious time!