frappe_form 0.10.0 copy "frappe_form: ^0.10.0" to clipboard
frappe_form: ^0.10.0 copied to clipboard

PlatformiOS

A library to render Frappe DocForm and generate a response

Frappe Form #

A Flutter package for rendering Frappe Forms.

This package takes care of building the UI of a Frappe Form, handle behavior and validations and finally generates the Response from the user answers.

Supported DocType form fields #

So far this package supports the following Field Types

Field Type Supported
Tab Break
Column Break
Section Break
Data
Text
Small Text
Long Text
Text Editor
Markdown Editor
Select
RadioGroup (This is not a Frappe default supported field, this is a custom field that is based on a Select field having a custom property render_rules with a JSON definition like: "{\n \"type\": \"RADIO_GROUP\"\n}", this will make the Select to be rendered as a RadioGroup, check the demo. Use the render_rules custom property to override any field rendering)
Geolocation
Autocomplete
Phone
Attach
Attach Image
Password
Check
Date
Time
Datetime
Int
Float
Percent
Currency
Rating
Heading
Table (relies on having a child_table property that contains the JSON DocType definition of the referenced DocType)
HTML (With support for link tap behavior by using the url_launcher plugin)
Link ☑️
Dynamic Link ☑️
Barcode ☑️
Button ☑️
Code ☑️
Color ☑️
Image ☑️
Read Only ☑️
Signature ☑️
Table MultiSelect ☑️
Duration ☑️
HTML Editor ☑️
Icon ☑️
JSON ☑️

Supported extra features #

  1. Mandatory Depends On (JS) expressions for validations
  2. Read Only Depends On (JS) expressions for validations
  3. Display Depends On (JS) expressions for validations
  4. Required fields
  5. Read only fields
  6. Description
  7. Default value
  8. Placeholder (rendered as the input hint text; on Attach and Attach Image fields, when the placeholder is an image url it is rendered as a preview image while no file is attached)

Depends On expressions #

The three Depends On (JS) properties are tokenized and parsed into a syntax tree, then evaluated against the current form values. Operator precedence and grouping parentheses work as they do in JavaScript, and the expression is re-evaluated automatically whenever any of the fields it references changes.

Supported syntax

Field references doc.field_name or just field_name
Literals numbers, single or double quoted strings, true, false, null
Logical &&, ||, !
Comparison ==, !=, ===, !==, >, <, >=, <=
Arithmetic +, -, *, /, % and the unary -/+
Grouping ( ... )
eval:(doc.check_1 == 1 || doc.check_2 == 1) && doc.none_of_the_above == 0
eval:doc.check_1 + doc.check_2 + doc.check_3 >= 2
eval:doc.select == 'Option 3' && doc.qty > doc.min_qty
eval:doc.some_date == '2025-01-01' && doc.discount > -5

Both operands of any operator can be a literal or a field reference, so 5 > doc.qty and doc.qty > doc.min_qty work just as well as doc.qty > 5. String literals are read as a whole, so operator symbols inside them (the dashes of a date, the slash of a path) are never mistaken for operators.

Besides the eval: form, the plain field name form is also supported, where depends_on: "my_check" is satisfied whenever that field holds a truthy value.

Anything outside this subset (function calls like in_list(...), array literals, assignments, …) raises a JsExpressionException, and the rule is ignored instead of being silently misinterpreted.

How values are read

A field the user has not filled yet is read as the Frappe default of its type, so a rule holds from the start without having to touch the field first:

Field type Unset value
Check, Int, Float, Currency, Percent, Rating, Duration 0
Data, Small Text, Long Text, Text, Select, Link, Password, Phone and the other text based ones ''
Anything else null

Date, Time and Datetime fields compare against the same string Frappe stores ('2025-01-01', '10:30:00'), and a Geolocation field against its GeoJSON string.

