frappe_form2 0.1.1
frappe_form2: ^0.1.1 copied to clipboard
A library to render Frappe DocForm and generate a response
Frappe Form Updated package #
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 | ✅ |
| Signature | ✅ |
| Table MultiSelect | ✅ |
| Color | ✅ |
| Image | ✅ |
| Button | ☑️ |
| Code | ☑️ |
| Read Only | ☑️ |
| Duration | ☑️ |
| HTML Editor | ☑️ |
| Icon | ☑️ |
| JSON | ☑️ |
Supported fields for dashboard #
| Field Type | Supported |
|---|---|
| heatmap | ✅ |
| connections links | ✅ |
Supported extra features #
- Mandatory Depends On (JS) expressions for validations
- Read Only Depends On (JS) expressions for validations
- Display Depends On (JS) expressions for validations
- Required fields
- Read only fields
- Description
- Default value
How to use #
Just add a DocFormView widget to your widget tree and you will have your Frappe Form UI.
//
final Map<String, List<Map<String, String>>> mockDocTypeData = {
"Fruit": [
{"value": "Apple", "description": "Red Crunchy"},
{"value": "Banana", "description": "Yellow Sweet"},
{"value": "Cherry", "description": "Small Red"},
],
"Animal": [
{"value": "Lion", "description": "King of Jungle"},
{"value": "Tiger", "description": "Striped Hunter"},
{"value": "Elephant", "description": "Trunk Giant"},
],
"Job": [
{"value": "Doctor", "description": "Medical Professional"},
{"value": "Engineer", "description": "Technical Builder"},
],
};
// The fetchSuggestions implementation
Future<List<Map<String, String>>> fetchLinkSuggestions({
required String doctype, // e.g., "Fruit"
required String query, // e.g., "ap"
}) async {
print("============================: '$query' and options: '$doctype'");
// 1. Simulate Network Latency
await Future.delayed(Duration(milliseconds: 200));
// 2. Find the "DocType" list
final List<Map<String, String>>? sourceList = mockDocTypeData[doctype];
if (sourceList == null) return [];
final lowerPattern = query.toLowerCase();
// 3. Filter by 'value' (the primary link field)
return sourceList.where((e) {
final value = e['value']?.toString().toLowerCase() ?? '';
final description = e['description']?.toString().toLowerCase() ?? '';
return value.contains(lowerPattern) ||
description.contains(lowerPattern);
}).toList();
} //
Map<String, String> _formValues = {};
// ... inside build ...
return DocFormView(
key: ValueKey(loading),
form: widget.form,
baseUrl: "https://your-frappe-instance.com/",
// ───────── STEP 1: CAPTURE CHANGES ─────────
onDocTypeChanged: (fieldname, value) {
setState(() {
_formValues[fieldname??""] = value ?? "";
print(" Field '$fieldname' changed to '$value'");
});
},
// ───────── STEP 2: RESOLVE DYNAMIC DOCTYPE ─────────
fetchSuggestions: (pattern, field) async {
try {
print('Searching in: ${field.options} for pattern: $pattern');
// Get the string from the field's 'options' metadata
final String? name = field.fieldName;
final String resolvedDocType =
// "Animal"??"";
(_formValues.containsKey(name))
? (_formValues[name] ?? "none")
: (field.options ?? "none");
// Call your API
final List<Map<String, dynamic>> results = await fetchLinkSuggestions(
doctype: resolvedDocType,
query: pattern,
);
return results;
} catch (e) {
return [];
}
},
controller: DocFormController(
onBuildFieldView: (field, children, onAttachmentLoaded) async {
switch (field.type) {
case FieldType.heatmap:
return HeatMapView(field: field, activityData: []);
case FieldType.connections:
return ConnectionsView(
transactions: transactions.map((e) => Transaction.fromMap(e)).toList(),
onTap: (String link) {},
);
default:
return null;
}
},
),
isLoading: loading,
onSubmit: onSubmit,
onCancel: onCancel,
onResponse: onResponse,
getDoctypesForDynamicLink: (String fieldName) async {
var list = widget.form.getDynamicLinkOptions(fieldName);
if (list.isEmpty) {
list = ["Job", "Fruit", "Animal"];
}
return list;
},
);
DocFormView #
DocForm form:DocFormViewrequires 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.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.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.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.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 setisLoading = trueso theDocFormViewwill show a Shimmer loading effect view.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 ofAttachment.List<Widget>? actions: To add custom actions to the AppBar.Future<bool> Function()? onSubmit: Callback when the user wants to submit Form. Return true to proceed with the submission, false otherwise.Future<bool> Function()? onCancel: Callback when the user wants to cancel the submission of the Form. Return true to allow the cancellation, false otherwise.ValueChanged<Map<String, dynamic>>? onResponse: Get the FormResponse after user taps on Submit button and all Form fields has been processed.DocFormController? controller: This is the controller to be used for questions and response generation within theDocFormView, the purpose of this controller here is to allow you to use an instance of an extension ofDocFormControllerso you can override the behavior and widgets.
Some extra notes #
- 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.
- The
DocFormViewimplementation takes care of validations depending on eachDocFielddefinition. - Check the example project which shows all the features in action.