Skip to Main Content
Talk Intermediate MIT for frappe-mobile-sdk (Flutter package). AGPL-3.0 for frappe-mobile-control (server app)

Offline-first mobile apps on Frappe: a metadata-driven Flutter SDK with server-side control

Proposal status is Approved
Session Description

Frappe keeps everything about a form on the server. The fields, the validations, the workflow, and the permissions all live in the DocType and change whenever someone edits it. A mobile app that copies that structure into hardcoded screens breaks the day a DocType gets a new field. To fix it you ship a new build to the Play Store, and a field worker on a weak connection may never download it. Add poor or missing network, where the app has to collect data offline and reconcile it later against a server that has also changed, and building mobile on Frappe gets hard fast. This talk is about how we made it tractable, and the code is open source.

We built two pieces that sit on top of Frappe core.

frappe-mobile-control is a Frappe app in Python (AGPL-3.0). It runs on your existing server and decides what the mobile app may do: which DocTypes appear on the phone and in what order, the minimum app version a user must have, whether the app is in maintenance mode, and how login works (password, mobile OTP, or OAuth with PKCE, with token refresh). You change any of this from Frappe Desk and the phone picks it up on the next sync.

frappe-mobile-sdk is a Flutter package in Dart (MIT). Give it a Frappe site and a config file and it builds a working app. It reads the DocType metadata and renders the form for you, with more than twenty field types including links, child tables, and geolocation. It handles the REST calls, the workflow actions, and role permissions, and it stores data in a local SQLite database that syncs both ways.

Four layers, one sync channel. The server stays the source of truth; the SDK renders forms from metadata and works offline; you build only your own UI and logic.

Offline is the focus of the talk. It is easy to describe and full of sharp edges once real users and real networks show up. What we had to solve, and how:

Collect with no network, survive an interrupted sync, and handle a record edited in two places: resumable cursor, conflict guard, dependency-ordered sync, write-ahead logging with serialized writes, a background isolate, and encryption at rest.

  • A pull that gets killed halfway on a weak connection should not start over. We save the sync position in SQLite so it resumes from where it stopped.

  • When a phone uploads a record at the same moment the server sends down a newer copy, the two can clash. We added a guard so neither one silently overwrites the other.

  • Records often depend on each other: a parent and its child rows, or a record and its attachment. We sync them in order and upload attachments first, so you never store a row that points at a file that is not there yet.

  • SQLite used to crash when several writes arrived together. We turned on write-ahead logging, made writes happen one at a time, and moved sync onto a separate Dart isolate so the screen never freezes while data moves.

  • When someone adds a field on the server while a form is open on the phone, the app keeps a fingerprint of the form's structure, notices its copy is stale, and refreshes before saving instead of crashing or dropping the field.

  • The local database is encrypted on the device, because in our work it holds personal data like Aadhaar numbers that should not sit in plain text on a phone in the field.

A field added on the server, with no app rebuild. The old hardcoded app threw NoSuchFieldError; this one picks the field up on the next sync.

We will demo all of this on a real device, not on slides. We build an app from a config file, add a field on the server and watch it appear after a restart with no new build, put the phone in airplane mode and collect a few records, then come back online and watch them sync. We kill the app mid-sync to show it resume, and edit a record on the server while the phone is offline to show how the conflict is handled.

This is not a prototype. The SDK already runs in production apps that field teams use to collect data for large social-sector programmes, on real phones, in places with little or no connectivity. The demo is the same code those teams run.

Both repositories are open. You can install the server app with bench, pull the SDK from pub.dev, point it at your own Frappe site, and build on it or send a patch.


Key Takeaways
  • How to drive a mobile app's forms, permissions, and versioning from the Frappe server, so a schema change does not need a new app build.

  • How to render Frappe DocType forms dynamically in Flutter, including links, child tables, and geolocation, without writing the screens by hand.

  • How an offline sync engine behaves in the field: resuming an interrupted pull, ordering records that depend on each other, and handling a record edited in two places.

  • Practical ways to keep offline SQLite stable and safe on a field device: write-ahead logging, one-at-a-time writes, a background sync thread, and on-device encryption.

  • Where to get the code (MIT SDK on pub.dev, AGPL-3.0 server app) and how to build on it or contribute.

References

Session Categories

Introducing a FOSS project or a new version of a popular project
Technology architecture
Talk License: MIT for frappe-mobile-sdk (Flutter package). AGPL-3.0 for frappe-mobile-control (server app)

Which track are you applying for?

Main track

Speakers

Deepak Batra
Deepak Batra VP of Engineering | Dhwani Rural Information Systems

Deepak Batra is Vice President of Engineering at Dhwani Rural Information Systems, where he leads engineering and information security. With over 12 years of experience, he has built and scaled production systems across backend, mobile, and platform architecture using Python, Flutter, AWS, and Frappe/ERPNext. At Dhwani, he has worked on field-first, offline-capable products including mForm and mGrant, contributing to both AI/ML initiatives and security architecture. In this session, he shares lessons from building a metadata-driven Flutter SDK for Frappe that enables configurable, offline-capable mobile applications with server-side control.