Skip to content
CARP Docs
Ask AISystemLightDark
On this page

Tasks and steps

The task classes and every step type, with their parameters and what each screen shows.

Copy MarkdownOpen inOpen in GitHubView as MarkdownOpen in Scira AIOpen in ChatGPTOpen in ClaudeOpen in Cursor

A task is an ordered list of steps that the participant works through, and a step is one screen of it. RPUITask shows a task one step at a time and collects one result for the whole run. See Running tasks.

Research Package has these step types:

StepShowsResult
RPInstructionStepText, with an optional image, video or audio playerNone
RPQuestionStepOne questionRPStepResult
RPFormStepSeveral questions on one scrolling pageRPStepResult with one result per question
RPTimerStepA countdownNone
RPCompletionStepA closing message with a DONE buttonNone
RPVisualConsentStepThe sections of a consent document, one page eachRPPermissionResult, when the step asks for permissions
RPConsentReviewStepThe whole consent document, with AGREE and a signatureRPConsentSignatureResult
RPActivityStepAn interactive test, drawn by a subclassRPActivityResult

Task classes

RPUITask shows two task classes:

ClassOrder of the stepsUse it for
RPOrderedTaskThe order of the listSurveys and consent flows that every participant goes through the same way
RPNavigableOrderedTaskThe order of the list, changed by navigation rulesSurveys that skip or select steps based on earlier answers

Both take these parameters:

ParameterTypeRequiredDefaultDescription
identifierStringYes—Identifies the task. The RPTaskResult of each run gets the same identifier.
stepsList<RPStep>Yes—The steps, in the order the participant sees them.
closeAfterFinishedboolNotrueWhether RPUITask closes its route after the last step. See Finish a task.

For navigation rules, see Branching.

Three rules apply when you build a task:

  • Unique identifiers: the result keys each step result by step identifier, so two steps with one identifier share one entry.
  • One object per step: RPOrderedTask finds the next step by the position of the current one in the list. A step object that appears twice makes the task loop.
  • A new task for each run: some objects change while a task runs. A free-text choice stores the typed text in the task, and a reorganizer rule rewrites the step list.

Common step fields

Every step class extends RPStep, which has these fields:

FieldTypeDescription
identifierStringIdentifies the step. Results and navigation rules refer to the step by it.
titleStringThe heading of the screen. A question step shows it as the question.
textString?The body text of instruction and completion steps. Question, form and timer steps do not show it.
optionalboolWhether the participant can skip the step. Only question and form steps read it. Default: false.
footnoteString?Small centered text at the bottom of instruction, question, form and timer steps, such as a copyright notice.
nextButtonTextString?The label of NEXT while this step is on screen. It wins over the nextButtonText of RPUITask.

Each of these texts can be a translation key or plain text. A step shows the translation when one exists, and the text as written otherwise. See Localization.

Only RPStep and RPInstructionStep take nextButtonText as a constructor parameter. For other steps, set it with a cascade:

final readyCheck = RPQuestionStep(
  identifier: 'readyCheck',
  title: 'Are you in a quiet place?',
  answerFormat: RPChoiceAnswerFormat(
    answerStyle: RPChoiceAnswerStyle.SingleChoice,
    choices: [
      RPChoice(text: 'Yes', value: 1),
      RPChoice(text: 'No', value: 0),
    ],
  ),
)..nextButtonText = 'Start the test';

Instruction step

RPInstructionStep shows information without asking anything: a title, a text and optionally an image, a video or an audio player. NEXT is enabled from the start, and the step records no result.

ParameterTypeRequiredDefaultDescription
identifierStringYes—Identifies the step.
titleStringYes—The heading of the screen.
textString?No—The text under the title.
detailTextString?No—A longer text behind a Learn more button, shown on a page of its own.
imagePathString?No—An image above the title.
videoPathString?No—A video above the title.
audioPathString?No—An audio player above the title.
footnoteString?No—Small text at the bottom of the screen.
nextButtonTextString?No—The label of NEXT on this step.
optionalboolNofalseNot used by this step.

Version 3.2.0 in CAMS apps

In research_package 3.2.0, an instruction step without text fails to build. Set text when your app uses that version.

An instruction step with a recording and a Learn more page:

final intro = RPInstructionStep(
  identifier: 'intro',
  title: 'Breathing exercise',
  text: 'Listen to the recording, then answer three questions.',
  detailText:
      'The recording guides you through four minutes of slow breathing.',
  audioPath: 'audio/breathing.mp3',
  footnote: 'Recording by the study team',
);

The three media paths take either an asset path or an http or https URL. For an asset, the image and the video take the full asset key, and the audio takes a path relative to assets/:

FieldAsset pathWhat the participant sees
imagePathassets/images/sleep.pngThe image, scaled to fit a box as tall as the screen. The title and the text follow below it, so the participant scrolls to read them.
videoPathassets/video/intro.mp4A video that plays and pauses on a tap, with a seek bar and a full-screen button. On a load error, a "Connection Error" dialog offers to retry.
audioPathaudio/breathing.mp3 for assets/audio/breathing.mp3A seek bar with play, pause and stop buttons.

Neither the video nor the audio starts by itself. To add the files to your app, see Add media and sound assets.

Question step

RPQuestionStep asks one question. Its answer format decides what the participant enters and what type the answer has. See Answer formats.

