App task model
Configure, execute, render and extend user-facing app tasks in CAMS.
An AppTask is a user-facing task type in CAMS. You define app tasks in the study protocol, CAMS puts each triggered task on a queue, and your app renders that queue and drives each task through its lifecycle.
End-to-end flow
Define an app task in the protocol
Configure an
AppTaskwith task metadata and one or more background measures.Trigger and enqueue a user task
When the task is triggered, CAMS creates a
UserTaskand puts it on the task queue in theAppTaskController.Render and run the task in the app
The app listens to the task queue, renders the task list and each task, and calls the callback methods that start, complete or cancel tasks.
Handle completion and notifications
The runtime APIs handle task state transitions and local notifications.
The examples on this page come from the PulmonaryMonitor app.
Task list
Task done
Configuring app tasks
In PulmonaryMonitor, app tasks are defined in study_protocol_manager.dart. The sensing task at the bottom of the task list comes from this configuration:
This code adds a NoUserTaskTrigger with an AppTask of type sensing and the measures WEATHER and AIR_QUALITY. When triggered, the task is enqueued and can be shown in the app UI. When the user starts it (for example by tapping PRESS HERE TO FINISH TASK), a background task collects the measures and the app task is marked as done.
An app task follows the Trigger-Task-Measure domain model in CAMS: a trigger activates the task, and the task defines which measures to collect. Unlike a BackgroundTask, an AppTask has user-facing properties:
| Property | Purpose |
|---|---|
type | Task type, for example sensing, survey or audio. |
title | Short title shown on the task card. |
description | Short descriptive text shown on the task card. |
instructions | More detailed guidance for the user. |
time to complete | Estimated completion time for the task. |
notification | Whether a notification is sent when a new task is available. |
expire | When the task expires and is no longer available. |
For the rest of the setup, see the PulmonaryMonitor source.
App task execution
When an app task is triggered, an AppTaskExecutor executes it:
- It creates a
UserTaskfrom theAppTaskconfiguration. The user task embeds aBackgroundTaskExecutorthat collects the measures once the task is started. - It enqueues the user task in the
AppTaskController. TheAppTaskControlleris a singleton and is central to handling app tasks, including creating notifications. - All triggered user tasks are available in
userTaskQueuefor custom rendering in the app. - The user task has a set of callback methods that the app calls to mark the task as started, done, canceled or expired.
- When the user task is started, its embedded
BackgroundTaskExecutorcollects the measures. Collection stops when the task is marked as done or, for one-time measures, when the measures have been collected.
The user task queue
Use the userTaskQueue property on AppTaskController to access enqueued tasks. In PulmonaryMonitor this happens in task_list_view_model.dart:
To react to state changes, listen to events on the user task queue:
Rendering user tasks in the app
You can render enqueued user tasks in any app-specific UI. In PulmonaryMonitor, the task list shown above is implemented in task_list_page.dart.
This widget builds the scrollable list view of task cards:
Each card uses a StreamBuilder on the task's stateEvents to render the user task:
When the user taps PRESS HERE TO FINISH TASK, the app starts the user task with userTask.onStart(). If the task has a widget, the app pushes that widget to the UI. A non-UI sensing task runs for 10 seconds.
User task callbacks
The app calls these callback methods on a UserTask:
| Callback | Purpose |
|---|---|
onStart | Starts the task and starts collecting the defined measures. |
onCancel | Cancels the task. |
onDone | Marks the task as done and typically stops measure collection. |
onExpired | Handles expiration and removes the task from the queue. |
onNotification | Called when the user taps the OS notification for this task. |
Notifications
If you enable notification on an app task, CAMS sends a local notification with the task title and description.
Notifications use the flutter_local_notifications plugin, which requires app-level configuration. See Installation.
Types of app tasks
CAMS supports the following app task types, all enumerated in the AppTask class:
| Type | Purpose |
|---|---|
SENSING_TYPE | Collects sensing data in a background task. |
SURVEY_TYPE | Starts a survey from the carp_survey_package. |
INFORMED_CONSENT_TYPE | Shows an informed consent flow to the user, typically using the research_package. |
COGNITIVE_ASSESSMENT_TYPE | Runs a cognitive assessment from the carp_survey_package. |
HEALTH_ASSESSMENT_TYPE | Collects health data via the carp_health_package. |
AUDIO_TYPE, VIDEO_TYPE, IMAGE_TYPE | Collects media data via the carp_audio_package. |
Extending the app task model
To add a custom app task, create a new UserTask type and register a factory for it. This works like extending measures and probes, as described in Extending CAMS.
Create a custom user task
The AudioUserTask in PulmonaryMonitor is a custom app task. It is implemented in audio_user_task.dart:
Define a user task factory
CAMS uses a UserTaskFactory to create and enqueue the right user task type, so you need a factory for your custom types. The factory for AudioUserTask:
Register the factory
Register the custom factory in AppTaskController:
Do this while you initialize CAMS in the app. In PulmonaryMonitor, registration happens when the Sensing class is created in sensing.dart.