# Dynamic Forms

The Dynamic Forms module presents one or more custom form screens to collect information from a user.

For an overview of this module and how it works, see [Forms and Data Entry](https://developer.incode.com/docs/forms-and-data-entry).

How you use this module depends on your [integration pattern](https://developer.incode.com/docs/android-common-implementation-patterns#integration-patterns). In Patterns 1 and 2, you add the module to a `FlowConfig` in code as shown on this page. In Pattern 3, you define your modules and configuration in Dashboard as a Flow or Workflow and reference it by ID with `startFlow()` or `startWorkflow()` as shown on [Run Flows Configured in Dashboard](https://developer.incode.com/docs/android-run-flows-configured-online).

This module is available for SDK 5.32.0 and later.

## Add Dynamic Forms

Add the module with `addDynamicForms(dynamicForms)`. In most integrations, the screens and questions come from your Incode Flow or Workflow configuration, so an empty `DynamicForms` is common. You can also build screens and questions in code with `DynamicForms.Builder` when you need to define them client-side.

```kotlin
flowConfigBuilder.addDynamicForms(dynamicForms)
```
```java
flowConfigBuilder.addDynamicForms(dynamicForms);
```

### Example

The example below builds an empty `DynamicForms`, adds it to a `FlowConfig` with a flow tag, and starts an onboarding section. The listener reads the user's answers from `onDynamicFormsCompleted`.

```kotlin
try {
    val dynamicForms = DynamicForms.Builder()
        .build()

    val flowConfig = FlowConfig.Builder()
        .setFlowTag("Dynamic forms section")
        .addDynamicForms(dynamicForms)
        .build()

    IncodeWelcome.getInstance()
        .startOnboardingSection(activityContext, flowConfig, object : OnboardingListener() {
            override fun onDynamicFormsCompleted(dynamicFormsResult: DynamicFormsResult) {
                // Read dynamicFormsResult.answers
            }

            override fun onError(error: Throwable) {}

            override fun onUserCancelled() {}

            override fun onOnboardingSectionCompleted(flowTag: String) {
                // Dynamic forms section complete
            }
        }
    )
} catch (e: ModuleConfigurationException) {
    e.printStackTrace()
}
```
```java
try {
    DynamicForms dynamicForms = new DynamicForms.Builder()
        .build();

    FlowConfig flowConfig = new FlowConfig.Builder()
        .setFlowTag("Dynamic forms section")
        .addDynamicForms(dynamicForms)
        .build();

    IncodeWelcome.getInstance()
        .startOnboardingSection(activityContext, flowConfig, new IncodeWelcome.OnboardingListener() {
            @Override
            public void onDynamicFormsCompleted(@NonNull DynamicFormsResult dynamicFormsResult) {
                // Read dynamicFormsResult.getAnswers()
            }

            @Override
            public void onError(@NonNull Throwable error) {}

            @Override
            public void onUserCancelled() {}

            @Override
            public void onOnboardingSectionCompleted(@NonNull String flowTag) {
                // Dynamic forms section complete
            }
        }
    );
} catch (ModuleConfigurationException e) {
    e.printStackTrace();
}
```

## Configuration Options

Configure the module with `DynamicForms.Builder`.

| Setting               | Description                                               |
| --------------------- | --------------------------------------------------------- |
| `addScreen(screen)`   | Adds a single `DynamicForms.Screen` to the form.          |
| `addScreens(screens)` | Adds a list of `DynamicForms.Screen` objects to the form. |

Each `DynamicForms.Screen` holds a `title`, a `hideTitle` flag, and a list of `DynamicForms.Screen.Question`. A question carries its `id`, `questionText`, an `inputType`, a `predefinedQuestionType`, optional `options`, and an `isOptional` flag.

For the complete option set, see `DynamicForms.Builder`, `DynamicForms.Screen`, and `DynamicForms.Screen.Question` in [API Reference](https://developer.incode.com/docs/android-api-reference).

## Result

Dynamic Forms delivers a `DynamicFormsResult` to the `onDynamicFormsCompleted(DynamicFormsResult)` callback on the `OnboardingListener`. Key fields include:

- `isSuccess`: `true` if the dynamic forms were submitted successfully; otherwise, `false`.
- `answers`: The list of answered form entries; `null` when unavailable. Each entry (`DynamicFormQuestionnaireModel`) contains the following:
  - `interviewId`: The interview ID the answer belongs to; `null` when unavailable.
  - `questionId`: The unique identifier of the question.
  - `question`: The question text shown to the user.
  - `inputType`: The input type of the question. One of: `NUMBER`, `CPF`, `COUNTRY`, `DATE`, `PHONE`, `EMAIL`, `TEXT`, `YESNO`, or `SELECT`.
  - `selectedAnswer`: The user's answer (`AnswerModel` with a `detail` string); `null` when unanswered.
- Fields inherited from `BaseResult`:
  - `resultCode`: The `ResultCode` for this result.
  - `error`: When `resultCode` is `ERROR`, the `Throwable` that caused it; otherwise, `null`.
  - `deviceStats`: The `DeviceStats` snapshot of the device state when the result was produced.
  - `motionStatus`: Device motion assessment during capture. One of the following:
    - `UNCLEAR`: Motion could not be determined; the default.
    - `PASS`: Device motion was within acceptable limits.
    - `FAIL`: Excessive device motion was detected.

Errors surface via `OnboardingListener.onError(Throwable)`. This module does not define a module-specific exception subtype.

For all fields, see `DynamicFormsResult` in [API Reference](https://developer.incode.com/docs/android-api-reference).
