Axon Form - Dynamic Form System for Flutter

A powerful and flexible dynamic form generation system for Flutter that renders forms from Axon JSON using a native FFI core.

๐ŸŽจ Key Features

  • ๐Ÿ“‹ Dynamic Form Generation from JSON configuration
  • ๐ŸŽฏ Provider-based State Management with AxonFormProvider
  • โœ… Built-in Validation via native Axon engine
  • ๐ŸŽจ Customizable Rendering with page/field/navigation builders
  • ๐Ÿ”ง 10 Input Field Types out of the box
  • ๐Ÿ“ฑ Multi-page Forms with default or custom navigation
  • ๐Ÿš€ Production-oriented FFI Architecture

๐Ÿ—๏ธ Architecture Overview

Core Components

  1. JSON Configuration - Defines pages, nodes, edges, and conditions
  2. Native Core (FFI) - Handles graph init, validation, visibility, and values
  3. State Management - AxonFormProvider + ChangeNotifier
  4. Rendering Layer - AxonForm + page/field builders
  5. Field Widgets - Modular widgets for each field type

Architecture Diagram

JSON Config
    โ†“
AxonFormFFI.initialize()
    โ†“
AxonFormGraph + AxonFormProvider
    โ†“
AxonForm / FormBuilder / PageBuilder
    โ†“
AxonFormFieldBuilder
    โ†“
Field Widgets (text, number, date, ...)

๐ŸŽฏ State Management Approach

AxonFormProvider (ChangeNotifier)

AxonFormProvider is the form state orchestrator:

  • Stores graph/page state and current page index
  • Retrieves field values through native core
  • Triggers field/page validation through native core
  • Handles dynamic visibility updates through native event listeners
  • Notifies UI consumers reactively

AxonFormController

AxonFormController is the public controller wrapper:

controller.getFieldValue('field_id');
controller.setFieldValue('field_id', value);
controller.validatePage('page_id');
controller.getOptions('field_id');
controller.nextPage();
controller.prevPage();

๐Ÿ“‹ Supported Input Field Types

Field Type Enum Value JSON Value Widget Description
Text FieldType.text "text" AxonTextInput Single-line text input
Number FieldType.number "number" AxonNumberInput Digits-only numeric input
Date FieldType.date "date" AxonDateInput Date picker
Password FieldType.password "password" AxonPasswordInput Obscured input with toggle
Radio FieldType.radio "radio" AxonRadioInput Single selection from options
Dropdown FieldType.dropdown "dropdown" AxonDropdownInput Dropdown selection
Address Dropdown FieldType.addressDropdown "address_dropdown" AxonAddressDropdownInput Cascading address selection
Checkbox FieldType.checkbox "checkbox" AxonCheckboxInput Boolean input
Multi-select FieldType.multiSelect "multi_select" AxonMultiSelectInput Multiple option selection
File FieldType.file "file" AxonFileInput File picker input

๐Ÿš€ Getting Started

Requirements

  • Flutter >=3.3.0
  • Dart SDK ^3.8.0

Installation

Add to your app pubspec.yaml:

dependencies:
  axon_form_flutter:
    path: ../axon_form_flutter

Then run:

flutter pub get

Basic Usage

import 'dart:convert';
import 'package:axon_form_flutter/axon_form_flutter.dart';
import 'package:flutter/material.dart';

class MyFormScreen extends StatelessWidget {
  const MyFormScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<String>(
      future: DefaultAssetBundle.of(context).loadString('assets/example.json'),
      builder: (context, snapshot) {
        if (snapshot.connectionState != ConnectionState.done) {
          return const Center(child: CircularProgressIndicator());
        }
        if (!snapshot.hasData) {
          return const Center(child: Text('Error loading form JSON'));
        }

        final formJson = jsonDecode(snapshot.data!) as Map<String, dynamic>;

        return AxonForm.json(
          formJson,
          onSubmit: (result) {
            debugPrint('Form submitted: $result');
          },
        );
      },
    );
  }
}

Alternative Constructor

AxonForm.file(
  'assets/example.json',
  onSubmit: (result) => debugPrint('$result'),
)

๐Ÿ“ JSON Configuration Structure

Expected Top-level Keys

  • layout.pages
  • nodes
  • edges
  • optional condition_groups
  • optional address (for address_dropdown)

Minimal Example

{
  "layout": {
    "pages": [
      {
        "id": "p1",
        "order": 0,
        "title": "Basic Info",
        "description": "Enter your details",
        "field_ids": ["f_name", "f_gender"]
      }
    ]
  },
  "nodes": [
    {
      "id": "p1",
      "type": "page",
      "field_name": "",
      "field_type": "",
      "label": ""
    },
    {
      "id": "f_name",
      "type": "input",
      "field_type": "text",
      "field_name": "name",
      "label": "Full Name",
      "validation_rules": [
        {"type": "required", "message": "Name is required"}
      ]
    },
    {
      "id": "f_gender",
      "type": "input",
      "field_type": "radio",
      "field_name": "gender",
      "label": "Gender"
    },
    {"id": "opt_m", "type": "value", "label": "Male", "value": "m"},
    {"id": "opt_f", "type": "value", "label": "Female", "value": "f"}
  ],
  "edges": [
    {
      "id": "e1",
      "source_node": "f_gender",
      "target_node": "opt_m",
      "type": "has_options"
    },
    {
      "id": "e2",
      "source_node": "f_gender",
      "target_node": "opt_f",
      "type": "has_options"
    }
  ]
}

Full example: example/assets/example.json.

โœ… Validation Rules

Validation rule enums available in Flutter:

  • required
  • email
  • minLength
  • maxLength
  • pattern
  • min
  • max

Validation is executed by native Axon core during:

  • field updates
  • page transitions
  • submit

๐ŸŽจ Customization

Custom Field / Page / Navigation Builders

AxonForm.json(
  formJson,
  onSubmit: (result) {},
  fieldBuilder: (context, node) {
    if (node.id == 'f_name') {
      return Text('Custom field for ${node.label}');
    }
    return null; // fallback to built-in field
  },
  pageBuilder: (context, page, nodes, defaultFieldBuilder) {
    return null; // fallback to built-in page
  },
  pageNavigatorBuilder: (context, pages, currentPage, pageCount, next, prev) {
    return null; // fallback to default navigator
  },
)

ThemeExtension-based Styling

MaterialApp(
  theme: ThemeData(
    extensions: const [
      AxonFormTextInputStyle(
        decoration: InputDecoration(border: OutlineInputBorder()),
      ),
      AxonFormNumberInputStyle(
        decoration: InputDecoration(border: OutlineInputBorder()),
      ),
      AxonFormDateInputStyle(
        inputDecoration: InputDecoration(border: OutlineInputBorder()),
      ),
      AxonFormDropdownInputStyle(isExpanded: true),
    ],
  ),
  home: AxonForm.json(formJson, onSubmit: (result) {}),
)

๐Ÿ“ฆ Package Exports

Import once:

import 'package:axon_form_flutter/axon_form_flutter.dart';

This includes:

  • AxonForm, AxonFormController
  • models (AxonFormNode, AxonFormPage, AxonFormGraph)
  • enums (FieldType, ValidationRuleType)
  • built-in field widgets
  • style extensions

๐Ÿงช Run the Example

cd example
flutter pub get
flutter run