flutter_smart_forms 1.0.2 copy "flutter_smart_forms: ^1.0.2" to clipboard
flutter_smart_forms: ^1.0.2 copied to clipboard

PlatformiOS

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()),
            ),
          );
        },
      ),
    );
  }
}
1
likes
140
points
273
downloads
screenshot

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

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.

Repository (GitHub)
View/report issues

Topics

#form #dynamic-forms #json-forms #form-builder #flutter-form

License

MIT (license)

Dependencies

crop_your_image, file_picker, flutter, flutter_image_compress, flutter_svg, flutter_typeahead, http, image_picker, intl, mobile_scanner, multi_select_flutter, path_provider, pinput, shared_preferences, signature

More

Packages that depend on flutter_smart_forms