Enable Dark Mode!
what-is-the-importance-of-the-isar-database-in-flutter.jpg
By: Syamili K

What Is the Importance of the ISAR Database in Flutter?

Technical Flutter

ISAR is a modern, cross-platform NoSQL database built specifically for Flutter and Dart. Written in Rust and compiled to native binaries, it delivers exceptional performance for local data storage while offering a fully type-safe query API, real-time reactive streams, and zero-boilerplate schema generation. This guide covers ISAR from the ground up with real-world Flutter examples.

Why ISAR Matters for Mobile Developers?

Most Flutter apps eventually need local caching, offline-first logic, or high-frequency writes. ISAR was purpose-built for exactly this. It runs on Android, iOS, macOS, Linux, and Windows with native performance on every platform. Note: Flutter Web is not supported by ISAR.

Mastering ISAR helps you:

  • Build offline-first apps that sync automatically when connectivity is restored.
  • Query large datasets without blocking the UI thread
  • Replace SharedPreferences or Hive wherever complex querying is needed.
  • Eliminate boilerplate with auto-generated schema code via build_runner
  • Auto-update UI using reactive streams when data changes.

Prerequisites

Before diving in, make sure you have:

  • Flutter SDK installed (3.x or later recommended)
  • Dart 3.x compatible project
  • Basic understanding of async/await in Dart
  • Familiarity with code generation (build_runner)

1. Setting Up ISAR in Flutter

Add the required packages to your pubspec.yaml:

dependencies:
  isar: ^3.1.0
  isar_flutter_libs: ^3.1.0  # Bundles native binaries
  path_provider: ^2.0.0
dev_dependencies:
  isar_generator: ^3.1.0
  build_runner: ^2.4.0

Note: isar_flutter_libs bundles native binaries and is required only for Flutter mobile/desktop apps. Pure Dart projects use a different setup and refer to the ISAR documentation for non-Flutter environments.

Install the packages by running:

flutter pub get

After defining your collection classes, generate the schema code:

dart run build_runner build

2. ISAR Collections (The Data Blueprint)

In ISAR, a Collection is the equivalent of a database table. Each collection maps to a Dart class annotated with @collection. ISAR auto-generates all schema metadata and typed query extensions from this annotation.

Common use-case collections in an enterprise Flutter app:

Collection ClassUse Case
EmployeeHR data — name, role, department
AttendanceRecordClock-in/out times, location
LeaveRequestLeave type, status, date range
PayslipSalary breakdown per period
SaleOrderOrder lines, totals, customer ref

Defining a Collection

Each collection must include the part directive pointing to its generated file:

import 'package:isar/isar.dart';
part 'employee.g.dart'; // Auto-generated
@collection
class Employee {
  Id id = Isar.autoIncrement;
  late String name;
  late String role;
  late String department;
  bool isActive = true;
  @Index(type: IndexType.value)
  late String email;
}

This tells ISAR to auto-increment the ID for every new record. Running build_runner generates employee.g.dart with all schema metadata and typed query extensions.

3. ISAR Field Types (The Data Shape)

Fields define the data a collection holds. ISAR supports a rich set of native Dart types with automatic serialization, no manual mapping required.

Supported Field Types

Dart TypeISAR TypeNotes
intLong64-bit integer; use for IDs and counts
doubleDoubleFloating-point; suitable for prices and GPS coords
boolBooltrue / false flags
StringStringUTF-8 text; supports full-text indexing
DateTimeDateTimeStored as UTC microseconds internally
List<T>ListLists of any supported primitive type
Embedded objectObjectNested schema — no foreign key needed

Embedded Objects (Nested Schemas)

ISAR supports embedding sub-objects directly inside a collection. These are ideal for nested address, location, or metadata structures with no join required:

@embedded
class Address {
  late String street;
  late String city;
  late String country;
}
@collection
class Employee {
  Id id = Isar.autoIncrement;
  late String name;
  Address? address; // Embedded — stored inline, no join
}

Indexes

Indexes speed up query performance on frequently filtered or sorted fields. Declare them using the @Index annotation:

@collection
class AttendanceRecord {
  Id id = Isar.autoIncrement;
  @Index(type: IndexType.value)
  late int employeeId;
  @Index(type: IndexType.hash, composite: [CompositeIndex('date')])
late String status; // 'present', 'absent', 'late'
  late DateTime date;
  DateTime? clockIn;
  DateTime? clockOut;
}

