dexie_web
A self-contained, zero-configuration Dexie.js (IndexedDB) wrapper for Flutter Web.
dexie_web eliminates the friction of using IndexedDB in Flutter Web. It bundles the Dexie JS library directly into the package assets and automatically injects it at run-time with Subresource Integrity (SRI) enforced. No external CDN dependencies, no manual <script> tags in your index.html, and fully WASM-ready using modern dart:js_interop.
For avoidance of doubt, dexie_web is web-only at run-time. It can be imported on non-web platforms for shared code, but calling its APIs off-web throws UnsupportedError.
Features
- Zero Config: Automatically loads the bundled Dexie.js script when you open a database.
- Offline-First & Secure: Does not rely on external networks. Loaded strictly from local package assets with built-in SRI hash validation to prevent tampering.
- Type-Safe & Dart-First: Wrap IndexedDB operations in a familiar Dart API (
open,put,get,getAll,whereEquals). - Cross-Platform Stubs: Safely import and compile on iOS/Android/Desktop (methods will throw an
UnsupportedErrorif invoked off-web, but compilation won't break). - Native Dates: Dart
DateTimeobjects are automatically serialized to native JavaScriptDateobjects for accurate IndexedDB sorting and querying.
Installation
Add the package to your pubspec.yaml:
dependencies:
dexie_web: ^0.1.1
Or install via the command line:
flutter pub add dexie_web
Usage
Using dexie_web is straightforward. Instantiate a DexieDatabase, define your schema, and start reading/writing data.
import 'package:dexie_web/dexie_web.dart';
Future<void> main() async {
// 1. Initialize the database instance
final db = DexieDatabase('myAppDb');
// 2. Open the database and define your schema.
// The Dexie script is automatically injected into the DOM here if it isn't already.
await db.open({
'friends': '++id, name, age, birthday',
'todos': '++id, title, completed',
});
// 3. Write data
await db.put('friends', {
'name': 'Alice',
'age': 30,
'birthday': DateTime.utc(1996, 1, 1),
});
// 4. Read data
final allFriends = await db.getAll<Map<String, dynamic>>('friends');
// 5. Query data
final adults = await db.whereEquals<Map<String, dynamic>>('friends', 'age', 18);
print('friends: ${allFriends.length}, adults: ${adults.length}');
db.close();
}
Fluent API (Dexie-style)
Alongside helper methods, dexie_web exposes a fluent API for Table, WhereClause, and Collection.
final friends = db.table<Map<String, dynamic>, dynamic, Map<String, dynamic>>(
'friends',
);
await friends.bulkPut([
{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 25},
]);
final adults = await friends
.whereIndex('age')
.aboveOrEqual(18)
.reverse()
.toList();
Parity Coverage (Dexie 4.3 Runtime Surfaces)
dexie_web covers the primary runtime query/mutation APIs for:
Table:get,where,filter,count,offset,limit,each,toArray,toCollection,orderBy,reverse,mapToClass,add,update,upsert,put,delete,clear,bulkGet,bulkAdd,bulkPut,bulkUpdate,bulkDeleteWhereClause:above,aboveOrEqual,anyOf,anyOfIgnoreCase,below,belowOrEqual,between,equals,equalsIgnoreCase,inAnyRange,startsWith,startsWithAnyOf,startsWithIgnoreCase,startsWithAnyOfIgnoreCase,noneOf,notEqualCollection:and,clone,count,distinct,each,eachKey,eachPrimaryKey,eachUniqueKey,filter,first,firstKey,keys,primaryKeys,last,lastKey,limit,offset,or,raw,reverse,sortBy,toArray,uniqueKeys,until,delete,modify
Aliases:
toList()->toArray()remove()->delete()removeAll()->clear()
Deterministic Validation Errors
dexie_web validates table/index names from your declared schema. Invalid table/index operations throw StateError immediately, rather than relying on browser-specific IndexedDB error timing.
Advanced: Preloading & Loader Policies
By default, dexie_web automatically injects the bundled dexie.min.js file the first time you call db.open(). If you'd like to preload the script earlier in your app's lifecycle to speed up initial database access, you can call:
await ensureDexieInitialized();
Customizing the Load Policy
If you need to control how the script is loaded (for example, if you prefer to manually provide your own Dexie script via a CDN in your index.html), you can change the global load policy before initialization:
// Default: Strictly loads the packaged asset and throws if an unmanaged global Dexie exists.
setDefaultDexieLoadPolicy(DexieLoadPolicy.strictPackage);
// Option 1: Expects a global `Dexie` object to already exist (e.g., loaded by your index.html).
setDefaultDexieLoadPolicy(DexieLoadPolicy.strictGlobal);
// Option 2: Uses a global `Dexie` if available, otherwise falls back to injecting the package asset.
setDefaultDexieLoadPolicy(DexieLoadPolicy.preferGlobalFallbackPackage);
You can also override the policy per initialization call:
await ensureDexieInitialized(
policy: DexieLoadPolicy.preferGlobalFallbackPackage,
);