Library / SDK
v-modal/vmodal_sdk_flutter avatar
v-modal/vmodal_sdk_flutter

VModal for Flutter: semantic video search inside a Dart app

V- Modal AI: Visual Video / Image Search - SDK Flutter

1,562 stars7 forksDartMIT

At a glance

What is it?
VModal is an MIT-licensed Flutter package that puts multimodal video and image search behind a typed Dart API, with streamed uploads and per-operation cancellation. It is a thin client for a hosted gateway, and the README is explicit that the SDK never stores your API key.
Who is it for?
Adopt VModal if you are building a Flutter app for Android or iOS whose value comes from searching a video library by meaning, speech, on-screen text or imagery, and you are willing to depend on the VModal gateway for indexing and search. Do not adopt it if you need on-device search, a self-hosted backend, Flutter Web support (the setup prompt tells the agent not to add it), or a package published on pub.dev, since the README only shows a Git dependency.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Dart, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 18, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What VModal for Flutter actually solves

A Flutter app that lets users search their own video library has to solve four problems at once: getting large files off the device without exhausting memory, tracking upload progress and honoring a cancel button, asking a search backend a question in natural language, and rendering whatever comes back. VModal takes the middle two and part of the first. The README describes the split plainly: your app owns the interface, and the SDK handles the VModal gateway, request models, responses, upload streams, progress, and cancellation.

The audience is narrow and identifiable. This is for teams shipping an Android or iOS app in Flutter that already has, or plans to have, a video corpus worth searching. The example directory points at the intended shapes: example/01_full_app for the full flow, example/02_users for account switching, example/03_cctv for surveillance footage, example/05_framebase for a frame-oriented library. If your app has no video to index, the package has nothing to offer you.

The scope is deliberately partial. The README states the SDK never owns your login screen or persists your API key, and that authentication identity is separate from project, collection, and stream organization. That means the package is not an auth layer, not a storage layer, and not a search engine you run yourself. It is a client for a hosted service, and the value it adds is in the Dart surface and the upload plumbing.

Projects, collections and streams: the addressing model

The SDK's organizing idea is a three-level address. You configure a project once with VModal.configure, then derive scopes by collection and stream name, and every content operation happens through a scope. The README's example uses projectId 'food_app' with collectionName 'user_123' and streamName 'favorites'.

The naming rules are enforced client-side and are stricter than they first look. projectId, collectionName, and streamName accept only letters, digits, and underscore. Each is trimmed and limited to 80 characters. Project and collection names cannot contain the reserved __ separator, and their encoded backend value is also limited to 80 characters. The SDK performs that encoding internally, so you never construct the wire value yourself.

The consequence worth internalizing is that collection access is key-scoped. A logical name copied from another account or environment can return HTTP 404 even when the search route is healthy. A 404 from a collection call is therefore not automatically a bug in your URL construction; it may simply mean the key in use has no such collection. The README recommends ScopedSearchOptions(versionLancedb: version) when your application tracks a specific index version, which implies a collection can have more than one index generation and that pinning a version is how you keep results stable while a reindex runs.

Installing the SDK and running a first search

There is no pub.dev entry in the README. It adds the package as a Git dependency on the main branch, and the repository ships a pinned toolchain (the .flutter-version file plus install.sh and build.sh scripts) rather than expecting a global Flutter install.

Add the dependency to your app's pubspec.yaml:

yaml
dependencies:
  vmodal_sdk_flutter:
    git:
      url: https://github.com/v-modal/vmodal_sdk_flutter.git
      ref: main

Then resolve it:

bash
flutter pub get

The README also documents a repository-local flow that uses the pinned toolchain instead of your global Flutter: bash install.sh install, then bash build.sh pub_get, bash build.sh analyze, and bash build.sh test. To list devices with that pinned binary, the README's prompt uses flutter_bin="$(bash install.sh flutter_bin)" followed by "$flutter_bin" devices, and runs the full example with bash run.sh example --device DEVICE_ID. Expect the example to open on a selected Android emulator or device, or an iOS simulator or device on macOS.

