Isar

Isar is "a super fast NoSQL database for Flutter/Dart" developed as a cross-platform database with native performance. Its core engine written in C++ achieves overwhelming read/write performance that surpasses SQLite, communicating directly with native code through Dart FFI (Foreign Function Interface). With type-safe and intuitive query APIs, powerful indexing capabilities, automatic schema migration, and reactive stream monitoring, it establishes a new standard for high-performance local storage solutions in modern Flutter application development.

NoSQLFlutterDartDatabaseHigh PerformanceIndexing

GitHub Overview

isar/isar

Extremely fast, easy to use, and fully async NoSQL database for Flutter

Stars3,861
Watchers53
Forks508
Created:June 10, 2020
Language:Dart
License:Apache License 2.0

Topics

androidcross-platformdartdatabaseflutteriosisarweb

Star History

isar/isar Star History
Data as of: 7/17/2025, 06:57 AM

Library

Isar

Overview

Isar is "a super fast NoSQL database for Flutter/Dart" developed as a cross-platform database with native performance. Its core engine written in C++ achieves overwhelming read/write performance that surpasses SQLite, communicating directly with native code through Dart FFI (Foreign Function Interface). With type-safe and intuitive query APIs, powerful indexing capabilities, automatic schema migration, and reactive stream monitoring, it establishes a new standard for high-performance local storage solutions in modern Flutter application development.

Details

Isar 2025 edition fully supports Flutter 3.16+ and Dart 3.0, providing a next-generation database engine that maximizes the benefits of null safety and solid type systems. Advanced indexing strategies enable super-fast execution of complex queries, allowing performance optimization through composite indexes, partial indexes, and full-text search indexes. With encryption functionality, backup/restore capabilities, schema migration, flexible relationship management through links and backlinks, and powerful query builders, it provides all features necessary for enterprise-level mobile application development.

Key Features

  • Ultra-Fast Performance: Up to 10x faster than SQLite with C++ core
  • Powerful Indexing: Composite, partial, and full-text search index support
  • Type-Safe Queries: Safe data access with compile-time type checking
  • Reactive Streams: Automatic monitoring of data changes and UI updates
  • Schema Migration: Support for both automatic and manual migrations
  • Encryption Support: AES-256 encryption and secure data storage

Pros and Cons

Pros

  • Overwhelming read/write performance far exceeding SQLite
  • Perfect integration with Dart/Flutter ecosystem and type safety
  • High-speed execution of complex queries through powerful indexing features
  • Excellent affinity with reactive programming
  • Improved development efficiency through automatic schema migration
  • Rich query functionality and full-text search support

Cons

  • Relatively new library with limited production track record
  • Dependency on Rust/C++ can make debugging difficult
  • Less documentation and community than Hive
  • Complex relationship expressions require technique
  • Larger binary size, unsuitable for lightweight apps
  • Platform-specific compilation required

Reference Pages

Code Examples

Setup

# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  isar: ^3.1.0+1
  isar_flutter_libs: ^3.1.0+1

dev_dependencies:
  isar_generator: ^3.1.0+1
  build_runner: ^2.4.9
// main.dart - Initialization
import 'package:flutter/material.dart';
import 'package:isar/isar.dart';
import 'package:isar_flutter_libs/isar_flutter_libs.dart';
import 'package:path_provider/path_provider.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // Initialize Isar
  final dir = await getApplicationDocumentsDirectory();
  final isar = await Isar.open(
    [UserSchema, PostSchema, CategorySchema],
    directory: dir.path,
  );

  runApp(MyApp(isar: isar));
}

class MyApp extends StatelessWidget {
  final Isar isar;

  const MyApp({super.key, required this.isar});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Isar Demo',
      home: HomePage(isar: isar),
    );
  }
}

Basic Usage

// Collection definitions
import 'package:isar/isar.dart';

