dartvex_flutter 0.1.0
dartvex_flutter: ^0.1.0 copied to clipboard
Flutter widgets for Convex — ConvexProvider, QueryBuilder, MutationBuilder, and more. Reactive UI powered by Dartvex.
dartvex_flutter #
Flutter widgets and builders for dartvex.
dartvex_flutter removes the repetitive widget lifecycle code around realtime
Convex subscriptions, mutations, actions, and connection state. The package is
designed around a small runtime interface so a future local-first adapter can
plug into the same widgets without breaking the public API.
Features #
ConvexQuery— reactive query widget with automatic subscription managementConvexMutation/ConvexAction— request builder widgetsConvexImage— display images from Convex file storagePaginatedQueryBuilder— cursor-based pagination with load-moreConvexAuthProvider/ConvexAuthBuilder— auth state widgetsConvexConnectionBuilder/ConvexConnectionIndicator— connection statusConvexOfflineImage/ConvexAssetCache— offline binary asset cachingFakeConvexClient— test helper for unit and widget tests- Runtime-interface based — works with
ConvexClientRuntimeand local-first adapters
Installation #
dependencies:
dartvex: ^0.1.0
dartvex_flutter: ^0.1.0
Provider Setup #
import 'package:dartvex/dartvex.dart';
import 'package:dartvex_flutter/dartvex_flutter.dart';
final client = ConvexClient('https://your-deployment.convex.cloud');
final runtime = ConvexClientRuntime(client);
class AppRoot extends StatelessWidget {
const AppRoot({super.key});
@override
Widget build(BuildContext context) {
return ConvexProvider(
client: runtime,
child: const MyApp(),
);
}
}
Auth Widgets #
final authedClient = client.withAuth(myAuthProvider);
ConvexAuthProvider<MyUser>(
client: authedClient,
child: ConvexAuthBuilder<MyUser>(
builder: (context, state) {
return switch (state) {
AuthLoading<MyUser>() => const CircularProgressIndicator(),
AuthAuthenticated<MyUser>(:final userInfo) => Text(userInfo.name),
AuthUnauthenticated<MyUser>() => const Text('Signed out'),
};
},
),
)
Query Widget #
ConvexQuery<List<Message>>(
query: 'messages:list',
args: const {'channel': 'general'},
decode: (value) => decodeMessages(value),
builder: (context, snapshot) {
if (snapshot.isLoading) return const CircularProgressIndicator();
if (snapshot.hasError) return Text(snapshot.error.toString());
final messages = snapshot.data ?? const <Message>[];
return ListView(
children: [for (final message in messages) Text(message.text)],
);
},
)
Mutation Widget #
ConvexMutation<String>(
mutation: 'messages:send',
builder: (context, mutate, snapshot) {
return FilledButton(
onPressed: snapshot.isLoading
? null
: () => mutate({'author': 'Flutter User', 'text': 'Hello'}),
child: Text(snapshot.isLoading ? 'Sending...' : 'Send'),
);
},
)
Pagination #
PaginatedQueryBuilder<Message>(
query: 'messages:list',
args: const {'status': 'active'},
pageSize: 20,
fromJson: Message.fromJson,
builder: (context, items, loadMore, status) {
return ListView.builder(
itemCount: items.length + (status == PaginationStatus.allLoaded ? 0 : 1),
itemBuilder: (_, i) => i < items.length
? MessageTile(items[i])
: TextButton(onPressed: loadMore, child: const Text('Load more')),
);
},
)
Testing #
final client = FakeConvexClient()
..whenQuery('messages:list', returns: [mockMessage1, mockMessage2])
..whenMutation('messages:send', returns: {'id': 'xxx'});
await tester.pumpWidget(
ConvexProvider(client: client, child: MyApp()),
);
API Overview #
| Widget | Description |
|---|---|
ConvexProvider |
Provides client to widget tree via InheritedWidget |
ConvexQueryBuilder |
Reactive query with automatic re-rendering |
ConvexMutationBuilder |
Mutation trigger with loading/error state |
ConvexActionBuilder |
Action trigger with loading/error state |
PaginatedQueryBuilder |
Cursor-based paginated query |
ConvexAuthBuilder |
Renders based on auth state |
ConvexConnectionBuilder |
Renders based on connection state |
ConvexImage |
Image from Convex storage |
ConvexOfflineImage |
Offline-capable image with caching |
FakeConvexClient |
Test double for widget tests |
Full Documentation #
See the Dartvex monorepo for full documentation and examples.