flutter_smart_forms 1.0.2
flutter_smart_forms: ^1.0.2 copied to clipboard
A powerful reactive JSON-driven form builder for Flutter with validation, async validation, conditional fields, dynamic UI, file upload, image upload, nested arrays, and extensible architecture.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_smart_forms/flutter_smart_forms.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SmartForms Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const SmartFormFullDemo(),
);
}
}
class SmartFormFullDemo extends StatefulWidget {
const SmartFormFullDemo({super.key});
@override
State<SmartFormFullDemo> createState() => _SmartFormFullDemoState();
}
class _SmartFormFullDemoState extends State<SmartFormFullDemo> {
late SmartFormController controller;
late List<FieldModel> fields;
// ------------------ JSON Form ------------------
final jsonForm = [
{
"type": "text",
"key": "firstName",
"label": "First Name",
"placeholder": "Enter first name",
"default": "Johna",
"validators": "required|min:2",
},
{
"type": "text",
"key": "lastName",
"label": "Last Name",
"placeholder": "Enter last name",
"visibleIf": {"firstName": "Johna"},
"validators": "required",
},
{
"type": "text",
"key": "nickName",
"label": "Nick Name",
"placeholder": "Enter nick name",
"validators": "required",
"extra": {"disabled": true}
},
{
"key": "email",
"label": "Email Address",
"placeholder": "Enter your email",
"type": "text",
"validators": "required|email",
"extra": {
"prefixIconAsset": null,
"prefixIcon": "email",
"prefixIconColor": "#1976D2",
"suffixIconAsset": "assets/icons/refresh.svg",
"suffixIconColor": "#1976D2",
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
"disabled": false
}
},
{
"key": "password",
"label": "Password",
"placeholder": "Enter your password",
"type": "password",
"validators": "required|min:6",
"extra": {
"suffixIconAsset": null,
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
"disabled": false
}
},
{
"type": "password",
"key": "confirmPassword",
"label": "Confirm Password",
"validators": "required|match:password"
},
{
"key": "phone",
"label": "Phone Number",
"placeholder": "Enter phone number",
"type": "text",
"validators": "required|phone",
"extra": {
"prefixIcon": "phone",
"prefixIconColor": "#1976D2",
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
"margin": {"left": 0, "top": 8, "right": 0, "bottom": 16},
"disabled": false
}
},
{
"type": "barcode",
"key": "barcode",
"label": "Scan Product",
"placeholder": "Scan or enter barcode",
"validators": "required",
"extra": {
"prefixIcon": null,
"prefixIconAsset": null,
"prefixIconColor": "#1976D2",
"suffixIconAsset": null,
"suffixIcon": null,
"suffixIconColor": null,
"scannerTitle": "Barcode Scan",
"scannerCenterTitle": true,
"scannerAppBarColor": "#1976D2",
"showClearIcon": true,
"clearIcon": "close",
"clearIconColor": "#1976D2",
"readOnly": false,
"disabled": false,
"autoValidateBarcode": false,
"barcodePattern": r"^[0-9]{8,14}$",
"multiScan": false,
"multiScanLimit": 10,
"multiScanDelay": 800,
"allowDuplicates": false,
"chipColorValid": "#4CAF50",
"chipColorInvalid": "#F44336",
"chipTextColor": "#FFFFFF",
"showChipDelete": true,
"scanButtonText": "Scan Products",
"scanButtonHeight": 50,
"scanButtonWidth": null,
"scanButtonIcon": "qr_code_scanner",
"scanButtonColor": "#1976D2",
"scanButtonTextColor": "#FFFFFF",
"scanButtonIconColor": "#FFFFFF",
"scanButtonAlignment": "center", // or "start"
"scanButtonPadding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
"margin": {"left": 0, "top": 8, "right": 0, "bottom": 16},
},
},
{
"type": "date",
"key": "birthday",
"label": "Select Birthday",
"default": null,
"extra": {
"placeholder": "Pick a date",
"format": "dd/MM/yyyy",
"fillColor": "0xFFFFFFFF",
"textColor": "#333333",
"borderRadius": 10,
"showClearIcon": true,
"clearIcon": "close",
"clearIconColor": "#1976D2",
"readOnly": false,
"disabled": false,
"borderColor": "#AAAAAA",
"padding": {"left": 12, "top": 10, "right": 12, "bottom": 10},
"margin": {"left": 0, "top": 8, "right": 0, "bottom": 8},
"suffixIconAsset": "assets/icons/calendar.png",
"suffixIconColor": "#1976D2"
}
},
{
"type": "dropdown",
"key": "country",
"label": "Country",
"default": "US",
"valueKey": "id",
"labelKey": "label",
"extra": {
"options": [
{"id": 1, "value": "US", "label": "USA"},
{"id": 2, "value": "CA", "label": "Canada"},
{"id": 3, "value": "UK", "label": "United Kingdom"},
{"id": 4, "value": "IN", "label": "India"},
{"id": 5, "value": "IN2", "label": "Indi2"},
],
"showClearIcon": true,
"clearIcon": "close",
"clearIconColor": "#1976D2",
"readOnly": false,
"disabled": false,
"suffixIconAsset": "assets/icons/arrow_down.png",
"fillColor": "0xFFFFFFFF",
"textColor": "#333333",
"borderColor": "#AAAAAA",
"borderRadius": 12,
"padding": {"left": 12, "top": 10, "right": 12, "bottom": 10},
"margin": {"left": 0, "top": 8, "right": 0, "bottom": 8}
}
},
{
"type": "dropdown",
"key": "country",
"label": "Country",
"extra": {
"api": "https://api.restful-api.dev/objects",
"labelKey": "name",
"valueKey": "id",
"cache": true
}
},
{
"type": "dropdown",
"key": "devices",
"label": "Devices",
"extra": {
"multiSelect": true,
"api": "https://api.restful-api.dev/objects",
"labelKey": "name",
"valueKey": "id",
"cache": true,
'selectedChipTextColor': '0xFFFFFFFF',
}
},
{
"type": "dropdown",
"key": "state",
"label": "State",
"watchFields": ["country"],
"visibleIf": {"country": "US"},
},
{
"type": "dropdown",
"key": "languages",
"label": "Languages",
"default": [1, 2],
"extra": {
"multiSelect": true,
"nullable": false,
"valueKey": "id",
"labelKey": "label",
"options": [
{"id": 1, "value": "en", "label": "English"},
{"id": 2, "value": "fr", "label": "French"},
{"id": 3, "value": "es", "label": "Spanish"},
{"id": 4, "value": "tam", "label": "Tamil"},
{"id": 5, "value": "tam2", "label": "Tamil2"}
],
'selectedChipColor': '#CCE5FF',
'selectedChipTextColor': '#003366',
"prefixIconAsset": null,
"suffixIconAsset": null,
"fillColor": "0xFFFFFFFF",
"textColor": "#333333",
"borderColor": "#AAAAAA",
"borderRadius": 12,
"padding": {"left": 12, "top": 10, "right": 12, "bottom": 10},
"margin": {"left": 0, "top": 8, "right": 0, "bottom": 8}
}
},
{
"type": "suggestion",
"key": "suggestion",
"name": "country",
"label": "Select Country",
"placeholder": "Type or select",
"isBarcode": false,
"showAll": true,
"valueKey": "id",
"labelKey": "label",
"extra": {
"options": [
{"id": 1, "value": "US", "label": "USA"},
{"id": 2, "value": "CA", "label": "Canada"},
{"id": 3, "value": "UK", "label": "United Kingdom"},
{"id": 4, "value": "IN", "label": "India"},
{"id": 5, "value": "IN2", "label": "Indi2"}
]
}
},
{
"type": "otp",
"key": "otp",
"label": "OTP",
"validators": "required|min:6",
"extra": {
"length": 6,
"boxSize": 50,
"spacing": 10,
"autoFocus": false,
"obscure": false,
"borderRadius": 8,
"borderColor": "#cccccc",
"focusBorderColor": "#2196F3",
"successBorderColor": "#4CAF50"
}
},
{
"type": "checkbox",
"key": "subscribe",
"label": "Subscribe to newsletter",
"default": true,
"extra": {
"readOnly": false,
"disabled": false,
"activeColor": "0xFF4CAF50",
"checkColor": "0xFFFFFFFF",
"borderColor": "0xFF888888",
"iconColor": "0xFFFFFFFF",
"borderRadius": 12,
"size": 24,
"padding": {"left": 0, "top": 8, "right": 0, "bottom": 16},
"iconPosition": "prefix",
"iconAsset": "assets/icons/custom_tick.svg",
"icon": "check_circle_outline",
"allowClear": true,
"watchFields": ["subscribe"]
}
},
{
"type": "text",
"key": "hiddenField",
"label": "Hidden Field",
"visibleIf": {"subscribe": true},
"default": "This is hidden"
},
{
"type": "array",
"key": "addresses",
"label": "Addresses",
"extra": {
"minItems": 1,
"maxItems": 5,
"reorderable": true,
"animated": true,
"addButtonText": "Add Address",
"disabled": false,
"readOnly": false,
"padding": {"left": 16, "right": 16, "top": 10, "bottom": 10},
"margin": {"top": 12, "bottom": 12}
},
"fields": [
{"type": "text", "key": "street", "label": "Street"},
{"type": "text", "key": "city", "label": "City"}
]
},
{
"type": "array",
"key": "orders",
"label": "Orders (Nested Array)",
"fields": [
{"type": "text", "key": "product", "label": "Product"},
{
"type": "array",
"key": "items",
"label": "Items",
"fields": [
{"type": "text", "key": "name", "label": "Item Name"},
{"type": "text", "key": "qty", "label": "Qty"}
]
}
]
},
{
"type": "signature",
"key": "signature",
"label": "Signature",
"extra": {
"height": 180,
"strokeWidth": 2,
"borderRadius": 10,
"grid": true,
"undo": true,
"preview": true,
"fullscreen": true,
"save": true,
"showClearButton": true,
"borderColor": "#E0E0E0",
"backgroundColor": "#FFFFFF",
"penColor": "#000000"
}
},
{
"type": "file",
"key": "resume",
"label": "Upload Resume",
"extra": {
"uploadText": "Upload Resume",
"uploadIcon": "cloud",
"multiple": true,
"preview": true,
"removable": true,
"extensions": ["pdf", "doc", "docx", "png", "jpg"],
"maxSize": 5000000,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
"align": "start",
"maxFiles": 5,
"compress": true,
"compressQuality": 75,
"uploadHeight": 50,
"uploadWidth": 0,
"previewHeight": 60,
"previewWidth": 60,
"borderRadius": 16,
"gradient": true,
"buttonColorStart": "0xFF888888",
"buttonColorEnd": "#00f2fe"
}
},
{
"type": "image",
"key": "profile",
"label": "Upload Profile Image",
"extra": {
"uploadText": "Upload Image",
"uploadIcon": "cloud",
"camera": true,
"gallery": true,
"multiple": true,
"maxFiles": 2,
"preview": true,
"removable": true,
"compress": true,
"compressQuality": 75,
"compressMinWidth": 1080,
"compressMinHeight": 1080,
"compressFormat": "jpeg",
"compressAutoRotate": true,
"extensions": ["jpg", "jpeg", "png", "webp"],
"maxSize": 5000000,
"crop": true,
"cropStyle": "rectangle",
"cropAspectRatio": {"x": 1, "y": 1},
"cropBaseColor": "#000000",
"cropMaskColor": "#00000088",
"returnType": "file",
"align": "center",
"uploadHeight": 50,
"uploadWidth": 0,
"previewHeight": 60,
"previewWidth": 60,
"borderRadius": 16,
"gradient": true,
"buttonColorStart": "0xFF888888",
"buttonColorEnd": "#00f2fe"
}
},
];
final sections = [
FormSectionModel(
title: "Personal",
fields: [
FieldModel(
key: "firstName",
label: "First Name",
type: "text",
validators: "required|min:2",
extra: {
"placeholder": "Enter first name",
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
"textColor": "#000000",
"prefixIcon": "person",
},
),
FieldModel(
key: "lastName",
label: "Last Name",
type: "text",
validators: "required",
extra: {
"placeholder": "Enter last name",
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
"textColor": "#000000",
},
),
FieldModel(
key: "email",
label: "Email",
type: "text",
validators: "required|email",
extra: {
"placeholder": "Enter your email",
"prefixIcon": "email",
"prefixIconColor": "#1976D2",
"suffixIconAsset": "assets/icons/refresh.svg",
"suffixIconColor": "#1976D2",
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
"textColor": "#000000",
},
),
],
),
FormSectionModel(
title: "Address",
fields: [
FieldModel(
key: "city",
label: "City",
type: "text",
validators: "required",
extra: {
"placeholder": "Enter city",
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
},
),
FieldModel(
key: "pincode",
label: "Pincode",
type: "number",
extra: {
"placeholder": "Enter pincode",
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
},
),
],
),
FormSectionModel(
title: "Payment",
fields: [
FieldModel(
key: "cardNumber",
label: "Card Number",
type: "number",
validators: "required",
extra: {
"placeholder": "Enter card number",
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
},
),
FieldModel(
key: "expiryDate",
label: "Expiry Date",
type: "date",
extra: {
"placeholder": "MM/YY",
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"textColor": "#000000",
"suffixIconAsset": "assets/icons/calendar.png",
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
},
),
FieldModel(
key: "cvv",
label: "CVV",
type: "number",
extra: {
"placeholder": "Enter CVV",
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
},
),
],
),
];
final jsonSections = [
{
"title": "Personal",
"fields": [
{
"type": "text",
"key": "firstName",
"label": "First Name",
"placeholder": "Enter first name",
"default": "Johna",
"validators": "required|min:2",
"extra": {
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
"textColor": "#000000",
}
},
{
"type": "text",
"key": "lastName",
"label": "Last Name",
"placeholder": "Enter last name",
"validators": "required",
"extra": {
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
}
},
{
"type": "text",
"key": "nickName",
"label": "Nick Name",
"placeholder": "Enter nick name",
"validators": "required",
"extra": {"disabled": true}
},
{
"type": "text",
"key": "email",
"label": "Email",
"placeholder": "Enter your email",
"validators": "required|email",
"extra": {
"prefixIcon": "email",
"prefixIconColor": "#1976D2",
"suffixIconAsset": "assets/icons/refresh.svg",
"suffixIconColor": "#1976D2",
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
}
},
{
"type": "password",
"key": "password",
"label": "Password",
"placeholder": "Enter your password",
"validators": "required|min:6",
"extra": {
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
}
},
{
"type": "password",
"key": "confirmPassword",
"label": "Confirm Password",
"validators": "required|match:password"
},
{
"type": "text",
"key": "phone",
"label": "Phone Number",
"placeholder": "Enter phone number",
"validators": "required|phone",
"extra": {
"prefixIcon": "phone",
"prefixIconColor": "#1976D2",
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
"margin": {"left": 0, "top": 8, "right": 0, "bottom": 16},
}
}
]
},
{
"title": "Scan & Date",
"fields": [
{
"type": "barcode",
"key": "barcode",
"label": "Scan Product",
"placeholder": "Scan or enter barcode",
"validators": "required",
"extra": {
"scannerTitle": "Barcode Scan",
"scannerCenterTitle": true,
"scannerAppBarColor": "#1976D2",
"showClearIcon": true,
"clearIcon": "close",
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
}
},
{
"type": "date",
"key": "birthday",
"label": "Select Birthday",
"extra": {
"placeholder": "Pick a date",
"format": "dd/MM/yyyy",
"fillColor": "0xFFFFFFFF",
"textColor": "#333333",
"borderRadius": 10,
"showClearIcon": true,
"borderColor": "#AAAAAA",
"padding": {"left": 12, "top": 10, "right": 12, "bottom": 10},
"margin": {"left": 0, "top": 8, "right": 0, "bottom": 8},
"suffixIconAsset": "assets/icons/calendar.png",
"suffixIconColor": "#1976D2"
}
}
]
},
{
"title": "Files & Signature",
"fields": [
{
"type": "signature",
"key": "signature",
"label": "Signature",
"extra": {
"height": 180,
"strokeWidth": 2,
"borderRadius": 10,
"grid": true,
"undo": true,
"preview": true,
"fullscreen": true,
"save": true,
"borderColor": "#E0E0E0",
"backgroundColor": "#FFFFFF",
"penColor": "#000000"
}
},
{
"type": "file",
"key": "resume",
"label": "Upload Resume",
"extra": {
"uploadText": "Upload Resume",
"uploadIcon": "cloud",
"multiple": true,
"preview": true,
"removable": true,
"extensions": ["pdf", "doc", "docx", "png", "jpg"],
"maxSize": 5000000,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
"borderRadius": 16,
}
},
{
"type": "image",
"key": "profile",
"label": "Upload Profile Image",
"extra": {
"uploadText": "Upload Image",
"uploadIcon": "cloud",
"camera": true,
"gallery": true,
"multiple": true,
"maxFiles": 2,
"preview": true,
"removable": true,
"compress": true,
"compressQuality": 75,
"crop": true,
"cropStyle": "rectangle",
"cropAspectRatio": {"x": 1, "y": 1},
"borderRadius": 16,
"buttonColorStart": "0xFF888888",
"buttonColorEnd": "#00f2fe"
}
}
]
},
{
"title": "Preferences & OTP",
"fields": [
{
"type": "otp",
"key": "otp",
"label": "OTP",
"validators": "required|min:6",
"extra": {
"length": 6,
"boxSize": 50,
"spacing": 10,
"borderRadius": 8,
"borderColor": "#cccccc",
"focusBorderColor": "#2196F3",
"successBorderColor": "#4CAF50"
}
},
{
"type": "checkbox",
"key": "subscribe",
"label": "Subscribe to newsletter",
"default": true,
"extra": {
"activeColor": "0xFF4CAF50",
"checkColor": "0xFFFFFFFF",
"borderColor": "0xFF888888",
"borderRadius": 12,
"size": 24,
"icon": "check_circle_outline",
}
}
]
}
];
// ---------------- Direct FieldModel list ----------------
final directFields = [
FieldModel(
type: 'text',
key: 'firstName2',
label: 'First Name',
placeholder: 'Enter first name',
defaultValue: 'Alice',
extra: {'disabled': true},
),
FieldModel(
type: 'text',
key: 'lastName2',
label: 'Last Name',
defaultValue: 'R',
placeholder: 'Enter last name',
),
FieldModel(
type: 'text',
key: 'fullName',
label: 'Full Name',
defaultValueCallback: (values) {
// dynamically combine first & last name
final first = values['firstName2'] ?? '';
final last = values['lastName2'] ?? '';
return '$first $last';
},
),
FieldModel(
key: "email",
label: "Email Address",
placeholder: "Enter your email",
type: "text",
validators: "required|email",
extra: {
"prefixIconAsset": null,
"prefixIcon": "email",
"prefixIconColor": "#1976D2",
"suffixIconAsset": null,
"suffixIcon": null,
"suffixIconColor": "#1976D2",
"fillColor": "#E3F2FD",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
"disabled": false
},
),
FieldModel(
key: "password",
label: "Password",
placeholder: "Enter your password",
type: "password",
validators: "required|min:6",
extra: {
"prefixIcon": "lock",
"prefixIconColor": "#1976D2",
"suffixIconAsset": null,
"fillColor": "#E3F2FD",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
"disabled": false
},
),
FieldModel(
key: "phone",
label: "Phone Number",
placeholder: "Enter phone number",
type: "text",
validators: "required|phone",
extra: {
"prefixIcon": "phone",
"prefixIconColor": "#1976D2",
"fillColor": "#E3F2FD",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
"disabled": false
},
),
FieldModel(
type: "barcode",
key: "barcode",
label: "Scan Product",
placeholder: "Scan or enter barcode",
validators: "required",
extra: {
"prefixIcon": null,
"prefixIconAsset": null,
"prefixIconColor": "#1976D2",
"suffixIconAsset": null,
"suffixIcon": "qr_code",
"suffixIconColor": "#1976D2",
"scannerTitle": "Barcode Scan",
"scannerCenterTitle": true,
"scannerAppBarColor": "#FF5722",
"fillColor": "0xFFFFFFFF",
"borderColor": "#1976D2",
"borderRadius": 12,
"padding": {"left": 16, "top": 12, "right": 16, "bottom": 12},
"disabled": false
},
),
FieldModel(
type: 'dropdown',
key: 'country',
label: 'Country',
defaultValue: 'US',
extra: {
"options": [
{"value": "US", "label": "USA"},
{"value": "CA", "label": "Canada"},
{"value": "UK", "label": "United Kingdom"}
],
"suffixIconAsset": "assets/icons/arrow_down.png",
"fillColor": "0xFFFFFFFF",
"textColor": "#333333",
"borderColor": "#AAAAAA",
"borderRadius": 12,
"padding": {"left": 12, "top": 10, "right": 12, "bottom": 10},
"margin": {"left": 0, "top": 8, "right": 0, "bottom": 8}
},
),
FieldModel(
type: 'text',
key: 'state2',
label: 'State',
placeholder: 'Enter your state',
visibleIf: {'country2': 'US'},
),
FieldModel(
type: 'dropdown',
key: 'languages2',
label: 'Languages',
defaultValue: ['en', 'fr'],
extra: {
'multiSelect': true,
'nullable': false,
'valueKey': 'value',
'labelKey': 'label',
'options': [
{'value': 'en', 'label': 'English'},
{'value': 'fr', 'label': 'French'},
{'value': 'es', 'label': 'Spanish'}
],
// Modern UI extras
'selectedChipColor': '#CCE5FF',
'selectedChipTextColor': '#003366',
'prefixIconAsset': null, // optional, default null
'suffixIconAsset': null, // optional, default null
'fillColor': '0xFFFFFFFF',
'textColor': '#333333',
'borderColor': '#AAAAAA',
'borderRadius': 12,
'padding': {'left': 12, 'top': 10, 'right': 12, 'bottom': 10},
'margin': {'left': 0, 'top': 8, 'right': 0, 'bottom': 8},
},
),
FieldModel(
type: 'checkbox',
key: 'subscribe2',
label: 'Subscribe to newsletter',
defaultValue: true,
extra: {
"activeColor": "0xFF4CAF50",
"checkColor": "0xFFFFFFFF",
"borderColor": "0xFF888888",
"iconColor": "0xFFFFFFFF",
"borderRadius": 12,
"size": 24,
"padding": {"left": 0, "top": 8, "right": 0, "bottom": 16},
"iconPosition": "suffix",
"iconAsset": "assets/icons/custom_tick.svg",
},
),
FieldModel(
type: 'otp',
key: 'otpCode2',
label: 'OTP Code',
defaultValue: '',
),
FieldModel(
type: 'date',
key: 'dob2',
label: 'Date of Birth',
defaultValue: '2000-01-01',
extra: {
"placeholder": "Pick a date",
"format": "dd/MM/yyyy",
"fillColor": "0xFFFFFFFF",
"textColor": "#333333",
"borderRadius": 10,
"borderColor": "#AAAAAA",
"padding": {"left": 12, "top": 10, "right": 12, "bottom": 10},
"margin": {"left": 0, "top": 8, "right": 0, "bottom": 8},
"suffixIconAsset": "assets/icons/calendar.png",
"suffixIconColor": "#FF0000",
},
),
FieldModel(
type: 'signature',
key: 'signature2',
label: 'Signature',
),
FieldModel(
type: 'file',
key: 'upload2',
label: 'Upload Document',
),
FieldModel(
type: 'text',
key: 'hidden2',
label: 'Hidden Field',
visibleIf: {'subscribe2': true},
defaultValue: 'This is hidden',
),
];
// ---------------- Nested Conditional Fields ----------------
final nestedFields = [
FieldModel(
type: 'dropdown',
key: 'vehicle',
label: 'Select Vehicle',
defaultValue: 'car',
extra: {
'options': [
{'value': 'car', 'label': 'Car'},
{'value': 'bike', 'label': 'Bike'},
{'value': 'truck', 'label': 'Truck'}
],
'valueKey': 'value',
'labelKey': 'label',
},
),
FieldModel(
type: 'dropdown',
key: 'carBrand',
label: 'Car Brand',
visibleIf: {'vehicle': 'car'},
extra: {
'options': [
{'value': 'bmw', 'label': 'BMW'},
{'value': 'audi', 'label': 'Audi'},
{'value': 'tesla', 'label': 'Tesla'}
],
'valueKey': 'value',
'labelKey': 'label',
},
),
FieldModel(
type: 'dropdown',
key: 'bikeBrand',
label: 'Bike Brand',
visibleIf: {'vehicle': 'bike'},
extra: {
'options': [
{'value': 'yamaha', 'label': 'Yamaha'},
{'value': 'honda', 'label': 'Honda'},
{'value': 'ducati', 'label': 'Ducati'}
],
'valueKey': 'value',
'labelKey': 'label',
},
),
FieldModel(
type: 'dropdown',
key: 'truckBrand',
label: 'Truck Brand',
visibleIf: {'vehicle': 'truck'},
extra: {
'options': [
{'value': 'volvo', 'label': 'Volvo'},
{'value': 'scania', 'label': 'Scania'},
{'value': 'man', 'label': 'MAN'}
],
'valueKey': 'value',
'labelKey': 'label',
},
),
FieldModel(
type: 'dropdown',
key: 'carOptions',
label: 'Car Options',
visibleIf: {
'vehicle': 'car',
'carBrand': 'tesla',
},
extra: {
'multiSelect': true,
'nullable': true,
'options': [
{'value': 'autopilot', 'label': 'Autopilot'},
{'value': 'full_self', 'label': 'Full Self Driving'},
{'value': 'premium', 'label': 'Premium Interior'}
],
'valueKey': 'value',
'labelKey': 'label',
},
),
];
@override
void initState() {
super.initState();
controller = SmartFormController(
formTheme: FormTheme(
inputDecoration: InputDecoration(fillColor: Colors.grey[100]),
labelStyle: const TextStyle(fontWeight: FontWeight.bold),
hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 12),
inputTextStyle: const TextStyle(fontSize: 16),
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
),
commonFieldTheme: const FieldTheme(
borderColor: Colors.grey,
borderRadius: 8,
fillColor: Colors.white,
),
fieldThemes: {
"firstName": const FieldTheme(
borderColor: Colors.blue,
borderRadius: 12,
fillColor: Colors.white,
),
},
);
fields = JsonFormBuilder.fromJson(jsonForm);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 6,
child: Scaffold(
appBar: AppBar(
title: const Text("SmartForms Demo"),
bottom: const TabBar(
tabs: [
Tab(text: "JSON Form"),
Tab(text: "Fields + Children"),
Tab(text: "Direct Widgets"),
Tab(text: "Nested Conditional"),
Tab(text: "RealTime Example"),
Tab(text: "Stepper Example"),
],
),
),
body: TabBarView(
children: [
// Tab 1: JSON Form
buildFormTab(controller, jsonForm),
// Tab 2: Fields + Children
buildFormTab(controller, fields),
// Tab 3: Direct Widgets
buildFormTab(controller, directFields),
// Tab 4: Nested Conditional
buildFormTab(controller, nestedFields),
// Tab 5: RealTime Example
SmartFormRealtimeExample(),
// Tab 5: RealTime Example
buildStepperForm(),
],
),
),
);
}
Widget buildStepperForm() {
final convertedSections =
jsonSections.map((s) => FormSectionModel.fromJson(s)).toList();
return SmartFormStepper(
controller: controller,
field:
FieldModel(type: "stepper", key: "mainStepper", extra: {}, label: ''),
sections: convertedSections,
onSubmit: (data) {
print(data);
},
);
}
/// Helper to build SmartForm
Widget buildFormTab(SmartFormController ctrl, dynamic formData) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
children: [
SmartForm(
controller: ctrl,
fields: formData is List<FieldModel> ? formData : null,
json: formData is List<Map<String, dynamic>> ? formData : null,
submitButton: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(10),
),
child: const Center(
child: Text(
"Save",
style: TextStyle(color: Colors.white),
),
),
),
onSubmit: (values) async {
showDialog(
context: context,
builder: (_) => AlertDialog(
title: const Text("Form Values"),
content: SingleChildScrollView(
child: Text(values.toString()),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text("Close"),
)
],
),
);
},
),
const SizedBox(height: 16),
// Reset Button
ElevatedButton.icon(
onPressed: () {
// Reset the form
ctrl.reset(
preserveKeys: [
// Add any system fields here you want to preserve
//'token',
],
);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Form Reset!")),
);
},
icon: const Icon(Icons.refresh),
label: const Text("Reset Form"),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.redAccent,
padding:
const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
),
),
],
),
),
);
}
}
class SmartFormRealtimeExample extends StatefulWidget {
const SmartFormRealtimeExample({super.key});
@override
State<SmartFormRealtimeExample> createState() =>
_SmartFormRealtimeExampleState();
}
class _SmartFormRealtimeExampleState extends State<SmartFormRealtimeExample> {
final controller = SmartFormController();
final jsonForm = [
{
"type": "text",
"key": "productName",
"label": "Product Name",
"placeholder": "Auto filled from barcode"
},
{
"type": "barcode",
"key": "barcode",
"label": "Scan Product",
"placeholder": "Scan barcode",
},
{
"type": "dropdown",
"key": "country",
"label": "Country",
"default": "US",
"extra": {
"options": [
{"value": "US", "label": "USA"},
{"value": "IN", "label": "India"},
{"value": "CA", "label": "Canada"}
]
}
},
{
"type": "dropdown",
"key": "state",
"label": "State",
"watchFields": ["country"],
"extra": {"options": []}
},
{"type": "checkbox", "key": "subscribe", "label": "Subscribe"}
];
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
controller.getNotifier("country")?.addListener(() {
final country = controller.getValue("country");
if (country != null) _handleCountry(country);
});
});
}
void _handleCountry(String country) {
List states = [];
if (country == "US") {
states = [
{"value": "CA", "label": "California"},
{"value": "TX", "label": "Texas"}
];
} else if (country == "IN") {
states = [
{"value": "TN", "label": "Tamil Nadu"},
{"value": "KL", "label": "Kerala"}
];
}
// 1️⃣ Reset the value first
controller.setValue("state", null, validate: false);
// 2️⃣ Update the options (this triggers the dropdown rebuild)
controller.updateFieldExtra("state", {"options": states});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("SmartForm Realtime Example")),
body: SmartForm(
controller: controller,
json: jsonForm,
onSubmit: (values) {
showDialog(
context: context,
builder: (_) => AlertDialog(
title: const Text("Form Data"),
content: Text(values.toString()),
),
);
},
),
);
}
}