Dart packages for Convex — pure-Dart realtime sync, type-safe codegen, Flutter widgets, and offline support.
- Pure-Dart core — the
dartvexclient has no Rust FFI or Flutter dependency; companion packages add Flutter/native integrations where noted. - Type-safe codegen — Generate Dart bindings from your Convex schema. Catch errors at compile time.
- Flutter widgets —
ConvexProvider,ConvexQuery,ConvexMutationand more for reactive UI. - Offline capable — SQLite query cache and mutation queue with optimistic updates.
- Multi-platform — iOS, Android, web, macOS, Linux, Windows.
Web support covers the core realtime client, auth, storage URL resolution, and
Flutter query/mutation widgets. Disk-backed file/image cache and offline image
fallback are native-only because they rely on dart:io and local filesystem
storage.
import 'package:dartvex/dartvex.dart';
void main() async {
final client = ConvexClient('https://your-deployment.convex.cloud');
// Subscribe to a query
final sub = client.subscribe('messages:list', {});
sub.stream.listen((result) {
if (result case QuerySuccess(:final value)) {
print('Messages: $value');
}
});
// Run a mutation
await client.mutate('messages:send', {'body': 'Hello from Dart!'});
}| Package | Description | Version |
|---|---|---|
dartvex |
Core client — WebSocket sync, subscriptions, auth | 0.2.0 |
dartvex_flutter |
Flutter widgets — Provider, Query, Mutation | 0.2.0 |
dartvex_codegen |
CLI code generator — type-safe Dart bindings from schema | 0.2.0 |
dartvex_local |
Offline support — SQLite cache, mutation queue | 0.2.0 |
dartvex_auth_better |
Better Auth adapter | 0.2.0 |
This repo ships Agent Skills that teach AI coding agents (Claude Code, Cursor, Copilot, Codex, …) how to build apps with Dartvex:
npx skills add AndreFrelicot/dartvex| Skill | Use it to |
|---|---|
dartvex |
Route any Dartvex task to the right skill |
dartvex-quickstart |
Connect a Dart/Flutter app to Convex |
dartvex-generate-bindings |
Generate a type-safe API with dartvex_codegen |
dartvex-setup-auth |
Better Auth login, session restore, manual JWT auth |
dartvex-build-realtime-ui |
Live queries, optimistic mutations, pagination widgets |
dartvex-setup-offline |
SQLite cache and offline mutation queue |
dartvex-upload-files |
Convex file storage and cached image widgets |
dartvex-test-with-fakes |
Widget tests with FakeConvexClient |
They pair with the official get-convex/agent-skills
(backend authoring) and flutter/skills
(general Flutter UI).
┌─────────────────────────────────────────────────────────────────────────────┐
│ Your App │
├────────────────────────┬──────────────────────┬─────────────────────────────┤
│ dartvex_flutter │ dartvex_local │ dartvex_auth_* │
│ (widgets) │ (offline) │ (better auth adapters) │
├────────────────────────┴──────────────────────┴─────────────────────────────┤
│ dartvex │
│ (core client + WebSocket sync) │
├─────────────────────────────────────────────────────────────────────────────┤
│ Convex Backend │
└─────────────────────────────────────────────────────────────────────────────┘
dartvex_codegen
(generates typed bindings from schema)
# pubspec.yaml
dependencies:
dartvex: ^0.2.0
dartvex_flutter: ^0.2.0 # If using Flutter
dev_dependencies:
dartvex_codegen: ^0.2.0 # For code generationimport 'package:dartvex/dartvex.dart';
import 'package:dartvex_flutter/dartvex_flutter.dart';
final client = ConvexClient('https://your-deployment.convex.cloud');
final runtime = ConvexClientRuntime(client);
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ConvexProvider(
client: runtime,
child: MaterialApp(home: HomePage()),
);
}
}dart run dartvex_codegen generate \
--spec-file path/to/function_spec.json \
--output lib/convex_api/ConvexQuery<List<Message>>(
query: 'messages:list',
args: const {},
decode: (v) => [
for (final item in v as List) Message.fromJson(item as Map<String, dynamic>),
],
builder: (context, snapshot) {
if (snapshot.isLoading) return const CircularProgressIndicator();
if (snapshot.hasError) return Text('Error: ${snapshot.error}');
final messages = snapshot.data ?? const <Message>[];
return ListView(children: messages.map(MessageTile.new).toList());
},
)final sub = client.subscribe('tasks:list', {'projectId': 'abc123'});
sub.stream.listen((result) {
if (result case QuerySuccess(:final value)) {
print('Tasks: $value');
}
});await client.mutate('tasks:create', {
'title': 'Ship dartvex',
'projectId': 'abc123',
});final result = await client.action('ai:summarize', {
'documentId': 'doc456',
});final authClient = BetterAuthClient(
baseUrl: 'https://your-deployment.convex.cloud',
);
final provider = ConvexBetterAuthProvider(client: authClient)
..email = 'user@example.com'
..password = 'securePassword';
final client = ConvexClient('https://your-deployment.convex.cloud');
final authedClient = client.withAuth(provider);
await authedClient.login();import 'package:dartvex_local/dartvex_local.dart';
final remoteClient = ConvexClient('https://your-deployment.convex.cloud');
final store = await SqliteLocalStore.open('my_app.sqlite');
final localClient = await ConvexLocalClient.open(
client: remoteClient,
config: LocalClientConfig(
cacheStorage: store,
queueStorage: store,
disposeRemoteClient: true,
),
);
// Queries served from cache when offline
// Mutations queued and synced when back onlineThe local-first query cache and mutation queue use SQLite and are intended for native targets. For files on web, resolve signed Convex storage URLs and render them directly; disk-backed file cache/offline image fallback is not supported on web in this release.
See CONTRIBUTING.md for guidelines.
MIT License - see LICENSE for details.