Values are compared with JavaScript semantics (0, '', null and NaN are falsy, == coerces while === does not), with two deliberate differences that fit form data better:

  • Relational operators compare numerically whenever both operands are numeric, even when both are strings. JS reads '10' > '9' as false, here it is true, because text based inputs expose numeric values as strings.
  • + sums whenever both operands are numeric and only concatenates otherwise, so '1' + '1' is 2 and not '11'.

Using the analyzer on its own

The analyzer lives in src/js_expression and knows nothing about Frappe, so it can be reused by binding the identifiers to whatever holds your values:

final expression = JsExpression.parse(
  "(check_1 == 1 || check_2 == 1) && total > 10",
  resolveVariable: JsConstantVariable.resolverOf({
    'check_1': 1,
    'check_2': 0,
    'total': 42,
  }),
);
expression.evaluateAsBool(); // true
expression.variables; // The variables it reads, to watch them for changes

Use JsExpression.tryParse to get null instead of an exception on a malformed expression. To read from something other than a map, implement JsVariable with your own name and value, and pass a JsVariableResolver that returns it. That is exactly what DocFieldVariable does for the form fields, and DocFieldDependsOnBundle is just the thin Frappe layer on top.

How to use #

Just add a DocFormView widget to your widget tree and you will have your Frappe Form UI.

DocFormView(
    form: form, // A Frappe Form instance
    onAttachmentLoaded: onAttachmentLoaded, // A callback to handle attachment loading (explained below) 
    actions: actions, // To add custom actions to the AppBar.
    locale: locale, // The specific locale for the Button and validation texts
    localizations: localizations, // To add support for extra localization 
    isLoading: loading, // Whether is some ongoing operation before loading the UI 
    onSubmit: onSubmit, // Callback when the user wants to submit the Form
    onCancel: onCancel, // Callback when the user wants to cancel the submission of the Form
    onResponse: onResponse, // Callback to get the Form Response
    controller: controller, // The DocFormController to use for item view and response generation
)

DocFormView #

  1. DocForm form: DocFormView requires an object of type DocForm this is the definition of the Frappe Form and will be used to build the Form UI and generate the Questions and Answers.
  2. Locale? locale: Optionally you can specify the language like "es" or "en" or "fr", etc. you want as a Locale object to use for validation messages and Submit button, by default the system language will be used.
  3. List<DocFormBaseLocalization>? localizations: this is a list that allows you to add extra language translations to the Form UI, currently the package supports only English and Spanish, so you can add other Languages, you just need to create a class for each new Language you want to support and extend DocFormBaseLocalization.
  4. DocFormBaseLocalization? defaultLocalization: Indicates what should be the fallback localization if the specified language or the system language is not supported, by default English is the fallback.
  5. bool isLoading: use this to indicate there is an ongoing operation, for instance if you need to make an API request to load your DocForm you can set isLoading = true so the DocFormView will show a Shimmer loading effect view.
  6. Future<Attachment?> Function()? onAttachmentLoaded: To make this package simpler and compatible with all Flutter supported platforms, the feature to load an attachment is delegated to the App, so you have to handle this logic by implementing this function and returning an instance of Attachment.
  7. List<Widget>? actions: To add custom actions to the AppBar.
  8. Future<bool> Function()? onSubmit: Callback when the user wants to submit Form. Return true to proceed with the submission, false otherwise.
  9. Future<bool> Function()? onCancel: Callback when the user wants to cancel the submission of the Form. Return true to allow the cancellation, false otherwise.
  10. ValueChanged<Map<String, dynamic>>? onResponse: Get the FormResponse after user taps on Submit button and all Form fields has been processed.
  11. DocFormController? controller: This is the controller to be used for questions and response generation within the DocFormView, the purpose of this controller here is to allow you to use an instance of an extension of DocFormController so you can override the behavior and widgets.

Some extra notes #

  1. This widget will use the app Theme to build, so if you want to change colors, InputDecorations, etc, you just have to change it in your app Theme. Also all the package widgets are public and exposed so you could override it if necessary.
  2. The DocFormView implementation takes care of validations depending on each DocField definition.
  3. Check the example project which shows all the features in action.

Demo #

Try example demo app here #