Isar Plus

Limitations

Platform-specific limitations and constraints

Limitations

Isar Plus works across mobile, desktop, and web platforms. Each platform has different characteristics and limitations.

Platform Overview

Platforms: iOS, Android, macOS, Linux, Windows

Limitations

  • String prefix where-clauses limited to first 1024 bytes
  • Maximum object size: 16MB

VM platforms have minimal limitations and full feature support!

Platforms: Chrome, Firefox, Safari, Edge

Storage Backends

Zap

OPFS (Modern)

Chrome, Edge (v102+)

  • Fast, native-like performance
  • Persistent storage
  • Full SQLite compatibility
Database

IndexedDB (Fallback)

Safari, Firefox, older browsers

  • Some limitations apply
  • Slower than OPFS
  • Good compatibility

Limitations

// ❌ Async operations not supported on web (require isolates)
await isar.writeAsync((isar) => isar.users.put(user));
await isar.users.getAsync(1);

// ❌ Watchers not supported on web
isar.users.watchLazy().listen((_) {});
isar.users.watchObject(1).listen((user) {});

// ✅ Use sync APIs on web
isar.write((isar) => isar.users.put(user));
final user = isar.users.get(1);

Async operations require isolates which are not available on web. Use synchronous APIs instead!

General Limitations

  • No support for very large transactions (keep under 100MB)
  • Links are lazy-loaded (small overhead on first access)
  • Embedded objects cannot contain other embedded objects (single level only)

VM Limitations

String Indexing

Only the first 1024 bytes of a string are indexed:

@collection
class Article {
  Id? id;

  @Index()
  late String title; // ✅ Usually fine

  @Index()
  late String content; // ⚠️ Only first 1024 bytes indexed
}

For full-text search on long strings, use the multi-entry index pattern.

Object Size

Maximum object size is 16MB:

@collection
class LargeDocument {
  Id? id;

  // ⚠️ Be careful with large data
  late List<int> fileData;
}

Store large files outside of Isar and keep only references in the database.

Web Limitations

API Compatibility

// ❌ Async transactions (require isolates)
await isar.readAsync((isar) => isar.users.get(1));
await isar.writeAsync((isar) => isar.users.put(user));
await isar.users.getAsync(1);
await isar.users.getAllAsync([1, 2, 3]);

// ❌ Watchers (require native isolates)
isar.users.watchLazy().listen((_) {});
isar.users.watchObject(1).listen((user) {});
isar.users.watchDetailed().listen((change) {});

// ❌ Text operations
Isar.splitWords('hello world');
isar.users.where().nameMatches('pattern*');
// ✅ Synchronous operations (use these on web)
final user = isar.users.get(1);
final users = isar.users.getAll([1, 2, 3]);

isar.write((isar) => isar.users.put(user));
isar.read((isar) => isar.users.where().findAll());

// ✅ Filters and queries
isar.users.where()
  .nameContains('John')
  .findAll();

// ✅ Where clauses
isar.users.where()
  .nameEqualTo('John')
  .findAll();

IndexedDB Fallback Limitations

When OPFS is not available, IndexedDB fallback has additional limitations:

// ⚠️ Return values may differ
await isar.writeAsync((isar) async {
  final count = isar.users.deleteAll([1, 2, 3]);
  // OPFS: returns exact count
  // IndexedDB: may return different value
});

// ⚠️ Auto-increment not reset by clear()
await isar.writeAsync((isar) async {
  isar.users.clear();
});
// On OPFS: next ID is 1
// On IndexedDB: next ID continues from before

Schema Migrations

// ⚠️ Less strict validation on web
@collection
class User {
  Id? id;
  
  late String name;
  
  // Changing type is less validated
  late int age; // was String before
}

Double-check schema changes on web during releases!

Text Operations

// ❌ Not available on web
final words = Isar.splitWords('Hello World');

// ❌ Wildcard matching not available
await isar.users.where()
  .nameMatches('John*')
  .findAll();

// ✅ Use alternative patterns
await isar.users.where()
  .nameStartsWith('John')
  .findAll();

await isar.users.where()
  .nameContains('John')
  .findAll();

Workarounds

Large Strings

// Instead of indexing entire content
@collection
class Article {
  Id? id;

  late String title;

  late String content;

  // Create searchable keywords
  @Index(type: IndexType.value)
  late List<String> keywords;
}

// Generate keywords before saving
final article = Article()
  ..title = 'My Article'
  ..content = 'Long content...'
  ..keywords = generateKeywords(content);

File Storage

@collection
class Document {
  Id? id;

  late String name;

  // Store file path, not content
  late String filePath;

  late int fileSize;
}

// Store actual file separately
final file = File('path/to/file.pdf');
final document = Document()
  ..name = 'Document'
  ..filePath = file.path
  ..fileSize = await file.length();

Web Async Pattern

// Platform-specific code for async operations
class IsarHelper {
  static Future<User?> getUser(Isar isar, int id) async {
    if (kIsWeb) {
      // Web: use sync API
      return isar.users.get(id);
    } else {
      // Native: can use async for background processing
      return isar.users.getAsync(id);
    }
  }
}
@collection
class Product {
  Id? id;

  late String name;

  // Store lowercase for case-insensitive search
  @Index(caseSensitive: false)
  late String searchName;
}

// Before saving
product.name = 'iPhone 15 Pro';
product.searchName = product.name.toLowerCase();

// Query works on all platforms
await isar.products
  .where()
  .searchNameContains('iphone')
  .findAll();

Platform Detection

import 'package:flutter/foundation.dart';

void configureIsar() {
  if (kIsWeb) {
    print('Running on web with limitations');
    // Use async-only APIs
  } else {
    print('Running on native platform');
    // Can use sync APIs for performance
  }
}

Browser Support

BrowserVersionStoragePerformance
Chrome102+OPFSExcellent
Edge102+OPFSExcellent
SafariAllIndexedDBGood
FirefoxAllIndexedDBGood

OPFS provides near-native performance on Chromium browsers!

Best Practices

  1. Write Cross-Platform Code

    // ✅ Sync works everywhere
    isar.write((isar) => isar.users.put(user));
    final user = isar.users.get(1);
    
    // ⚠️ Async only works on native (mobile/desktop)
    // Throws UnsupportedError on web
    await isar.writeAsync((isar) => isar.users.put(user));
    // Platform-aware pattern
    if (kIsWeb) {
      isar.write((isar) => isar.users.put(user));
    } else {
      await isar.writeAsync((isar) => isar.users.put(user));
    }
  2. Test on Target Platforms

    • Test web builds in different browsers
    • Verify performance on mobile devices
    • Check desktop builds
  3. Handle Platform Differences

    if (kIsWeb) {
      // Web-specific logic
    } else {
      // Native-specific logic
    }
  4. Keep Objects Small

    // Store references, not large data
    @collection
    class Photo {
      Id? id;
      late String url; // Not the image bytes
      late String thumbnailUrl;
    }

Migration Notes

When migrating from Isar 3:

Isar Plus uses SQLite instead of LMDB. Some behaviors may differ slightly.

See the Migration Guide for details.

Next Steps

Last Update