part 'user.g.dart'; // Generated file

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

  @Index()
  late String name;

  @Index(unique: true)
  late String email;

  late int age;

  late DateTime createdAt;

  @Index()
  late bool isActive;

  List<String> hobbies = [];

  @ignore
  String? temporaryData; // Not saved to database

  // Links (relationships)
  final posts = IsarLinks<Post>();
  final profile = IsarLink<UserProfile>();

  // Computed property
  @Index()
  String get nameEmail => '$name $email';

  // Object constructor
  User({
    required this.name,
    required this.email,
    required this.age,
    DateTime? createdAt,
    this.isActive = true,
    this.hobbies = const [],
  }) : createdAt = createdAt ?? DateTime.now();
}

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

  @Index()
  late String title;

  late String content;

  @Index()
  late DateTime publishedAt;

  @Index()
  late bool isPublished;

  List<String> tags = [];

  @Index()
  late int categoryId;

  // Backlink
  @Backlink(to: 'posts')
  final author = IsarLink<User>();
}

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

  @Index(unique: true)
  late String name;

  late String description;

  late DateTime createdAt;
}

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

  late String bio;

  String? website;

  String? location;

  DateTime? birthDate;

  // Backlink
  @Backlink(to: 'profile')
  final user = IsarLink<User>();
}

// Adapter generation command:
// dart run build_runner build

Data Operations

// Database operations class
class UserService {
  final Isar isar;

  UserService(this.isar);

  // Create user
  Future<User> createUser({
    required String name,
    required String email,
    required int age,
    List<String> hobbies = const [],
  }) async {
    final user = User(
      name: name,
      email: email,
      age: age,
      hobbies: hobbies,
    );

    await isar.writeTxn(() async {
      await isar.users.put(user);
    });

    return user;
  }

  // Update user
  Future<void> updateUser(User user) async {
    await isar.writeTxn(() async {
      await isar.users.put(user);
    });
  }

  // Delete user
  Future<bool> deleteUser(int id) async {
    return await isar.writeTxn(() async {
      return await isar.users.delete(id);
    });
  }

  // Get single user
  Future<User?> getUser(int id) async {
    return await isar.users.get(id);
  }

  // Get all users
  Future<List<User>> getAllUsers() async {
    return await isar.users.where().findAll();
  }

  // Search user by email
  Future<User?> getUserByEmail(String email) async {
    return await isar.users.filter().emailEqualTo(email).findFirst();
  }

  // Search by name (partial match)
  Future<List<User>> searchUsersByName(String nameQuery) async {
    return await isar.users
        .filter()
        .nameContains(nameQuery, caseSensitive: false)
        .findAll();
  }

  // Filter by age range
  Future<List<User>> getUsersByAgeRange(int minAge, int maxAge) async {
    return await isar.users
        .filter()
        .ageBetween(minAge, maxAge)
        .findAll();
  }

  // Get active users only
  Future<List<User>> getActiveUsers() async {
    return await isar.users
        .filter()
        .isActiveEqualTo(true)
        .sortByCreatedAtDesc()
        .findAll();
  }

  // Complex query example
  Future<List<User>> getActiveAdultUsersWithHobbies() async {
    return await isar.users
        .filter()
        .isActiveEqualTo(true)
        .and()
        .ageGreaterThan(18)
        .and()
        .hobbiesIsNotEmpty()
        .sortByName()
        .findAll();
  }

  // Pagination
  Future<List<User>> getUsersPaged(int offset, int limit) async {
    return await isar.users
        .where()
        .offset(offset)
        .limit(limit)
        .findAll();
  }

  // Get count
  Future<int> getUserCount() async {
    return await isar.users.count();
  }

  Future<int> getActiveUserCount() async {
    return await isar.users.filter().isActiveEqualTo(true).count();
  }
}

