Connect FlutterFlow to JFrog Artifactory by creating an API Group at your Artifactory base URL (must end in /artifactory) with an X-JFrog-Art-Api header for API keys or an Authorization Bearer header for access tokens. The standout capability is AQL search — posted to /api/search/aql as plain text with Content-Type: text/plain, not JSON, which is the single most common FlutterFlow-specific mistake.
| Fact | Value |
|---|---|
| Tool | JFrog Artifactory |
| Category | DevOps & Tools |
| Method | FlutterFlow API Call |
| Difficulty | Intermediate |
| Time required | 30 minutes |
| Last updated | July 2026 |
Building a Mobile Artifactory DevOps Dashboard with FlutterFlow
JFrog Artifactory is where your organization's compiled artifacts, Docker images, npm packages, Maven JARs, and other binary outputs live. It is not app data — Artifactory is part of your build and release pipeline, not your application's database. With that context, the honest FlutterFlow use-case is a lightweight mobile DevOps dashboard: a phone-friendly view for on-call engineers to check repository storage usage, search for artifact versions, and verify which builds have been promoted to production — all from their phones without opening a browser or a VPN-connected laptop.
FlutterFlow consumes Artifactory's REST API as an API Group, and two paths serve most use-cases: standard REST calls for repository metadata (`GET /api/repositories`, `GET /api/storage/{repo}`) and **AQL (Artifactory Query Language)** for multi-condition artifact search. AQL is Artifactory's proprietary query language — it lets you ask questions like 'find all Docker images in the releases repository modified in the last 7 days with a specific property value.' The technical quirk that trips every FlutterFlow developer: AQL is sent as a POST with `Content-Type: text/plain` and a raw text body, not as JSON. Sending AQL as `application/json` returns a 400 'AQL parsing error' response every time.
JFrog Cloud offers a free tier with 2 GB of storage and 10 GB of monthly transfer, with Pro plans starting from approximately $150/month — verify current pricing at jfrog.com because it changes. Self-hosted Artifactory is available under separate licensing terms. Importantly, self-hosted Artifactory behind a VPN is only reachable from a FlutterFlow mobile app that's connected to the corporate network or VPN — a published web FlutterFlow app can't reach an internal Artifactory instance unless it's exposed to the internet.
Integration method
FlutterFlow connects to JFrog Artifactory through a REST API Group pointing at your Artifactory instance's base URL (Cloud at yourcompany.jfrog.io/artifactory, or self-hosted). Standard repository and artifact queries use JSON responses with X-JFrog-Art-Api or Bearer token authentication. The AQL (Artifactory Query Language) search endpoint is a special case: it receives a plain-text POST body with Content-Type: text/plain, not JSON — sending AQL as JSON returns a 400 parse error.
Prerequisites
- A JFrog Artifactory instance — either JFrog Cloud (yourcompany.jfrog.io) or a self-hosted instance reachable from the internet (or from the mobile network you'll use)
- A read-only Artifactory access token (the modern auth method, generated in JFrog Platform → Administration → User Management → Access Tokens) or an API key (older method, available in user profile settings)
- Your Artifactory base URL, which must end in '/artifactory' — for JFrog Cloud: https://yourcompany.jfrog.io/artifactory; for self-hosted: https://artifactory.yourcompany.com/artifactory
- A FlutterFlow account (any plan — API Calls work on all plans)
- Basic familiarity with FlutterFlow's API Calls panel and JSON Path
Step-by-step guide
Generate a read-only Artifactory access token
Before opening FlutterFlow, generate the credential you'll use for API calls. Artifactory supports two authentication methods: **Access Tokens (recommended, modern path):** Log into your JFrog Platform (yourcompany.jfrog.io for Cloud) → go to Administration → User Management → Access Tokens → Generate Token. Set a descriptive name like 'FlutterFlow Dashboard Read-Only', set an expiration date, and assign a scope of 'Reader' or read-only. This generates a long-lived token you'll use with the `Authorization: Bearer` header. **API Keys (legacy path):** In your JFrog Platform, click your profile avatar → Edit Profile → Unlock the API Key section → Generate. API keys are scoped to your user account's permissions and use the `X-JFrog-Art-Api` header. Note: JFrog is gradually deprecating API keys in favor of access tokens — prefer tokens for new setups. Critical security note: a full-access Artifactory token or API key can delete artifacts, modify repositories, and change permissions. For a phone dashboard with read-only needs, always generate a read-only credential. A leaked read-only token allows someone to see your artifact inventory — serious but recoverable. A leaked admin token allows deleting your entire artifact storage. Copy the generated token or API key. For tokens, you'll only see the full value once — copy it immediately.
Pro tip: JFrog Cloud access tokens and API keys are separate from your JFrog account password. Generating a token does not change your login credentials. Always prefer access tokens over API keys for new FlutterFlow integrations.
Expected result: You have a read-only Artifactory access token or API key copied and ready to add as a FlutterFlow App Constant.
Create the Artifactory API Group with the correct base URL
Open FlutterFlow and click API Calls in the left navigation panel → + Add → Create API Group. The base URL is critical: it must end in `/artifactory`. For JFrog Cloud this is `https://yourcompany.jfrog.io/artifactory`; for self-hosted it is `https://artifactory.yourcompany.com/artifactory` (or whatever hostname your instance uses). If the base URL is missing the `/artifactory` segment, every API call will return 404 — this is the second most common setup mistake. Set the Group Name to `Artifactory`. In the Headers section, add your auth header: **If using an access token (recommended):** - Header Name: `Authorization` - Header Value: `Bearer [App Constant: ARTIFACTORY_TOKEN]` **If using an API key (legacy):** - Header Name: `X-JFrog-Art-Api` - Header Value: `[App Constant: ARTIFACTORY_API_KEY]` Note: the header name `X-JFrog-Art-Api` is case-sensitive — Artifactory is strict about the casing. Using `x-jfrog-art-api` (all lowercase) or `X-Jfrog-Art-Api` (wrong capitalization) will return a 401 even with a valid API key. Store the credential as an App Constant: go to Settings & Integrations → App Settings → App Constants → add a constant named `ARTIFACTORY_TOKEN` (or `ARTIFACTORY_API_KEY`) and paste the value. Also add a `Content-Type: application/json` header for standard REST calls — you'll override this for AQL calls in a separate API Call configuration. Click Save.
1{2 "api_group_name": "Artifactory",3 "base_url": "https://yourcompany.jfrog.io/artifactory",4 "headers": [5 {6 "name": "Authorization",7 "value": "Bearer [App Constant: ARTIFACTORY_TOKEN]"8 },9 {10 "name": "Content-Type",11 "value": "application/json"12 }13 ]14}Pro tip: Double-check the base URL ends exactly in '/artifactory' — many Artifactory instances run at that path. Omitting it causes 404s on every call, which can look confusing because the server is reachable but returning 'not found' for a correctly-spelled endpoint path.
Expected result: The Artifactory API Group appears in the API Calls panel with the base URL ending in '/artifactory' and the correct auth header stored via App Constant.
Add a GET Repositories API Call and bind to a ListView
With the API Group configured, add your first API Call to list repositories. Inside the Artifactory API Group, click + Add API Call: - **Call Name:** `GetRepositories` - **Method:** GET - **Endpoint (relative):** `api/repositories` This calls `https://yourcompany.jfrog.io/artifactory/api/repositories` and returns a JSON array of repository objects. In the Response & Test tab, click Test. If you see a 200 response, great — paste or use the live response to generate JSON Paths. The response is a flat array of objects, so JSON Path expressions are straightforward: - `repo_key` → `$[*].key` (repository identifier, e.g., 'docker-local') - `repo_type` → `$[*].type` (LOCAL, REMOTE, or VIRTUAL) - `repo_package_type` → `$[*].packageType` (Docker, Maven, npm, etc.) - `repo_url` → `$[*].url` (for remote repositories) In your FlutterFlow screen, add a ListView → Backend Query → API Call → select Artifactory / GetRepositories. Inside the list item, bind Text widgets to the `repo_key` and `repo_type` JSON Path values. Add a color-coded Tag widget based on `repo_type` (LOCAL = blue, REMOTE = green, VIRTUAL = orange) for quick visual identification. For a more detailed view, add a second API Call `GetStorageInfo` with endpoint `api/storageinfo` — this returns overall storage statistics including fileCount, usedSpace, and per-repository breakdown in a `repositoriesSummaryList` array.
1{2 "call_name": "GetRepositories",3 "method": "GET",4 "endpoint": "api/repositories",5 "json_paths": [6 { "name": "repo_key", "path": "$[*].key" },7 { "name": "repo_type", "path": "$[*].type" },8 { "name": "repo_package_type", "path": "$[*].packageType" }9 ]10}Pro tip: If the Test button returns a 403 Forbidden on /api/repositories, your read-only token may not have permission to list all repositories. Ask your Artifactory admin to grant the 'Manage Repositories' read permission or scope the token to specific repositories.
Expected result: The GetRepositories call returns a 200 with a JSON array, and the ListView displays repository names and types correctly.
Add an AQL Search call with Content-Type: text/plain
AQL (Artifactory Query Language) is Artifactory's most powerful search capability — it lets you find artifacts by name, property, date, repository, and more in a single query. However, it has a critical non-standard requirement that breaks most FlutterFlow setups: **AQL must be sent as a POST with `Content-Type: text/plain` and a raw text body — not JSON.** Sending an AQL query wrapped in a JSON object (`{"query": "items.find(...)"}`) returns a 400 'AQL parsing error'. The raw AQL text itself must be the entire POST body. In FlutterFlow, this requires a separate API Call with overriding headers. Inside the Artifactory API Group, click + Add API Call: - **Call Name:** `SearchAQL` - **Method:** POST - **Endpoint:** `api/search/aql` In this API Call's Headers tab (not the Group headers), add a call-level override: - `Content-Type` → `text/plain` This overrides the Group-level `application/json` for this specific call only. In the Body tab, select Raw/Text body type and add a Variable: `aql_query` (type String). Reference it as the entire body content: `[aql_query]`. In the Variables tab, add a default value for testing: ``` items.find({"repo":{"$eq":"docker-local"}}).include("name","repo","created").sort({"$desc":["created"]}).limit(20) ``` In the Response & Test tab, click Test with the default AQL query. A successful response returns a JSON object with a `results` array containing matching artifact objects. Add JSON Paths: - `artifacts` → `$.results` - `artifact_name` → `$.results[*].name` - `artifact_repo` → `$.results[*].repo` - `artifact_created` → `$.results[*].created` Bind the results to a ListView in the same pattern as the repositories call.
1{2 "call_name": "SearchAQL",3 "method": "POST",4 "endpoint": "api/search/aql",5 "call_level_headers": [6 {7 "name": "Content-Type",8 "value": "text/plain"9 }10 ],11 "body_type": "raw_text",12 "body": "[aql_query]",13 "variables": [14 {15 "name": "aql_query",16 "type": "String",17 "default": "items.find({\"repo\":{\"$eq\":\"docker-local\"}}).include(\"name\",\"repo\",\"created\").sort({\"$desc\":[\"created\"]}).limit(20)"18 }19 ],20 "json_paths": [21 { "name": "artifacts", "path": "$.results" },22 { "name": "artifact_name", "path": "$.results[*].name" },23 { "name": "artifact_repo", "path": "$.results[*].repo" },24 { "name": "artifact_created", "path": "$.results[*].created" }25 ]26}Pro tip: AQL's '.limit()' clause is essential for FlutterFlow — without it, a busy repository could return thousands of results and hang your UI. Start with a limit of 20-50 and add pagination logic only if users genuinely need to browse deeper.
Expected result: The SearchAQL call returns a 200 response with a 'results' array containing matching artifacts. An AQL body sent with Content-Type: text/plain succeeds where the same body sent as JSON would return a 400 parse error.
Handle null results defensively and test on device
The final step is making your AQL-powered screens robust to edge cases and validating on a real device. **Defensive null handling for empty AQL results:** When an AQL query returns no matches, the response is `{"results":[],"range":{...}}` — the `results` key exists but is an empty array. However, on certain error conditions (malformed AQL, permission issues), the `results` key may be absent entirely. JSON Path expressions like `$.results[*].name` return null when applied to a missing `results` key, which causes FlutterFlow's ListView binding to throw an error. Fix this in the FlutterFlow UI: wrap the ListView in a Conditional Widget that checks `getJsonField(response, r'$.results') != null` before rendering. When the condition is false, show an empty state message ('No artifacts found') rather than a broken list. For the same reason, JSON Path the `results` key first with `$.results` to get the array, then use that array as the ListView source rather than binding directly to the nested paths. This gives you one null-check point rather than guarding every individual field binding. **Test on a device for self-hosted Artifactory:** If your Artifactory instance is self-hosted and only accessible on the corporate network or VPN, FlutterFlow's web-based Test mode will reach it (since tests run from your browser, which may be on-network). But your published app's users need to be on the same network or VPN when the app runs. Test this on a physical device using the FlutterFlow mobile app or an APK build while connected to the corporate WiFi. If you need help building a more comprehensive Artifactory dashboard or troubleshooting network connectivity, RapidDev builds FlutterFlow integrations like this every week — free scoping call at rapidevelopers.com/contact.
Pro tip: The AQL response also includes a 'range' object with 'total', 'offset', and 'limit' fields that you can use to implement pagination. Store the offset in a Page State variable and add a 'Load More' button that increments the offset and appends results to the list.
Expected result: Your Artifactory dashboard handles empty search results gracefully (showing an empty state rather than crashing), and the full integration works on a physical device connected to the corporate network.
Common use cases
On-call engineer's artifact status dashboard
A DevOps team builds a FlutterFlow app for on-call engineers to check repository storage levels, see the latest artifact versions in the releases repository, and verify that a specific build version has been deployed to the production repo. The app uses a read-only Artifactory access token and queries the REST API for repository metadata and AQL artifact searches. Engineers get phone-readable answers without VPN access to a Jenkins dashboard or a browser.
Build a dashboard screen that fetches all Artifactory repositories from GET /api/repositories, shows each repo name and type in a ListView, and lets the engineer tap a repo to see its storage usage from GET /api/storageinfo.
Copy this prompt to try it in FlutterFlow
Release version tracker for mobile release managers
A mobile release manager uses a FlutterFlow app to track which version of the iOS and Android app builds have been promoted from the staging-local to the releases-local repository in Artifactory. An AQL search finds artifacts with specific properties (build.number, release.stage) and shows a color-coded list of recent promotions. The release manager can confirm a build is in the right repo without logging into the JFrog platform.
Create a release tracker screen that uses AQL search to find artifacts in 'releases-local' with property 'app.platform=ios' modified in the last 30 days, and displays artifact name, version, and creation date sorted by newest first.
Copy this prompt to try it in FlutterFlow
Artifact search utility for dependency management
A large engineering organization uses a FlutterFlow app as a mobile artifact search tool. Engineers search for a package name using AQL and see matching artifacts across all repositories, including their version, repository location, and last modified date. This replaces ad-hoc browser searches in the Artifactory UI for quick lookups during incidents or code reviews.
Add a search screen with a text input that fires an AQL query to find artifacts whose name matches the search term across all repositories, and displays results in a ListView showing artifact name, repo name, and last modified date.
Copy this prompt to try it in FlutterFlow
Troubleshooting
API Call returns 400 with 'AQL parsing error' on the SearchAQL call
Cause: The AQL query body is being sent with Content-Type: application/json instead of Content-Type: text/plain. Artifactory's AQL endpoint requires raw plain text — it cannot parse a JSON envelope around the AQL string.
Solution: In the SearchAQL API Call (not the API Group), add a call-level Content-Type header override set to 'text/plain'. In the Body tab, use a Raw/Text body type and enter the AQL string directly as the body — do not wrap it in a JSON object or quotes. Verify in the Response & Test tab that the Content-Type header in the request preview shows 'text/plain'.
All API Calls return 404 Not Found even though the Artifactory instance is reachable
Cause: The API Group base URL is missing the '/artifactory' path segment. Without this suffix, API Call endpoints like 'api/repositories' resolve to 'https://yourcompany.jfrog.io/api/repositories' which doesn't exist.
Solution: Edit the Artifactory API Group and add '/artifactory' to the end of the base URL. The correct format is 'https://yourcompany.jfrog.io/artifactory' for JFrog Cloud, or 'https://artifactory.yourcompany.com/artifactory' for self-hosted. Save and re-test the API Calls.
401 Unauthorized even with what appears to be a valid credential
Cause: Either the wrong header name is being used (X-JFrog-Art-Api for API keys vs Authorization: Bearer for access tokens), or the header name casing is incorrect. Artifactory is case-sensitive for the X-JFrog-Art-Api header.
Solution: Verify which auth method your token uses: access tokens go in 'Authorization: Bearer [token]'; legacy API keys go in 'X-JFrog-Art-Api: [key]' with that exact capitalization. Check the App Constant value has no leading/trailing spaces. If uncertain which type of credential you have, generate a new access token from JFrog Platform → Administration → User Management → Access Tokens and use the Bearer pattern.
ListView crashes or shows no data when AQL returns an empty result set
Cause: The JSON Path expression binding to 'results[*].name' (or similar) returns null when 'results' is an empty array or missing from the response, and FlutterFlow's ListView throws an error when the bound data is null.
Solution: Wrap the ListView in a Conditional Widget that checks whether the 'results' field exists and has items before rendering the list. Bind the ListView source to the '$.results' JSON Path (which returns an empty list rather than null on empty responses), and add an empty state widget below the conditional for the 'no results' case.
The app works in FlutterFlow's web test preview but fails on mobile with 'connection refused' or timeout
Cause: Self-hosted Artifactory is behind a corporate VPN or firewall and is not accessible from the public internet. The web preview runs in your browser (on-network), but the mobile app tries to reach the same hostname from the cellular network.
Solution: Ensure the mobile device is connected to the corporate WiFi or VPN when testing. For users who need access from external networks, ask your infrastructure team to expose Artifactory through a reverse proxy with proper authentication, or use JFrog Cloud instead of a self-hosted instance for mobile-accessible deployments.
Best practices
- Always generate a read-only access token or API key for FlutterFlow — a token that can delete artifacts must never live in a compiled Flutter binary.
- Store the Artifactory credential as a FlutterFlow App Constant (Settings & Integrations → App Settings → App Constants), not as a raw string in an API Call header or Custom Action code.
- Ensure the API Group base URL ends exactly in '/artifactory' — missing this suffix causes 404s on every call and can be difficult to diagnose.
- Use access tokens (Bearer) instead of legacy API keys (X-JFrog-Art-Api) for new integrations — JFrog is moving away from API keys and access tokens have better scope controls and expiration support.
- Always set Content-Type: text/plain at the API Call level for AQL search requests — it must override the Group-level application/json header.
- Add a .limit() clause to every AQL query to prevent unbounded result sets from overloading your UI — start with 20 and add pagination only when needed.
- Handle null results defensively in ListView bindings — wrap lists in Conditional Widgets that check for a non-null, non-empty results array before rendering.
- For self-hosted Artifactory behind a VPN, clearly communicate to users that they must be on the corporate network or VPN for the dashboard to function.
Alternatives
Use GitHub Packages instead of Artifactory if your artifacts are npm, Maven, or Docker images tied to GitHub repositories and you want a simpler setup without a dedicated artifact repository.
Choose GitLab's built-in Package Registry if your team already uses GitLab for source control and CI/CD and wants artifact management without a separate JFrog subscription.
Use DigitalOcean Container Registry or Spaces for simpler Docker image or file storage needs that don't require Artifactory's multi-format universal repository and AQL search.
Frequently asked questions
Why does my AQL query keep returning a 400 error even though the syntax looks correct?
The most common cause is sending the AQL query with Content-Type: application/json instead of Content-Type: text/plain. Artifactory's AQL endpoint at /api/search/aql expects the raw AQL string as the entire POST body in plain text format — it cannot parse a JSON object like {"query": "items.find(...)"}. In FlutterFlow, add a call-level Content-Type header override of 'text/plain' on the SearchAQL API Call, and use a Raw/Text body type with the AQL string directly as the body.
Is it safe to store an Artifactory API key or access token in FlutterFlow?
FlutterFlow App Constants are compiled into the Flutter binary, which means a determined attacker can extract them from a distributed APK or IPA. For a read-only token scoped to browsing repository lists and artifact metadata, this is a manageable risk for internal team dashboards — someone who extracts the token can see your artifact inventory, not delete it. For any token with write or delete permissions, it must live exclusively in a server-side environment variable (Firebase Cloud Function, backend API) and never in FlutterFlow.
Can I use FlutterFlow to upload artifacts directly to Artifactory?
You can upload files to Artifactory via its REST API (a PUT to /api/artifact-path) from a FlutterFlow Custom Action. However, the upload credentials must be secret — use a read-only token for dashboard features and route upload operations through a Firebase Cloud Function or your own backend that holds a deploy-scoped token. Never place upload credentials in a FlutterFlow App Constant, as they would be compiled into the app binary.
Does this integration work with self-hosted Artifactory?
Yes — the same API Group and AQL patterns work with self-hosted Artifactory. The base URL changes to your internal hostname (e.g., https://artifactory.yourcompany.com/artifactory). The key constraint is network access: your FlutterFlow app users must be on the same network or VPN as the Artifactory instance. A mobile app on a cellular connection cannot reach an Artifactory instance behind a corporate firewall unless that instance is exposed via a reverse proxy or the device is on VPN.
What does 'X-JFrog-Art-Api' mean and when should I use it instead of Bearer?
X-JFrog-Art-Api is the legacy API key authentication header for Artifactory — you place your API key directly as the header value (no 'Bearer' prefix). The modern approach is access tokens, which use the standard 'Authorization: Bearer [token]' format. For new FlutterFlow integrations, use access tokens — they support expiration dates, fine-grained scopes, and are the direction JFrog is investing in. Use X-JFrog-Art-Api only if your Artifactory admin has provided an API key and hasn't migrated to access tokens yet.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation