---
schema_version: "1"
id: 655542cea81a788d3bf93f3b74ff1ac72808dc77b6bf34999c576154d0da6c9e
title: Create a Custom Exercise Type That Uses a Screen Flow
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/sales-cloud/enablement/guides/enablement-custom-exercise-with-screen-flows.md
canonical_url: https://developer.salesforce.com/docs/sales/enablement/guide/enablement-custom-exercise-with-screen-flows.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: e1ce7894e5c9138f930cd36553455638c080989af97dd0e44586381b460d63e4
products:
  - Sales Programs and Partner Tracks with Enablement
  - Sales Cloud
---
# Create a Custom Exercise Type That Uses a Screen Flow

This implementation example demonstrates how to create a custom exercise type that uses an active screen flow. After creating this custom exercise type, an Enablement admin can search for a screen flow to use with the exercise in Program Builder. When users take the program in the Guidance Center, they see the screen flow when they complete the exercise.

To follow the code in this custom exercise implementation example, download the [enablement_custom_exercise_screen_flow.zip](https://resources.docs.salesforce.com/rel1/doc/en-us/static/misc/enablement_custom_exercise_screen_flow.zip) file.

## Set Up the Objects for the Custom Exercise Type

To store the records associated with the screen flows, create a custom object. Connect the screen flow records to the [LearningItem object](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_learningitem.htm). You can find the code examples in the `objects` folder of the downloaded files.

1. Create a custom object called `ScreenFlow_Object__c`.

   See [Create a Custom Object](https://help.salesforce.com/s/articleView?id=platform.dev_objectcreate_task_parent.htm&type=5) in _Salesforce Help_ or [Custom Objects](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_custom_objects.htm) in the _Object Reference for the Salesforce Platform_.

   ```xml
   <CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
      <fields>
          <fullName>ScreenFlow_Field__c</fullName>
          <deleteConstraint>SetNull</deleteConstraint>
          <label>Screen Flow</label>
          <referenceTo>ScreenFlow_Object__c</referenceTo>
          <relationshipLabel>Learning</relationshipLabel>
          <relationshipName>Learning</relationshipName>
          <required>false</required>
          <type>Lookup</type>
      </fields>
   </CustomObject>
   ```

2. On the standard object LearningItem, create a custom field called `ScreenFlow_Field__c` that references the `ScreenFlow_Object__c` object, as shown in this example.

   See [Create Custom Fields](https://help.salesforce.com/s/articleView?id=platform.adding_fields.htm&type=5) in _Salesforce Help_ and [Custom Fields](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/custom_fields.htm) in the _Object Reference for the Salesforce Platform_.

   ```xml
   <CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
       <fields>
           <fullName>ScreenFlow_Field__c</fullName>
           <deleteConstraint>SetNull</deleteConstraint>
           <label>Screen Flow</label>
           <referenceTo>ScreenFlow_Object__c</referenceTo>
           <relationshipLabel>Learning</relationshipLabel>
           <relationshipName>Learning</relationshipName>
           <required>false</required>
           <type>Lookup</type>
       </fields>
   </CustomObject>
   ```

## Create a Subcategory for the Custom Exercise Type

To configure how a custom exercise appears when Enablement admins edit a program or when users take the program in the Guidance Center, define a corresponding category and subcategory for each custom exercise type.

For the `category` property, assign the value `Exercise`. The `subcategory` property contains the name and icon of the custom exercise type in Program Builder and the Guidance Center.

![Custom items in the Enablement Programs Builder](https://developer.salesforce.com/docs/media/enablement/enablement-program-custom-items.png '{"class": "image-xl"}')

Create a Screen Flow subcategory by using Metadata API. See [Quick Start: Metadata API](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_quickstart_retrieve.htm) in the _Metadata API Developer Guide_. You can find the code examples in the `learningItemTypes` and `enblProgramTaskSubCategories` folders of the downloaded files.

1. Create a [LearningItemType metadata type](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_learningitemtype.htm) record that represents the custom exercise type in the Guidance Center.

   This definition references the custom object and the custom field on the LearningItem object that you created in [Set Up the Objects for the Custom Exercise Type](#set-up-the-objects-for-the-custom-exercise-type).

   ```xml
   <LearningItemType xmlns="http://soap.sforce.com/2006/04/metadata">
       <apexEvaluationHandler>ScreenFlowEvaluationHandler</apexEvaluationHandler>
       <apexSerializerDeserializer>ScreenFlowSerializerDeserializer</apexSerializerDeserializer>
       <customField>ScreenFlow_Field__c</customField>
       <customObject>ScreenFlow_Object__c</customObject>
       <developerName>ScreenFlowLearningItemType</developerName>
       <icon>standard:flow</icon>
       <lightningComponentDefinition>screenFlowViewer</lightningComponentDefinition>
       <masterLabel>Screen Flow Learning Item Type</masterLabel>
   </LearningItemType>
   ```

   Specify these properties:

   - `<apexEvaluationHandler>`
     - : Specifies an Apex class that defines how progress and completion of the custom exercise are assessed when users take the program in the Guidance Center. See [Track a User's Progress in a Custom Exercise](https://developer.salesforce.com/docs/sales/enablement/guide/enablement-custom-exercise-track-progress.md).
   - `<apexSerializerDeserializer>`
     - : Specifies an Apex class that defines how data related to the custom exercise type is retrieved and deployed with change sets or managed packages.
   - `<icon>`
     - : Specifies the icon to use for the custom exercise type. Use the format _iconType_:_iconName_, where the values correspond to icon types and names from the [Salesforce Lightning Design System](https://core-main.lightningdesignsystem.com/icons/). This example uses the Standard type Flow icon, so this value is `standard:flow`.

2. Create an [EnblProgramTaskSubCategory metadata type](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_enblprogramtasksubcategory.htm) record that represents the custom exercise type in Program Builder. This definition references the LearningItemType metadata record that you created.

   ```xml
   <EnblProgramTaskSubCategory xmlns="http://soap.sforce.com/2006/04/metadata">
       <developerName>ScreenFlowTaskSubCategory</developerName>
       <icon>standard:flow</icon>
       <learningItemType>ScreenFlowLearningItemType</learningItemType>
       <masterLabel>Screen Flow</masterLabel>
   </EnblProgramTaskSubCategory>
   ```

## Customize the Property Editor for the Custom Exercise Type

When an Enablement admin adds the custom exercise type to a program in Program Builder, they search for a specific content record to use for that instance of the exercise. To specify how Enablement admins find the appropriate content for the exercise, customize the exercise’s property editor. This example implements a search box for finding screen flow records and a dropdown to select a record.

![Property editor of custom exercise in the Enablement Programs Builder](https://developer.salesforce.com/docs/media/enablement/enablement-program-custom-property-editor.png '{"class": "image-xl"}')

To customize the property editor, create an LWC and add it to the Program Builder’s property sheet of the custom learning item. You can find the code examples in the `lwc` folder of the downloaded files.

1. Create an LWC bundle. Let's name our example bundle `screenFlowPropertyEditor`.

   See [_Trailhead_: Build Lightning Web Components](https://trailhead.salesforce.com/content/learn/trails/build-lightning-web-components) or [Create Lightning Web Components](https://developer.salesforce.com/docs/platform/lwc/guide/create-components-introduction.html) in the _Lightning Web Components Developer Guide_.

2. In the `screenFlowPropertyEditor.html` file, configure how the screen flow picker is rendered in the property editor.

   ```html
   <template>
     <lightning-record-picker
       lwc:ref="screenFlowPickerRef"
       label="Screen Flow"
       placeholder="Search Screen Flows..."
       object-api-name="ScreenFlow_Object__c"
       value="{_value}"
       onchange="{handleRecordChanged}"
     >
     </lightning-record-picker>

     <a
       href="/builder_platform_interaction/flowBuilder.app"
       class="slds-button slds-button_outline-brand slds-m-bottom_x-small slds-m-top_x-small"
       target="_blank"
       >Create Screen Flow</a
     >
     <a
       href="/lightning/o/ScreenFlow_Object__c/new"
       class="slds-button slds-button_brand slds-m-left_none"
       target="_blank"
       >Create Screen Flow Custom Object Record</a
     >
   </template>
   ```

3. In the `screenFlowPropertyEditor.js-meta.xml` configuration file, specify the `lightning__PropertyEditor` target, as shown in this example.

   ```xml
   <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
       <apiVersion>62.0</apiVersion>
       <isExposed>true</isExposed>
       <masterLabel>Screen Flow Custom Property Editor Component</masterLabel>

       <targets>
         <target>lightning__PropertyEditor</target>
       </targets>

   </LightningComponentBundle>
   ```

4. In the `screenFlowPropertyEditor.js` file, declare an `@api` variable named `value` to store the selected custom object record ID in Program Builder.

   Add the LWC of the custom property editor to the `customContent` property of the property sheet. See the [Add the Custom Exercise Type to Program Builder](#add-the-custom-exercise-type-to-program-builder) section. The ID of the record that the Enablement admin selects for the exercise in Program Builder is stored in the `value` attribute of the `customContent` property. To change the `value` from this property editor component, call the `valuechange` event and specify the custom object record ID.

   ```js
   import { LightningElement, api, track } from "lwc";

   // This component is rendered in place of the customContent property
   // in the custom exercise that uses this LWC component as an editor.
   export default class ScreenFlowPropertyEditor extends LightningElement {
     @track _value;
     @track compConnected;

     connectedCallback() {
       this.compConnected = true;
     }

     // The value variable holds the selected custom object record ID.
     @api
     get value() {
       return this._value;
     }

     // Set the record-picker value.
     set value(val) {
       if (this.compConnected && this._value !== val) {
         this.refs?.screenFlowPickerRef?.clearSelection();
       }

       this._value = val;
     }

     // Fire the value change event to let the Program Builder know
     // that the value for the customContent property has changed.
     handleRecordChanged(event) {
       this._value = event.detail.recordId;

       this.dispatchEvent(
         new CustomEvent("valuechange", {
           detail: {
             value: CUSTOM_OBJECT_RECORD_ID,
           },
         }),
       );
     }
   }
   ```

### Limitations of Custom Property Editor

- Only the selected `customContent` property `value` is passed to the LWC. No other metadata information like `min`, `max`, or `label`, are passed.
- When the program is in the Published state, custom exercise properties in Program Builder, such as `day`, are in read-only mode. However, when you implement a custom property editor by adding the custom LWC to the `editor` attribute of the `customContent` property, the LWC doesn’t automatically get disabled when the program is published. This is because the LWC doesn’t know about the published state or read-only mode.
- To add the LWC of the custom property editor to the `customContent` property of the `lightning__EnablementProgram` target, configure these attributes.

  - Add the LWC of the custom property editor to the `editor` attribute.
  - The `label` attribute isn’t used but must be included with a placeholder value.

    ```xml
    <property name="customContent" type="String" editor="c/screenFlowPropertyEditor" label="Screen Flows"/>
    ```

See [Add the Custom Exercise Type to Program Builder](#add-the-custom-exercise-type-to-program-builder)

## Add the Custom Exercise Type to Program Builder

To add the custom exercise type with screen flows to the Components palette in Program Builder, create an LWC. You can find the code examples in the `lwc` folder of the downloaded files.

1. Create an LWC bundle. Let's name our example bundle `screenFlow`.

   See [_Trailhead_: Build Lightning Web Components](https://trailhead.salesforce.com/content/learn/trails/build-lightning-web-components) or [Create Lightning Web Components](https://developer.salesforce.com/docs/platform/lwc/guide/create-components-introduction.html) in the _Lightning Web Components Developer Guide_.

2. Leave the `screenFlow.html` file empty. We don’t render the markup of this HTML file.

   ```html
   <template> </template>
   ```

3. In the `screenFlow.js-meta.xml` configuration file, add and configure the [`lightning__EnablementProgram` target](https://developer.salesforce.com/docs/platform/lwc/guide/targets-lightning-enablement-program.html), as shown in this example. This LWC target adds the custom exercise type to the Components palette. The target properties are the options available in the Program Builder property sheet when an Enablement admin adds the custom exercise type to a program.

   Make the `default` attribute of the `subcategory` property point to the `developerName` of the EnblProgramTaskSubCategory Metadata type record.
   The `editor` attribute of the `customContent` property calls the `screenFlowPropertyEditor` LWC, which you created in the [Customize the Property Editor for the Custom Exercise Type](#customize-the-property-editor-for-the-custom-exercise-type) section.

   ```xml
   <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
       <apiVersion>62.0</apiVersion>
       <isExposed>true</isExposed>
       <masterLabel>Screen Flows</masterLabel>
       <description>Screen Flows</description>

       <targets>
           <target>lightning__EnablementProgram</target>
       </targets>

       <targetConfigs>
           <targetConfig targets="lightning__EnablementProgram">
               <property name="title" type="String" max="80" required="true" default="" label="Name" />
               <property name="description" type="multilinetext" required="true" default="" max="350" label="Description" />
               <property name="day" type="Integer" min="1" max="365" default="1" required="true" label="Day" />
               <property name="category" type="String" default="Exercise" />
               <property name="subCategory" type="String" default="ScreenFlowTaskSubCategory" />
               <property name="customContent" type="String" editor="c/screenFlowPropertyEditor" label="Screen Flows" />
           </targetConfig>
       </targetConfigs>
   </LightningComponentBundle>
   ```

4. In the `screenFlow.js` file, include the `@api` variables for each property configured in the `.js-meta.xml` file.

   ```js
   import { api, LightningElement } from "lwc";

   export default class ScreenFlow extends LightningElement {
     @api title;
     @api description;
     @api day;
     @api category;
     @api subCategory;
     @api customContent;
   }
   ```

5. In the `screenFlow.svg` file, add the custom exercise icon as an SVG image, as shown here. Program Builder shows this icon in the Custom Items section of the Components palette.

   **tip:** To find the SVG definition of an icon, download the icons from the [Salesforce Lightning Design System website](https://core-main.lightningdesignsystem.com/icons/) and navigate to the SVG file for the icon you want to use.

   ```xml
   <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
       <rect width="48" height="48" rx="4" fill="#107CAD"/>
       <path d="M38.352 14.7359C37.104 12.2879 33.936 7.72791 28.032 10.4159C24.384 12.0959 22.32 13.0559 22.32 13.0559L17.04 15.3599C15.552 16.0799 12.288 15.0719 10.464 14.3999C9.93599 14.2079 9.45599 14.7839 9.69599 15.3119C10.944 17.7599 14.112 22.3199 20.016 19.6319C23.664 17.9519 31.008 14.7839 31.008 14.7839C32.496 14.0639 35.76 15.0719 37.584 15.7439C38.112 15.8399 38.592 15.3119 38.352 14.7359Z" fill="white"/>
       <path d="M25.68 22.4639C25.008 22.8479 22.368 24.0479 22.368 24.0479L19.728 25.1999C18.432 25.9199 15.6 24.9599 13.92 24.2879C13.44 24.0479 13.008 24.6719 13.248 25.1519C14.304 27.5519 17.136 31.8719 22.32 29.2319C25.536 27.5999 28.272 26.5439 28.272 26.5439C29.568 25.8239 32.4 26.7839 34.08 27.4559C34.56 27.6479 34.992 27.0719 34.752 26.5439C33.648 24.1439 30.816 19.8239 25.68 22.4639Z" fill="white"/>
       <path d="M23.76 33.0719C23.232 33.3599 22.32 33.9359 22.32 33.9359C21.312 34.5599 19.2 33.7439 17.952 33.1199C17.616 32.9279 17.28 33.5039 17.472 33.9839C18.24 36.1439 20.352 40.0319 24.24 37.6319C25.68 36.7199 25.68 36.7679 25.68 36.7679C26.736 36.2399 28.8 36.9599 30.048 37.5359C30.384 37.7279 30.72 37.1519 30.528 36.6719C29.76 34.5599 27.792 30.8639 23.76 33.0719Z" fill="white"/>
   </svg>
   ```

## Configure the Custom Exercise for the Guidance Center

Create a new LWC component for showing the custom exercise type in the Guidance Center. You can find the code examples in the `lwc` folder of the downloaded files.

1. Create an LWC bundle. Let's name our example bundle `screenFlowViewer`.

   See [_Trailhead_: Build Lightning Web Components](https://trailhead.salesforce.com/content/learn/trails/build-lightning-web-components) or [Create Lightning Web Components](https://developer.salesforce.com/docs/platform/lwc/guide/create-components-introduction.html) in the _Lightning Web Components Developer Guide_.

2. In the `screenFlowViewer.html` file, configure how to display the custom exercise in the Guidance Center.

   ```html
   <template>
     <lightning-flow
       lwc:if="{renderFlow}"
       onstatuschange="{handleStatusChange}"
       flow-api-name="{flowDeveloperName}"
     >
     </lightning-flow>

     <lightning-badge
       class="slds-m-top_medium slds-text-body_regular"
       lwc:if="{learningItemStatusCompleted}"
       label="All good. You have finished the flow."
     ></lightning-badge>

     <lightning-spinner
       lwc:if="{loading}"
       variant="brand"
       alternative-text="Loading"
       size="x-small"
     ></lightning-spinner>
   </template>
   ```

3. In the `screenFlowViewer.js-meta.xml` configuration file, expose this component to other namespaces, and specify the API version.

   Salesforce’s internal LWC that invokes this LWC is in a different namespace. To make this component visible to other namespaces, expose this component.

   ```xml
   <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
       <apiVersion>62.0</apiVersion>
       <isExposed>true</isExposed>
   </LightningComponentBundle>
   ```

4. In the `screenFlowViewer.js` file, add these methods and variables.

   - `getFlowDeveloperName()`: Fetches the screen flow’s developer name by using this method of the optional custom `ResourceController` Apex class. You can add more Apex classes to `ResourceController` for additional customization. You can find an example in the `classes/ResourceController.cls` file of the download.
   - `@api customContent`: Contains the ID of the content record that the Enablement admin selected for the custom exercise in Program Builder.
   - `@api learningItemId`: Contains the learning item record ID, which you can use as input to evaluateLearningItem API to determine the progress of the custom exercise. See [Track a User's Progress in a Custom Exercise](https://developer.salesforce.com/docs/sales/enablement/guide/enablement-custom-exercise-track-progress.md) and [evaluateLearningItem LWC wire adapter reference](https://developer.salesforce.com/docs/platform/lwc/guide/reference-evaluate-learning-item) in the _Lightning Web Components Developer Guide_.

   ```js
   import { api, wire, LightningElement } from "lwc";
   import getFlowDeveloperName from "@salesforce/apex/ResourceController.getFlowDeveloperName";
   import isLearningItemStatusCompleted from "@salesforce/apex/ResourceController.isLearningItemStatusCompleted";
   import getScreenNames from "@salesforce/apex/ResourceController.getScreenNames";
   import { evaluateLearningItem } from "lightning/uiLearningPlatformApi";
   import Id from "@salesforce/user/Id";

   export default class ScreenFlowViewer extends LightningElement {
     flowDeveloperName;
     screenNames;
     loading = false;
     userId = Id;
     learningItemStatusCompleted;
     progressLoaded = false;
     isFlowCompleted = false;

     @api customContent;
     @api learningItemId;

     get renderFlow() {
       return (
         this.flowDeveloperName &&
         this.screenNames &&
         this.progressLoaded &&
         !this.learningItemStatusCompleted &&
         !this.isFlowCompleted
       );
     }

     @wire(isLearningItemStatusCompleted, { learningItemId: "$learningItemId", userId: "$userId" })
     learningItemprogress({ error, data }) {
       if (error) {
         // show error
       } else if (data !== undefined) {
         this.progressLoaded = true;
         this.learningItemStatusCompleted = data;
       }
     }

     @wire(getFlowDeveloperName, { customScreenFlowId: "$customContent" })
     flowDevName({ error, data }) {
       if (error) {
         // show error
       } else if (data) {
         this.flowDeveloperName = data;
       }
     }

     @wire(getScreenNames, { flowDeveloperName: "$flowDeveloperName" })
     flowScreens({ error, data }) {
       if (error) {
         // show error
       } else if (data) {
         this.screenNames = data.join(",");
       }
     }

     async handleStatusChange(event) {
       if (this.flowStatus === "FINISHED") {
         return;
       }

       const learningItemId = this.learningItemId;
       this.loading = true;

       this.flowStatus = event.detail.status;
       this.currentScreen = event.detail.locationName;

       const res = await evaluateLearningItem({
         learningItemId,
         details: {
           status: event.detail.status,
           currentScreen: event.detail.locationName,
           allScreens: this.screenNames,
         },
       });

       if (this.flowStatus === "FINISHED") {
         this.isFlowCompleted = true;
       }

       const { error } = res;

       if (error) {
         // show error
       } else {
         this.learningItemStatusCompleted = res.isCompleted;
       }

       this.loading = false;
     }
   }
   ```

That's it! Enablement admins can now start adding the screen flow custom exercise type to programs, and users can start taking the exercise when they enroll in programs.