// Usage example
void demonstrateBasicOperations() async {
  final userService = UserService(isar);

  // Create user
  final user1 = await userService.createUser(
    name: 'John Doe',
    email: '[email protected]',
    age: 30,
    hobbies: ['reading', 'programming', 'gaming'],
  );

  print('Created user: ${user1.id}');

  // Create multiple users
  final users = [
    ('Jane Smith', '[email protected]', 25, ['cooking', 'travel']),
    ('Bob Johnson', '[email protected]', 35, ['sports', 'music']),
    ('Alice Brown', '[email protected]', 28, ['photography', 'art']),
  ];

  for (final (name, email, age, hobbies) in users) {
    await userService.createUser(
      name: name,
      email: email,
      age: age,
      hobbies: hobbies,
    );
  }

  // Retrieve data
  final allUsers = await userService.getAllUsers();
  print('Total users: ${allUsers.length}');

  // Search
  final searchResults = await userService.searchUsersByName('john');
  print('Search results: ${searchResults.length}');

  // Age filter
  final youngAdults = await userService.getUsersByAgeRange(20, 30);
  print('Young adults: ${youngAdults.length}');
}

Links and Relationships

// Relationship operations service
class RelationshipService {
  final Isar isar;

  RelationshipService(this.isar);

  // Add profile to user
  Future<void> createUserProfile({
    required User user,
    required String bio,
    String? website,
    String? location,
    DateTime? birthDate,
  }) async {
    final profile = UserProfile()
      ..bio = bio
      ..website = website
      ..location = location
      ..birthDate = birthDate;

    await isar.writeTxn(() async {
      await isar.userProfiles.put(profile);
      user.profile.value = profile;
      await user.profile.save();
    });
  }

  // Create user post
  Future<Post> createUserPost({
    required User user,
    required String title,
    required String content,
    List<String> tags = const [],
    required int categoryId,
    bool isPublished = false,
  }) async {
    final post = Post()
      ..title = title
      ..content = content
      ..tags = tags
      ..categoryId = categoryId
      ..publishedAt = DateTime.now()
      ..isPublished = isPublished;

    await isar.writeTxn(() async {
      await isar.posts.put(post);
      user.posts.add(post);
      await user.posts.save();
    });

    return post;
  }

  // Get user with posts (including relations)
  Future<User?> getUserWithPosts(int userId) async {
    final user = await isar.users.get(userId);
    if (user != null) {
      await user.posts.load();
      await user.profile.load();
    }
    return user;
  }

  // Get post with author
  Future<Post?> getPostWithAuthor(int postId) async {
    final post = await isar.posts.get(postId);
    if (post != null) {
      await post.author.load();
    }
    return post;
  }

  // Get user posts by category
  Future<List<Post>> getUserPostsByCategory(int userId, int categoryId) async {
    final user = await isar.users.get(userId);
    if (user == null) return [];

    await user.posts.load();
    return user.posts.where((post) => post.categoryId == categoryId).toList();
  }

  // Get published posts only
  Future<List<Post>> getPublishedPosts({
    int? limit,
    int? offset,
  }) async {
    var query = isar.posts
        .filter()
        .isPublishedEqualTo(true)
        .sortByPublishedAtDesc();

    if (offset != null) {
      query = query.offset(offset);
    }

    if (limit != null) {
      query = query.limit(limit);
    }

    final posts = await query.findAll();

    // Load author information for each post
    for (final post in posts) {
      await post.author.load();
    }

    return posts;
  }

  // Complex relationship query
  Future<List<User>> getActiveUsersWithRecentPosts(int days) async {
    final cutoffDate = DateTime.now().subtract(Duration(days: days));

    return await isar.users
        .filter()
        .isActiveEqualTo(true)
        .and()
        .posts((q) => q
            .isPublishedEqualTo(true)
            .and()
            .publishedAtGreaterThan(cutoffDate))
        .findAll();
  }

  // Get user statistics
  Future<Map<String, dynamic>> getUserStats(int userId) async {
    final user = await isar.users.get(userId);
    if (user == null) return {};

    await user.posts.load();

    final totalPosts = user.posts.length;
    final publishedPosts = user.posts.where((p) => p.isPublished).length;
    final recentPosts = user.posts
        .where((p) => p.publishedAt.isAfter(
            DateTime.now().subtract(const Duration(days: 30))))
        .length;

    return {
      'totalPosts': totalPosts,
      'publishedPosts': publishedPosts,
      'draftPosts': totalPosts - publishedPosts,
      'recentPosts': recentPosts,
      'joinDate': user.createdAt,
      'hasProfile': user.profile.value != null,
    };
  }
}

