# Create Vitruvio Mobile Form > All messages shown to the user must be written in Portuguese. Mobile forms are **not** desktop forms with a different skin. They use a different schema, a smaller component set, a different JavaScript runtime, and an explicit server↔client data contract. Read this whole skill before scaffolding — the mistakes here are not the desktop mistakes. ## The four things that make mobile different 1. **Different schema.** Root is ``, namespace `http://www.davinti.com.br/vitruvio/mobile-form`, XSD `vitruvio-mobile-form.xsd`. 2. **Only 18 components** (vs 78 desktop). Do **not** assume a desktop widget exists on mobile. The full list: `CheckBox, ComboBox, GoogleMapsField, HorizontalLayout, ImageLibraryByFieldValue, ImageWidget, Label, MaskedField, MoneyField, NumericField, OptionGroup, ProgressBarWidget, RatingStars, SignaturePadField, TabLayout, TextField, Toggle, VerticalLayout` (plus structural `SubForm`/`ItemList`). Read `~/.local/share/vitruvio-platform/docs/components/mobile/.md` before using one. 3. **Two JavaScript runtimes.** - **Client-side** (`initScript`, `discoveryScript`, validators, component event scripts): **modern React-Native JS** — arrow functions, Promises, `.then()/.catch()` are fine and expected. Most APIs are **async** and return Promises. - **Server-side** (`` `execute(...)` bodies): **Rhino ES5**, same rules as scripts/desktop. This is the only place with access to platform libs and services (`libService`, `runtimeService`, db, etc.). 4. **Data is explicit.** Nothing is auto-injected the way desktop process variables are. Every piece of server data the form needs must be declared — via an `` variable, a `` (synced to the device, works offline), or fetched on demand from a named `` with `vCommunicationService.executeOnServer(...)`. ## Step 1 — Confirm you are inside a Vitruvio repo ```bash ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO" ``` If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo. ## Step 2 — Determine variant and gather the data contract Ask (only what is missing): - **Panel or process mobile form?** (see **Differences: panel vs process** below) - **Key** — the panel or process key (folder name). - For a **process**: which `formKey`(s) — they must match the `activiti:formKey` in `processes//.bpmn`. - **Which variables does the form need?** Because nothing is auto-injected, ask explicitly: - process variables to read (each becomes an `` `` and/or a Bridge call) - reference/lookup data (each becomes a `` backed by a `queries/*.sql`) - any server-only logic / libs needed (each becomes a ``) - **Offline?** Whether the data sources must be available without connectivity (affects `autoSyncOnInit` / `autoSyncOnDiscovery`). ## Differences: panel vs process The schema, component set, ServerSide/Bridge mechanics and JS runtimes below are **identical** for both. Only these differ: | | Panel mobile form | Process mobile form | |----------------|-------------------|---------------------| | File | `panels//-mobile.xml` | `processes//-mobile.xml` | | Root attribute | `` (no `processKey`) | `` | | Forms per file | one `
` | **one `` per BPMN `activiti:formKey`** (must match) | | Manifest | set `forms.mobile` **and** `showInMobileList: true` on the panel entry | set `forms.mobile` (and optionally `forms.mobileAlternative`) on the process entry | | Variables | use `engine.getGlobalVariable` / Autoload | process variables are fetched server-side via a Bridge (`runtimeService.getVariable`) and/or declared in `` | > Filename: name files by the **artifact key + suffix** — `-mobile.xml` (and > `-desktop.xml`, `.bpmn`). This keeps every form searchable by its key instead of > dozens of identical `form-mobile.xml` tabs. Older content used `form-mobile.xml` / > `form_web_mobile.xml`; the real path is whatever `forms.mobile` points to, so legacy files > still work — but new scaffolds use `-mobile.xml`. ## Step 3 — Scaffold the file ### Skeleton (process variant shown; for a panel drop `processKey` and use a single ``) ```xml Coleta Etapa de coleta no app { var n = parseInt(result, 10); execution.setVariable('numeroCarga', n).then(ok => {}).catch(err => { console.log('Erro ao setar numeroCarga localmente', err); }); }).catch(err => { console.log('Erro ao coletar numeroCarga no server', err); }); } ]]> nomeLoja numeroCarga ``` ## How libs and process variables reach the mobile form The mobile app cannot call `libService.loadScript(...)` or read process variables directly — those live on the server. The pattern is always **declare a Bridge, call it from the client**: ```xml ``` ```javascript // CLIENT-SIDE (modern JS): call the bridge, use the Promise result vCommunicationService.executeOnServer('imagemCodBarras', codigoBarras).then(base64 => { engine.getField('codigoImagem').setValue(base64); }).catch(err => console.log('Erro no bridge imagemCodBarras', err)); ``` Rules of thumb: - Anything needing a **platform lib, the database, or a platform service** → put it in a **Bridge** (server, ES5) and call it with `vCommunicationService.executeOnServer`. - **Reference/lookup tables** the form reads repeatedly → a **`QueryDataSource`** backed by a `queries/*.sql` (works offline once synced). - **Process variables** the form needs → either declare them in `` or fetch them in a Bridge via `runtimeService.getVariable(processInstanceId, 'varName')` and store locally with `execution.setVariable(...)`. - Client-side `execution.getVariable(...)` / `execution.setVariable(...)` are **async** and return Promises — use `.then()`. ## List-based entry: SubForm + ItemList For "add many items" screens (collect a list of rows), use a `SubForm` with an ``: ```xml Executar AÇÃO ``` Validate list completeness with `engine.getSubFormListSizeByStatus(function(size){ ... }, "all")` inside a `COMPLETE` validator. ## Step 4 — Manifest If run **standalone**, update the matching entry in `vitruvio.json`: - **Panel**: set `forms.mobile = "panels//-mobile.xml"` and `showInMobileList: true`. - **Process**: set `forms.mobile = "processes//-mobile.xml"`. Full entry creation is handled by **vitruvio-criar-painel** / **vitruvio-criar-processo**. ## Step 5 — Report Tell the user: - File created/updated and variant (panel/process). - Which variables/queries/bridges were declared, and that **only declared data is available** on the device — anything else must be added as an Autoload variable, QueryDataSource, or Bridge. - Reminder: client scripts are modern JS (Promises); Bridge bodies are server-side Rhino ES5. - For processes: each `
` must match an `activiti:formKey` in the BPMN. - Suggest reading `docs/components/mobile/` and an example (`~/.local/share/vitruvio-platform/examples/processes/*/form_web_mobile.xml`) for richer screens.