Index types in ISAR:

  • IndexType.value: Exact match and range queries (default; best for numbers and enums)
  • IndexType.hash: Equality-only lookups on Strings (faster writes, no range support)
  • IndexType.hashElements: For List fields, indexes each element individually

4. Opening the ISAR Database

ISAR must be opened once at app startup, and the instance reused throughout the app lifecycle. Typically done inside main() or a top-level provider:

import 'package:isar/isar.dart';
import 'package:path_provider/path_provider.dart';
late Isar isar;
Future<void> initIsar() async {
  final dir = await getApplicationDocumentsDirectory();
  isar = await Isar.open(
    [EmployeeSchema, AttendanceRecordSchema], // Register all schemas
    directory: dir.path,
    name: 'appDatabase',
  );
}

Pro Tip: ISAR Inspector: During development, ISAR provides a built-in browser-based Inspector that lets you browse collections, run queries, and inspect records live. It launches automatically in debug mode. So, open http://localhost:8080 in your browser while the app is running on a simulator/emulator.

Call initIsar() before runApp() in your main function:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await initIsar();
  runApp(const MyApp());
}

5. CRUD Operations

All write operations in ISAR must run inside a write transaction. Reads are performed freely without a transaction.

Create / Update (Write)

Future<void> saveEmployee(Employee employee) async {
  await isar.writeTxn(() async {
    await isar.employees.put(employee); // Insert or update by Id
  });
}

Read (Query)

Future<List<Employee>> getActiveEmployees() async {
  return await isar.employees
      .filter()
      .isActiveEqualTo(true)
      .findAll();
}
// Fetch a single record by ID
Future<Employee?> getEmployeeById(int id) async {
  return await isar.employees.get(id);
}

Delete

Future<void> deleteEmployee(int id) async {
  await isar.writeTxn(() async {
    await isar.employees.delete(id);
  });
}
// Delete all inactive employees
Future<void> deleteInactive() async {
  await isar.writeTxn(() async {
    await isar.employees
        .filter()
        .isActiveEqualTo(false)
        .deleteAll();
  });
}

6. Querying Data (Filter Logic)

ISAR provides a type-safe query builder that compiles to native queries; no raw SQL, no string-based parsing, and no runtime errors from typos.

Common Filter Operations

OperationExample MethodDescription
Equality.nameEqualTo('Ali')Exact match on field
Contains.nameContains('ahmed')Substring search
Range.salaryBetween(3000, 8000)Between two values
Greater / Less.salaryGreaterThan(5000)Comparison operators
Multiple (AND).and()Chained AND conditions
Multiple (OR).or()Chained OR conditions
Not.not()Negate the next filter
List contains.tagsAnyEqualTo('flutter')Match inside a List field

Chained Filter Example

Future<List<AttendanceRecord>> getLateArrivals({
  required int employeeId,
  required DateTime from,
  required DateTime to,
}) async {
  return await isar.attendanceRecords
      .filter()
      .employeeIdEqualTo(employeeId)
      .and()
      .statusEqualTo('late')
      .and()
      .dateBetween(from, to)
      .sortByDateDesc()
      .findAll();
}

Sorting and Pagination

// Sort by date descending and paginate (use .where() only when filtering by an index)
final records = await isar.attendanceRecords
    .filter()
    .offset(0)    // Skip N records
    .limit(20)    // Fetch only 20
    .sortByDateDesc()
    .findAll();

7. Reactive Streams (Watchers)

One of ISAR's most powerful Flutter features is its built-in reactive stream support. Watch a collection or a filtered query and rebuild widgets automatically whenever the underlying data changes, perfect for real-time dashboards, attendance trackers, or live order lists.

Watching a Collection

Stream<void> watchEmployees() {
  return isar.employees.watchLazy();
  // Emits whenever the employees collection changes
}

Watching a Filtered Query

Stream<List<AttendanceRecord>> watchTodayAttendance() {
  final today = DateTime.now();
  final start = DateTime(today.year, today.month, today.day);
  final end = start.add(const Duration(days: 1));
  return isar.attendanceRecords
      .filter()
      .dateBetween(start, end)
      .watch(fireImmediately: true); // Emits initial result immediately
}