Reactive Queries and Streams

// Reactive UI implementation
class UserListWidget extends StatelessWidget {
  final Isar isar;

  const UserListWidget({super.key, required this.isar});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('User List')),
      body: StreamBuilder<List<User>>(
        stream: isar.users
            .filter()
            .isActiveEqualTo(true)
            .watch(fireImmediately: true),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          }

          if (snapshot.hasError) {
            return Center(child: Text('Error: ${snapshot.error}'));
          }

          final users = snapshot.data ?? [];

          if (users.isEmpty) {
            return const Center(child: Text('No active users found'));
          }

          return ListView.builder(
            itemCount: users.length,
            itemBuilder: (context, index) {
              final user = users[index];
              return UserTile(user: user, isar: isar);
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _showAddUserDialog(context),
        child: const Icon(Icons.add),
      ),
    );
  }

  void _showAddUserDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (context) => AddUserDialog(isar: isar),
    );
  }
}

// Individual user tile
class UserTile extends StatelessWidget {
  final User user;
  final Isar isar;

  const UserTile({super.key, required this.user, required this.isar});

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: isar.users.watchObject(user.id, fireImmediately: true),
      builder: (context, snapshot) {
        final currentUser = snapshot.data ?? user;

        return ListTile(
          title: Text(currentUser.name),
          subtitle: Text('${currentUser.age} years old - ${currentUser.email}'),
          trailing: Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              IconButton(
                icon: Icon(
                  currentUser.isActive ? Icons.pause : Icons.play_arrow,
                ),
                onPressed: () => _toggleActiveStatus(currentUser),
              ),
              IconButton(
                icon: const Icon(Icons.delete),
                onPressed: () => _deleteUser(currentUser),
              ),
            ],
          ),
          leading: CircleAvatar(
            child: Text(currentUser.name.substring(0, 1)),
          ),
          onTap: () => _showUserDetails(context, currentUser),
        );
      },
    );
  }

  void _toggleActiveStatus(User user) async {
    await isar.writeTxn(() async {
      user.isActive = !user.isActive;
      await isar.users.put(user);
    });
  }

  void _deleteUser(User user) async {
    await isar.writeTxn(() async {
      await isar.users.delete(user.id);
    });
  }

  void _showUserDetails(BuildContext context, User user) {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => UserDetailPage(user: user, isar: isar),
      ),
    );
  }
}

// User detail page
class UserDetailPage extends StatelessWidget {
  final User user;
  final Isar isar;

  const UserDetailPage({super.key, required this.user, required this.isar});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(user.name)),
      body: StreamBuilder<User?>(
        stream: isar.users.watchObject(user.id, fireImmediately: true),
        builder: (context, snapshot) {
          final currentUser = snapshot.data;
          if (currentUser == null) {
            return const Center(child: Text('User not found'));
          }

          return SingleChildScrollView(
            padding: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                _UserInfoCard(user: currentUser),
                const SizedBox(height: 16),
                _UserPostsSection(user: currentUser, isar: isar),
              ],
            ),
          );
        },
      ),
    );
  }
}

class _UserInfoCard extends StatelessWidget {
  final User user;

  const _UserInfoCard({required this.user});

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              user.name,
              style: Theme.of(context).textTheme.headlineSmall,
            ),
            const SizedBox(height: 8),
            Text('Email: ${user.email}'),
            Text('Age: ${user.age}'),
            Text('Status: ${user.isActive ? "Active" : "Inactive"}'),
            Text('Joined: ${_formatDate(user.createdAt)}'),
            if (user.hobbies.isNotEmpty) ...[
              const SizedBox(height: 8),
              Text('Hobbies: ${user.hobbies.join(", ")}'),
            ],
          ],
        ),
      ),
    );
  }

  String _formatDate(DateTime date) {
    return '${date.year}/${date.month}/${date.day}';
  }
}