Once you have an API key, configure a project and derive a scope. The key comes from an authenticated app at runtime, not from a constant:

dart
import 'package:vmodal_sdk_flutter/vmodal_sdk_flutter.dart';

final keys = MutableApiKeyProvider(runtimeApiKey);
final project = VModal.configure(
  projectId: 'food_app',
  apiKeyProvider: keys,
);
final favorites = project.scope(
  collectionName: 'user_123',
  streamName: 'favorites',
);

A first search checks that the collection exists before querying it, because a missing collection is a normal outcome rather than an exception the backend raises for you:

dart
final collections = await project.listCollections(mode: 'vid_file');
if (!collections.contains('user_123')) {
  throw StateError('No video collection exists for this API key');
}

final results = await favorites.search(
  'the cyclist crossing the bridge at sunset',
  options: const ScopedSearchOptions(
    searchSources: ['image'],
    limit: 20,
  ),
);

print('${results.cntActual} moments found');
for (final moment in results.data) {
  print(moment);
}

What you should see is a count in cntActual and a printable list in results.data. The README notes the response stays typed where the contract is stable and preserves the raw JSON so new server fields remain available immediately, so printing a moment is a reasonable first probe before you write a model.

Uploads that stream, report progress, and can be cancelled

The upload path is where the SDK earns its place. It reads an app-accessible File as a stream and, per the README, does not load the entire video into memory. That single design decision is what makes multi-hundred-megabyte clips viable in a mobile app.

The API returns a task rather than a future for the whole operation, which lets you attach a progress listener and cancel independently:

dart
import 'dart:io';

final task = favorites.upload(
  UploadSource.fromFile(File(videoPath)),
);

final progress = task.progress.listen((value) {
  print('Uploading ${value.percent}%');
});

final uploaded = await task.result;
await progress.cancel();
print('Ready: ${uploaded.fileName}');

The README shows task.cancel() as the hook for a Flutter cancel button, which means cancellation is per-operation rather than per-client. That matters in a picker flow where a user abandons one upload and starts another: cancelling the first task should not disturb the second.

CCTV footage gets a separate path. The README states that for CCTV footage you provide the public filename and offset-aware recording origin in VideoUploadOptions, and that the backend, not the SDK, normalizes the datetime and returns the canonical UTC epoch milliseconds. Metadata tags are repeated independently on the wire. That division is sensible, but it also means timestamp correctness is a server-side contract you cannot fully verify from the client, and the README does not describe what the backend does with an ambiguous or missing origin.

Where the SDK stops being the right tool

The clearest limitation is architectural, not a bug: search and indexing live behind the VModal gateway. Nothing in the README suggests an offline mode, a local index, or a self-hosted deployment. If your product requires search that works without a network round trip, or if your organization will not send video to a third-party service, this package cannot be made to fit, regardless of how well the Dart API suits you.

A second boundary is the platform matrix. The README badges list Android and iOS as supported, and the setup prompt explicitly instructs the agent not to add Flutter Web support. The repository topics include flutter-web, but the documentation's own instruction points the other way, so treat web as unsupported until the SDK reference says otherwise. Desktop is not mentioned at all.

The API key handling is a real operational constraint rather than a limitation of the library. Because the SDK never persists the key and never owns the login screen, you must supply credentials at runtime. The README's prompt warns against hard-coding credentials or persisting an API key, which is correct guidance, but it also means every cold start depends on your own auth flow completing before any VModal call can succeed. If your app has guest sessions with no identity, there is no documented path here.

Finally, the naming rules are unforgiving in a way that will surface as confusing errors. A collection name containing a hyphen, a dot, or a space is rejected, and the README does not document what error you get when validation fails versus when the collection simply does not exist for your key. Budget time for that distinction before you build error UI around it.

How it compares to wiring a search backend yourself

The obvious alternative is to skip the SDK and call a multimodal search service directly with an HTTP client such as Dio or package:http. The difference is not capability but surface area and failure handling. With raw HTTP you own multipart or resumable upload construction, progress accounting, cancellation, retry semantics, and the mapping from JSON to Dart types. VModal packages those into UploadSource, task.progress, task.cancel(), and typed responses that still expose the raw JSON. If your team already has a hardened upload pipeline and only needs one search endpoint, the SDK's upload layer duplicates work you have already done.