ParameterTypeRequiredDefaultDescription
identifierStringYes—Identifies the step and its result.
titleStringYes—The question.
answerFormatRPAnswerFormatYes—How the participant answers, as a subclass of RPAnswerFormat.
optionalboolNofalseAdds a Skip this question button.
footnoteString?No—Small text at the bottom of the screen.
autoSkipboolNofalseSkips the question automatically. See the known issue below.
timeoutDurationNoDuration(seconds: 0)Meant as the time before autoSkip skips the question, but not used.
autoFocusboolNofalseNot used. To open the keyboard, set autoFocus on RPTextAnswerFormat.

A question that the participant may skip:

final sleepHours = RPQuestionStep(
  identifier: 'sleepHours',
  title: 'How many hours did you sleep last night?',
  answerFormat: RPIntegerAnswerFormat(
    minValue: 0,
    maxValue: 24,
    suffix: 'hours',
  ),
  optional: true,
  footnote: 'Do not count naps.',
);

The screen shows the question and the input of the answer format. NEXT is enabled while the question has a valid answer. The step records an RPStepResult with the answer under the key answer, and Skip this question records one whose answer is null.

Known issue

With autoSkip: true, a question step skips itself about one second after it appears, whatever timeout says. Leave autoSkip off on question steps. For a timed question, put it in a form step, whose countdown follows timeout.

Form step

RPFormStep shows several questions on one scrolling page, each with its own answer format. Use it for questions that belong together, such as demographics.

ParameterTypeRequiredDefaultDescription
identifierStringYes—Identifies the step and its result.
titleStringYes—The heading above the questions.
questionsList<RPQuestionStep>Yes—The questions, each with its own identifier.
optionalboolNofalseAdds a Skip these questions button, which skips the whole form.
autoSkipboolNofalseStarts a countdown of timeout that ends the form by itself. The countdown is not shown.
timeoutDurationNoDuration(seconds: 0)The length of the countdown, in whole seconds.
saveResultsOnAutoSkipboolNofalseWhat happens when the countdown ends: true submits the answers given so far, and false skips the form with every answer set to null.
forceWaitboolNofalseWith autoSkip, keeps NEXT disabled until the countdown ends.
footnoteString?No—Small text at the bottom of the screen.

A form with one required and one optional question:

final aboutYou = RPFormStep(
  identifier: 'aboutYou',
  title: 'About you',
  questions: [
    RPQuestionStep(
      identifier: 'age',
      title: 'How old are you?',
      answerFormat: RPIntegerAnswerFormat(
        minValue: 18,
        maxValue: 110,
        suffix: 'years',
      ),
    ),
    RPQuestionStep(
      identifier: 'occupation',
      title: 'What is your occupation?',
      answerFormat: RPTextAnswerFormat(hintText: 'For example, nurse'),
      optional: true,
    ),
  ],
);

The form behaves as follows:

  • NEXT is enabled once every question that is not optional has an answer. In a form whose questions are all optional, it is enabled after the first answer.
  • From each question, the form uses only identifier, title, answerFormat and optional. The autoSkip, timeout and footnote of a question have no effect inside a form.
  • forceWait works only together with autoSkip. Without the countdown, NEXT never becomes enabled.
  • The form records one RPStepResult under its own identifier, which holds one RPStepResult per question. For the result of this form, see JSON format.

Timer step

RPTimerStep makes the participant wait, for example between two exercises. It shows the title and the remaining time as minutes and seconds, and it enables NEXT when the countdown reaches zero. The step records no result.

ParameterTypeRequiredDefaultDescription
identifierStringYes—Identifies the step.
titleStringYes—The text above the countdown.
timeoutDurationYes—The waiting time, in whole seconds.
showTimeboolNotrueWhether the remaining time is shown.
autoSkipboolNofalseMoves on to the next step when the countdown ends.
playSoundboolNofalsePlays assets/audio/RPTimerStepSound.mp3 from your app when the countdown ends.
footnoteString?No—Small text at the bottom of the screen.
optionalboolNofalseNot used by this step.

A two-minute rest that moves on by itself:

final rest = RPTimerStep(
  identifier: 'rest',
  title: 'Sit still for two minutes',
  timeout: const Duration(minutes: 2),
  autoSkip: true,
);

The countdown shows no hours, so keep timeout under one hour. The package does not bundle the sound for playSound into your app. See Add media and sound assets.

Completion step

RPCompletionStep ends a task with a title, a text, a checkmark animation and a DONE button. It hides the bottom bar and records no result. When it is the last step, DONE finishes the task.

ParameterTypeRequiredDefaultDescription
identifierStringYes—Identifies the step.
titleStringYes—The heading of the screen.
textString?No—The message under the title. Always set it: the screen fails to build when text is null.
optionalboolNofalseNot used by this step.

A closing screen:

final done = RPCompletionStep(
  identifier: 'done',
  title: 'Thank you',
  text: 'Your answers are saved. You can close the app.',
);

RPVisualConsentStep and RPConsentReviewStep make up an informed consent flow, and both show an RPConsentDocument:

StepParametersResult
RPVisualConsentStepidentifier, consentDocument and askPermission (default false)RPPermissionResult, when the step asks for permissions
RPConsentReviewStepidentifier, title, consentDocument, text and reasonForConsentRPConsentSignatureResult

A task that contains a consent review step is a consent task: it has no close button, and the participant leaves it with DISAGREE. Both steps hide the bottom bar and bring their own buttons. See Informed consent.

Activity step

RPActivityStep is the base class of interactive tests, such as the cognitive tests in cognition_package. A subclass draws the test and records an RPActivityResult; the base class itself shows an empty screen. See Activity steps.

Quick links

Page · DocumentationCARP DocumentationPage · DocumentationIntroductionPage · DocumentationQuickstartPage · DocumentationTerminologyPage · DocumentationArchitecture