class _UserPostsSection extends StatelessWidget {
  final User user;
  final Isar isar;

  const _UserPostsSection({required this.user, required this.isar});

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<List<Post>>(
      stream: isar.posts
          .filter()
          .author((q) => q.idEqualTo(user.id))
          .watch(fireImmediately: true),
      builder: (context, snapshot) {
        final posts = snapshot.data ?? [];

        return Card(
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  'Posts (${posts.length})',
                  style: Theme.of(context).textTheme.titleMedium,
                ),
                const SizedBox(height: 8),
                if (posts.isEmpty)
                  const Text('No posts yet')
                else
                  ...posts.map((post) => ListTile(
                        title: Text(post.title),
                        subtitle: Text(
                          'Published: ${_formatDate(post.publishedAt)}',
                        ),
                        trailing: Icon(
                          post.isPublished
                              ? Icons.visibility
                              : Icons.visibility_off,
                        ),
                      )),
              ],
            ),
          ),
        );
      },
    );
  }

  String _formatDate(DateTime date) {
    return '${date.year}/${date.month}/${date.day}';
  }
}

// Add new user dialog
class AddUserDialog extends StatefulWidget {
  final Isar isar;

  const AddUserDialog({super.key, required this.isar});

  @override
  State<AddUserDialog> createState() => _AddUserDialogState();
}

class _AddUserDialogState extends State<AddUserDialog> {
  final _formKey = GlobalKey<FormState>();
  final _nameController = TextEditingController();
  final _emailController = TextEditingController();
  final _ageController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: const Text('Add New User'),
      content: Form(
        key: _formKey,
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            TextFormField(
              controller: _nameController,
              decoration: const InputDecoration(labelText: 'Name'),
              validator: (value) {
                if (value == null || value.isEmpty) {
                  return 'Please enter a name';
                }
                return null;
              },
            ),
            TextFormField(
              controller: _emailController,
              decoration: const InputDecoration(labelText: 'Email'),
              validator: (value) {
                if (value == null || value.isEmpty) {
                  return 'Please enter an email';
                }
                if (!value.contains('@')) {
                  return 'Please enter a valid email';
                }
                return null;
              },
            ),
            TextFormField(
              controller: _ageController,
              decoration: const InputDecoration(labelText: 'Age'),
              keyboardType: TextInputType.number,
              validator: (value) {
                if (value == null || value.isEmpty) {
                  return 'Please enter an age';
                }
                if (int.tryParse(value) == null) {
                  return 'Please enter a valid number';
                }
                return null;
              },
            ),
          ],
        ),
      ),
      actions: [
        TextButton(
          onPressed: () => Navigator.of(context).pop(),
          child: const Text('Cancel'),
        ),
        ElevatedButton(
          onPressed: _saveUser,
          child: const Text('Save'),
        ),
      ],
    );
  }

  void _saveUser() async {
    if (_formKey.currentState!.validate()) {
      final user = User(
        name: _nameController.text,
        email: _emailController.text,
        age: int.parse(_ageController.text),
      );

      await widget.isar.writeTxn(() async {
        await widget.isar.users.put(user);
      });

      if (mounted) {
        Navigator.of(context).pop();
      }
    }
  }

  @override
  void dispose() {
    _nameController.dispose();
    _emailController.dispose();
    _ageController.dispose();
    super.dispose();
  }
}

Indexing and Performance Optimization

// Advanced indexing strategy
@collection
class OptimizedUser {
  Id id = Isar.autoIncrement;

  // Basic index
  @Index()
  late String name;

  // Unique index
  @Index(unique: true)
  late String email;

  // Composite index (for fast range queries)
  @Index(composite: [CompositeIndex('age')])
  late bool isActive;
  late int age;

  // Case-insensitive index
  @Index(caseSensitive: false)
  late String username;

  // Partial index (conditional index)
  @Index(
    type: IndexType.hash,
    replace: true, // Replace duplicate keys
  )
  String? phoneNumber;

  // Full-text search index
  @Index(type: IndexType.words)
  late String biography;