Using StreamBuilder in Flutter

StreamBuilder<List<AttendanceRecord>>(
  stream: watchTodayAttendance(),
  builder: (context, snapshot) {
    if (!snapshot.hasData) return const CircularProgressIndicator();
    final records = snapshot.data!;
    return ListView.builder(
      itemCount: records.length,
      itemBuilder: (context, index) => ListTile(
        title: Text('Employee: ${records[index].employeeId}'),
        subtitle: Text('Status: ${records[index].status}'),
      ),
    );
  },
)

8. Real-World Example: Offline Attendance Tracker

Below is a complete practical example combining collections, write transactions, queries, and streams. This scenario mirrors the attendance module pattern found in Odoo-integrated Flutter apps.

Collection Definition

part 'attendance_record.g.dart';
@collection
class AttendanceRecord {
  Id id = Isar.autoIncrement;
  @Index(type: IndexType.value)
  late int employeeId;
  late DateTime checkIn;
  DateTime? checkOut;
  @Index(type: IndexType.hash)
  late String status; // 'present', 'late', 'absent'
  bool synced = false; // Track Odoo sync state
}

Repository Layer

class AttendanceRepository {
  final Isar isar;
  AttendanceRepository(this.isar);
  // 1. Clock In
  Future<void> clockIn(int employeeId) async {
    final record = AttendanceRecord()
      ..employeeId = employeeId
      ..checkIn = DateTime.now()
      ..status = 'present'
      ..synced = false;
    await isar.writeTxn(() async {
      await isar.attendanceRecords.put(record);
    });
  }
  // 2. Clock Out
  Future<void> clockOut(int recordId) async {
    await isar.writeTxn(() async {
      final record = await isar.attendanceRecords.get(recordId);
      if (record != null) {
        record.checkOut = DateTime.now();
        await isar.attendanceRecords.put(record);
      }
    });
  }
  // 3. Get unsynced records to push to Odoo
  Future<List<AttendanceRecord>> getUnsynced() async {
    return await isar.attendanceRecords
        .filter()
        .syncedEqualTo(false)
        .findAll();
  }
  // 4. Mark records as synced after successful Odoo push
  Future<void> markSynced(List<int> ids) async {
    await isar.writeTxn(() async {
      final records = await isar.attendanceRecords.getAll(ids);
      for (final r in records) {
        if (r != null) {
          r.synced = true;
          await isar.attendanceRecords.put(r);
        }
      }
    });
  }
  // 5. Watch live attendance stream
  Stream<List<AttendanceRecord>> watchAttendance(int employeeId) {
    return isar.attendanceRecords
        .filter()
        .employeeIdEqualTo(employeeId)
        .watch(fireImmediately: true);
  }
}

This pattern “store locally first, sync later” is the backbone of any robust offline-first Odoo Flutter integration. The synced flag allows a background service to push pending records whenever network connectivity is restored.

9. When to Use Each ISAR Feature

FeaturePurposeMobile Benefit
CollectionsDefine data structureType-safe, auto-generated schema
Embedded ObjectsNested data without joinsSimpler model, faster reads
IndexesSpeed up filtered queriesInstant lookups on large datasets
Write TransactionsAtomic multi-record writesData integrity even on crash
Filter BuilderType-safe query chainingNo raw SQL, no runtime errors
Watchers / StreamsReactive UI updatesAuto-rebuild on data change
synced Flag PatternOffline-first sync trackingReliable Odoo integration

ISAR is the definitive local database for serious Flutter development. Its combination of native performance, expressive queries, reactive streams, and zero boilerplate makes it ideal for enterprise-grade mobile apps.

  • Collections define what data you store.
  • Fields and Embedded Objects define how data is shaped.
  • Indexes make large-scale queries fast.
  • The Filter Builder handles complex query logic type-safely
  • Watchers turn your database into a reactive data source.

Whether you are developing an HR attendance tracker system, a field-service sales application, or any offline-first solution integrated with Odoo, ISAR provides reliable tools for fast and efficient local data management. Although official ISAR development has slowed as of 2026, the isar_community fork continues to offer ongoing updates and improvements.

To read more about How to Integrate RESTful APIs in Flutter Applications, refer to our blog How to Integrate RESTful APIs in Flutter Applications.


If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



Recent Posts

whatsapp_icon
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message