A second alternative is a self-hosted or on-device retrieval stack, for example embeddings generated locally and searched with a vector index you operate. That keeps video inside your infrastructure and removes the per-request dependency on a hosted gateway. The trade-off is that you then own model selection, index building, reindexing, and the query path, which is precisely the work VModal is selling. The README's mention of versionLancedb in ScopedSearchOptions hints that the backend's index is LanceDB-based, but the SDK does not let you run that index yourself.

A third path is to build the search experience around transcripts alone, using an ASR service and a text index. It is cheaper and simpler, and for libraries where speech is the only signal it may be sufficient. VModal's stated search sources include ASR and OCR alongside image search, so the difference is that a transcript-only stack cannot answer a query like the README's cyclist example, where the signal is visual rather than spoken.

Maintenance, licence, and what an upgrade costs

The repository is not archived and the last push was on 2026-09-15, so the code is current. That says nothing about release cadence: no releases were retrieved, and the packaging is a Git dependency on main rather than a versioned artifact. Depending on a branch means an upgrade is whatever landed since your last pub get, and your pubspec.lock is the only thing pinning you to a known state. Commit the lock file.

The repository ships CHANGELOG.md, RELEASE_METADATA, release_note.md, and a SOURCE_MANIFEST.sha256 alongside security_check.sh and .gitleaks.toml, which suggests the maintainers treat releases as artifacts with provenance. The README does not document rollback, and there is no deprecation policy in the repository documentation, so plan to read the changelog before moving the ref.

The licence is MIT, which is permissive and imposes no copyleft obligation on your app. The SDK is a client for a hosted service, so the licence covers the Dart code you embed and says nothing about the terms under which the VModal gateway is provided. Those terms are a separate agreement, and the README points to v-modal.com/page/contact.ts for API keys rather than to a pricing or terms page. Read whatever you receive with the key before you ship, because that document, not the MIT licence, governs your use of the search backend.

Editorial conclusion

Adopt VModal if you are building a Flutter app for Android or iOS whose value comes from searching a video library by meaning, speech, on-screen text or imagery, and you are willing to depend on the VModal gateway for indexing and search. Do not adopt it if you need on-device search, a self-hosted backend, Flutter Web support (the setup prompt tells the agent not to add it), or a package published on pub.dev, since the README only shows a Git dependency. Before writing UI, verify three things: that a VModal API key is issued to you through v-modal.com/page/contact.ts, that the collection names your app expects exist for that key in the mode you pass to listCollections, and that the example you plan to copy (01_full_app, 03_cctv, 05_framebase) matches your footage type, because the CCTV path expects a recording origin and public filename that a phone camera flow does not produce.

Frequently asked questions

What is VModal for Flutter and what are its main uses?

It is an MIT-licensed Dart package that adds multimodal video and image search to Android and iOS apps built with Flutter. The README lists semantic search, ASR and OCR search sources, streamed signed uploads with progress, and per-operation cancellation as the capabilities it provides.

What does SDK mean in Flutter?

In this context it means a Dart package you add to pubspec.yaml and import, not a separate runtime. VModal for Flutter is added as a Git dependency and imported with package:vmodal_sdk_flutter/vmodal_sdk_flutter.dart.

What does modal mean in coding?

In this package the word is part of the product name VModal, which covers multimodal video and image search, not a UI modal or dialog. The SDK's own modal-adjacent surface is the search and upload API, and the README points to the Flutter SDK reference for the full contract.

How do you use VModal for Flutter?

Add the package as a Git dependency, run flutter pub get, then call VModal.configure with a projectId and an API key provider. From the returned project you derive a scope with collectionName and streamName, and call search or upload on that scope.

Official sources

  1. Issues
  2. License: MIT
  3. Project website
  4. README
  5. v-modal/vmodal_sdk_flutter on GitHub
Community notes

Community notes