  late DateTime createdAt;
  late DateTime lastLoginAt;
  List<String> skills = [];
  
  // Computed property for composite index
  @Index(composite: [CompositeIndex('lastLoginAt')])
  bool get isRecentlyActive => 
      lastLoginAt.isAfter(DateTime.now().subtract(const Duration(days: 7)));
}

// Performance-optimized query service
class OptimizedQueryService {
  final Isar isar;

  OptimizedQueryService(this.isar);

  // High-speed search leveraging indexes
  Future<List<OptimizedUser>> findActiveUsersByAge(int minAge, int maxAge) async {
    // Leverage composite index (isActive, age)
    return await isar.optimizedUsers
        .where()
        .isActiveAge(true, between: [minAge, maxAge])
        .findAll();
  }

  // Search leveraging hash index
  Future<OptimizedUser?> findUserByPhone(String phone) async {
    return await isar.optimizedUsers
        .where()
        .phoneNumberEqualTo(phone)
        .findFirst();
  }

  // Full-text search example
  Future<List<OptimizedUser>> searchUsersByBio(String searchTerm) async {
    final words = searchTerm.toLowerCase().split(' ');
    return await isar.optimizedUsers
        .filter()
        .biographyWordsAnyStartWith(words)
        .findAll();
  }

  // Case-insensitive search
  Future<List<OptimizedUser>> findUsersByUsername(String username) async {
    return await isar.optimizedUsers
        .where()
        .usernameEqualTo(username)
        .findAll();
  }

  // Complex composite query (optimized)
  Future<List<OptimizedUser>> getRecentlyActiveSkillfulUsers(
    List<String> requiredSkills,
  ) async {
    return await isar.optimizedUsers
        .filter()
        .isRecentlyActiveEqualTo(true)
        .and()
        .repeat((q) => q.skillsAnyOf(requiredSkills), requiredSkills.length)
        .sortByLastLoginAtDesc()
        .findAll();
  }

  // Performance improvement through batch operations
  Future<void> batchUpdateUsers(List<OptimizedUser> users) async {
    await isar.writeTxn(() async {
      await isar.optimizedUsers.putAll(users);
    });
  }

  // Aggregate query optimization
  Future<Map<String, dynamic>> getUserAnalytics() async {
    final total = await isar.optimizedUsers.count();
    final active = await isar.optimizedUsers
        .filter()
        .isActiveEqualTo(true)
        .count();

    final recentlyActive = await isar.optimizedUsers
        .filter()
        .isRecentlyActiveEqualTo(true)
        .count();

    // Get age distribution
    final ageGroups = <String, int>{};
    const ageRanges = [(18, 25), (26, 35), (36, 45), (46, 60), (61, 100)];
    
    for (final (min, max) in ageRanges) {
      final count = await isar.optimizedUsers
          .filter()
          .ageBetween(min, max)
          .count();
      ageGroups['$min-$max'] = count;
    }

    return {
      'total': total,
      'active': active,
      'recentlyActive': recentlyActive,
      'ageGroups': ageGroups,
      'activePercentage': total > 0 ? (active / total * 100).round() : 0,
    };
  }
}

// Index maintenance and monitoring
class IndexMaintenanceService {
  final Isar isar;

  IndexMaintenanceService(this.isar);

  // Get database statistics
  Future<Map<String, dynamic>> getDatabaseStats() async {
    final stats = <String, dynamic>{};

    // Size of each collection
    stats['collections'] = {
      'users': await isar.optimizedUsers.count(),
      'posts': await isar.posts.count(),
      'categories': await isar.categorys.count(),
    };

    // Database file size (estimated)
    stats['estimatedSize'] = await _getEstimatedSize();

    return stats;
  }

  // Compact database
  Future<void> compactDatabase() async {
    await isar.compact();
  }

  // Rebuild indexes (if necessary)
  Future<void> rebuildIndexes() async {
    // Isar automatically manages indexes,
    // so manual index rebuilding is usually unnecessary
    // Processed automatically during schema changes
  }

  Future<int> _getEstimatedSize() async {
    // In actual implementation, get filesystem size
    // Here we return an estimated value
    final userCount = await isar.optimizedUsers.count();
    final postCount = await isar.posts.count();
    
    // Assume approximately 1KB per user, 2KB per post
    return userCount * 1024 + postCount * 2048;
  }
}

Error Handling

// Robust database operations service
class RobustIsarService {
  final Isar isar;

  RobustIsarService(this.isar);

  // Safe user creation
  Future<Result<User, String>> createUserSafely({
    required String name,
    required String email,
    required int age,
    List<String> hobbies = const [],
  }) async {
    try {
      // Input validation
      final validation = _validateUserInput(name, email, age);
      if (validation != null) {
        return Result.error(validation);
      }

      // Duplicate check
      final existingUser = await isar.users
          .filter()
          .emailEqualTo(email)
          .findFirst();

      if (existingUser != null) {
        return Result.error('User with email $email already exists');
      }

      // Create user
      final user = User(
        name: name,
        email: email,
        age: age,
        hobbies: hobbies,
      );

      await isar.writeTxn(() async {
        await isar.users.put(user);
      });

      return Result.success(user);

    } on IsarError catch (e) {
      return Result.error('Database error: ${e.message}');
    } catch (e) {
      return Result.error('Unexpected error: $e');
    }
  }

  // Safe user update
  Future<Result<User, String>> updateUserSafely(
    int userId,
    Map<String, dynamic> updates,
  ) async {
    try {
      final user = await isar.users.get(userId);
      if (user == null) {
        return Result.error('User not found');
      }

      // Validate and apply update fields
      if (updates.containsKey('name')) {
        final name = updates['name'] as String?;
        if (name == null || name.trim().isEmpty) {
          return Result.error('Name cannot be empty');
        }
        user.name = name.trim();
      }

      if (updates.containsKey('email')) {
        final email = updates['email'] as String?;
        if (email == null || !_isValidEmail(email)) {
          return Result.error('Invalid email format');
        }

        // Duplicate check (excluding self)
        final existingUser = await isar.users
            .filter()
            .emailEqualTo(email)
            .and()
            .not()
            .idEqualTo(userId)
            .findFirst();

        if (existingUser != null) {
          return Result.error('Email already exists');
        }

        user.email = email;
      }

      if (updates.containsKey('age')) {
        final age = updates['age'] as int?;
        if (age == null || age < 0 || age > 150) {
          return Result.error('Invalid age');
        }
        user.age = age;
      }

      if (updates.containsKey('hobbies')) {
        final hobbies = updates['hobbies'] as List<String>?;
        user.hobbies = hobbies ?? [];
      }

      if (updates.containsKey('isActive')) {
        final isActive = updates['isActive'] as bool?;
        user.isActive = isActive ?? true;
      }

      await isar.writeTxn(() async {
        await isar.users.put(user);
      });

      return Result.success(user);

    } on IsarError catch (e) {
      return Result.error('Database error: ${e.message}');
    } catch (e) {
      return Result.error('Unexpected error: $e');
    }
  }

  // Safe batch operations
  Future<Result<BatchResult, String>> batchOperationSafely(
    List<BatchOperation> operations,
  ) async {
    try {
      final results = BatchResult();

      await isar.writeTxn(() async {
        for (final operation in operations) {
          try {
            switch (operation.type) {
              case BatchOperationType.create:
                final user = operation.data as User;
                await isar.users.put(user);
                results.successful.add(operation);
                break;

              case BatchOperationType.update:
                final userId = operation.id!;
                final updates = operation.data as Map<String, dynamic>;
                final user = await isar.users.get(userId);
                
                if (user != null) {
                  _applyUpdates(user, updates);
                  await isar.users.put(user);
                  results.successful.add(operation);
                } else {
                  results.failed.add(FailedOperation(operation, 'User not found'));
                }
                break;

              case BatchOperationType.delete:
                final userId = operation.id!;
                final deleted = await isar.users.delete(userId);
                if (deleted) {
                  results.successful.add(operation);
                } else {
                  results.failed.add(FailedOperation(operation, 'User not found'));
                }
                break;
            }
          } catch (e) {
            results.failed.add(FailedOperation(operation, e.toString()));
          }
        }
      });

      return Result.success(results);

    } on IsarError catch (e) {
      return Result.error('Database transaction failed: ${e.message}');
    } catch (e) {
      return Result.error('Batch operation failed: $e');
    }
  }

  // Database integrity check
  Future<Result<IntegrityReport, String>> checkDatabaseIntegrity() async {
    try {
      final report = IntegrityReport();

      // User data integrity check
      final users = await isar.users.where().findAll();
      for (final user in users) {
        if (user.name.trim().isEmpty) {
          report.errors.add('User ${user.id}: Empty name');
        }

        if (!_isValidEmail(user.email)) {
          report.errors.add('User ${user.id}: Invalid email format');
        }

        if (user.age < 0 || user.age > 150) {
          report.errors.add('User ${user.id}: Invalid age ${user.age}');
        }
      }

      // Duplicate check
      final emailCounts = <String, int>{};
      for (final user in users) {
        emailCounts[user.email] = (emailCounts[user.email] ?? 0) + 1;
      }

      for (final entry in emailCounts.entries) {
        if (entry.value > 1) {
          report.errors.add('Duplicate email: ${entry.key} (${entry.value} occurrences)');
        }
      }

      report.totalUsers = users.length;
      report.checkCompleted = DateTime.now();

      return Result.success(report);

    } catch (e) {
      return Result.error('Integrity check failed: $e');
    }
  }

  // Private helper methods
  String? _validateUserInput(String name, String email, int age) {
    if (name.trim().isEmpty) {
      return 'Name cannot be empty';
    }

    if (!_isValidEmail(email)) {
      return 'Invalid email format';
    }

    if (age < 0 || age > 150) {
      return 'Age must be between 0 and 150';
    }

    return null;
  }

  bool _isValidEmail(String email) {
    return RegExp(r'^[^@]+@[^@]+\.[^@]+$').hasMatch(email);
  }

  void _applyUpdates(User user, Map<String, dynamic> updates) {
    updates.forEach((key, value) {
      switch (key) {
        case 'name':
          user.name = value as String;
          break;
        case 'email':
          user.email = value as String;
          break;
        case 'age':
          user.age = value as int;
          break;
        case 'hobbies':
          user.hobbies = List<String>.from(value as List);
          break;
        case 'isActive':
          user.isActive = value as bool;
          break;
      }
    });
  }
}

// Result type and error handling classes
class Result<T, E> {
  final T? _value;
  final E? _error;

  const Result._(this._value, this._error);

  factory Result.success(T value) => Result._(value, null);
  factory Result.error(E error) => Result._(null, error);

  bool get isSuccess => _value != null;
  bool get isError => _error != null;

  T get value => _value!;
  E get error => _error!;

  R fold<R>(R Function(T value) onSuccess, R Function(E error) onError) {
    if (isSuccess) {
      return onSuccess(value);
    } else {
      return onError(error);
    }
  }
}

// Batch operation related classes
enum BatchOperationType { create, update, delete }

class BatchOperation {
  final BatchOperationType type;
  final int? id;
  final dynamic data;

  BatchOperation.create(this.data) : type = BatchOperationType.create, id = null;
  BatchOperation.update(this.id, this.data) : type = BatchOperationType.update;
  BatchOperation.delete(this.id) : type = BatchOperationType.delete, data = null;
}

class BatchResult {
  final List<BatchOperation> successful = [];
  final List<FailedOperation> failed = [];
}

class FailedOperation {
  final BatchOperation operation;
  final String error;

  FailedOperation(this.operation, this.error);
}

class IntegrityReport {
  int totalUsers = 0;
  final List<String> errors = [];
  DateTime? checkCompleted;

  bool get hasErrors => errors.isNotEmpty;
  int get errorCount => errors.length;
}