This content originally appeared on DEV Community and was authored by HarmonyOS
Read the original article:Building a Wearable Reminder App with HarmonyOS NEXT
Introduction
In this guide, we’ll create QuickReminder,a simple, efficient reminder app built with HarmonyOS NEXT and ArkTS. Tailored for wearables, the app emphasizes performance, minimal UI, and native system features like notifications and background tasks.
QuickReminder App
QuickReminderis a lightweight reminder app designed for HarmonyOS wearables, featuring a clean interface for fast input and short-term tasks like workouts or focus sessions.
Reminder Types & Agent-Powered Reminder (ArkTS)
When an application moves to the background or its process is terminated, it may still need to schedule tasks that remind users, such as clock reminders. To support this scenario, HarmonyOS provides agent-powered remindersimplemented via the reminderAgentManager API. These reminders are sent by the system on behalf of the application even when the app is not running.
Currently, the system supports three types of agent-powered reminders:
- Timer: Reminders based on countdown timers.
- Calendar: Reminders tied to calendar events.
- Alarm: Reminders based on alarm clocks. InQuickReminder, we usetimer-based reminders(REMINDER_TYPE_TIMER), implemented via the ReminderRequestTimerstructure. This type is well-suited for wearables, as it supports quick, disposable notifications without requiring persistent storage or user accounts. ## HarmonyOS NEXT Kits Used in QuickReminder and Implementation Overview QuickReminder uses several HarmonyOS NEXT kits:
- BackgroundTasksKit— Publishes and manages agent-powered timer reminders that can execute even when the app is backgrounded or terminated.
- NotificationKit— Delivers native notifications to alert the user when a reminder is triggered.
- AbilityKit— Provides access to the current UI context and app metadata.
- BasicServicesKit— Handles system-level errors through the BusinessError interface, improving reliability. Now, let’s dive into the core implementation and see how the QuickReminder app is built step-by-step. ## Required Permissions To ensure that reminders can run in the background and function without the app staying active, the following permissions must be added to module.json5:
"requestPermissions": [
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING"
},
{
"name": "ohos.permission.PUBLISH_AGENT_REMINDER"
}
]
These permissions are essential to publish and manage agent-based reminders reliably.
UI Architecture
The application defines a single entry component named Index
@Entry
@Component
struct Index {
private context = this.getUIContext().getHostContext() as common.UIAbilityContext;
...
}
Upon launch, the app immediately requests notification permissions via:
notificationManager.requestEnableNotification();
The interface uses a combination ofScroll,Column,Text, andButtoncomponents, optimized for compact, round smartwatch screens. A reactive flagshowInputFormtoggles between the reminder input screen and control panel, ensuring that the user interface remains uncluttered and contextually adaptive.
Reminder Scheduling Logic
Reminder creation is encapsulated within the createReminder() method. Below is a simplified structure of how a timer-based reminder is constructed:
let timerReminder: reminderAgentManager.ReminderRequestTimer = {
reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_TIMER,
triggerTimeInSeconds: timeInSeconds,
title: this.reminderTitle,
content: this.reminderContent,
notificationId: ++this.notificationId
};
This data structure is then passed to the system via:
reminderAgentManager.publishReminder(timerReminder).then((id: number) => {
this.reminderId = id;
this.reminderStatus = 'Reminder successfully set.';
...
}).catch((error: BusinessError) => {
this.reminderStatus = `Error: ${error.message}`;
});
Note: The notification ID is auto-incremented to ensure uniqueness.
Validation and Error Handling
The app proactively validates user input before invoking any system APIs:
if (isNaN(timeInSeconds) || timeInSeconds <= 0) {
this.reminderStatus = 'Invalid time value.';
return;
}
if (!this.reminderTitle || !this.reminderContent) {
this.reminderStatus = 'Please complete all fields.';
return;
}
This ensures that no malformed requests are sent to the reminder system and improves the user experience with immediate error feedback.
Managing Active Reminders
QuickReminder supports simple reminder management functions such as listing and cancelling active timers:
reminderAgentManager.getValidReminders().then((reminders) => {
this.reminderStatus = `Active reminders: ${reminders.length}`;
});
reminderAgentManager.cancelReminder(this.reminderId).then(() => {
this.reminderStatus = 'Reminder cancelled successfully.';
});
The current implementation supports asingle reminder instance at a time, tracked by reminderId. This constraint reduces memory overhead and simplifies logic — a practical design decision for wearable targets.
Reusable Styling and UI Extensions
To maintain design consistency, the app uses @Extend decorators to encapsulate reusable UI styling:
import { reminderAgentManager } from '@kit.BackgroundTasksKit';
import { BusinessError } from '@kit.BasicServicesKit';
import Logger from '../common/utils/Logger';
import { notificationManager } from '@kit.NotificationKit';
import { common } from '@kit.AbilityKit';
@Entry
@Component
struct Index {
private context = this.getUIContext().getHostContext() as common.UIAbilityContext;
private reminderId: number = -1;
@State reminderStatus: string = 'Ready';
@State reminderTitle: string = '';
@State reminderContent: string = '';
@State reminderTime: string = '';
@State showInputForm: boolean = false;
@State notificationId: number = 1000;
aboutToAppear() {
this.openNotificationPermission(this.context);
}
build() {
Scroll() {
Column({ space: 12 }) {
Text('Quick Reminder')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor(Color.Blue)
if (this.showInputForm) {
this.addForm()
} else {
Button('⏰ Create Reminder').onClick(() => {
this.showInputForm = true;
this.createReminder();
}).buttonsStyle()
Button('📋 Show Reminders').onClick(() => {
this.listReminders();
}).buttonsStyle()
Button('❌ Cancel Reminder').onClick(() => {
this.cancelReminder();
}).buttonsStyle()
Text(this.reminderStatus)
.fontSize(10)
.fontColor(Color.Blue)
}
}
.padding(20)
}
.height('100%')
.width('100%')
.backgroundColor(Color.White)
}
private openNotificationPermission(context: common.UIAbilityContext) {
notificationManager.requestEnableNotification(context).then(() => {
Logger.info('Enable notification success');
}).catch((err: Error) => {
Logger.error('Enable notification failed because ' + JSON.stringify(err));
});
}
private createReminder() {
Logger.info('createReminder() started');
const timeInSeconds = parseInt(this.reminderTime);
if (isNaN(timeInSeconds) || timeInSeconds <= 0) {
Logger.info('Invalid time input');
this.reminderStatus = 'Please enter a valid time in seconds.';
return;
}
if (!this.reminderTitle || !this.reminderContent) {
Logger.info('Title or content missing');
this.reminderStatus = 'Please fill in both title and content.';
return;
}
let timerReminder: reminderAgentManager.ReminderRequestTimer = {
reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_TIMER,
triggerTimeInSeconds: timeInSeconds,
title: this.reminderTitle,
content: this.reminderContent,
notificationId: ++this.notificationId
};
Logger.info(`Publishing reminder with ID: ${this.notificationId}`);
reminderAgentManager.publishReminder(timerReminder).then((id: number) => {
Logger.info(`Reminder published successfully with request ID: ${id}`);
this.reminderId = id;
this.reminderStatus = `Reminder set.`;
this.showInputForm = false;
this.reminderTitle = '';
this.reminderContent = '';
this.reminderTime = '';
}).catch((err: BusinessError) => {
Logger.info(`Failed to publish reminder: ${err.code}, ${err.message}`);
this.reminderStatus = `Error: ${err.code}, ${err.message}`;
});
}
private listReminders() {
Logger.info('listReminders() started');
reminderAgentManager.getValidReminders().then((reminders) => {
Logger.info(`Retrieved ${reminders.length} active reminders`);
if (reminders.length === 0) {
this.reminderStatus = 'No active reminders.';
return;
}
this.reminderStatus = `Total reminders: ${reminders.length}`;
}).catch((err: BusinessError) => {
Logger.info(`Error while listing reminders: ${err.code}`);
this.reminderStatus = `Listing error: ${err.code}`;
});
}
private cancelReminder() {
Logger.info('cancelReminder() started');
if (this.reminderId === -1) {
Logger.info('No reminder to cancel');
this.reminderStatus = 'You need to create a reminder first.';
return;
}
reminderAgentManager.cancelReminder(this.reminderId).then(() => {
Logger.info(`Reminder ${this.reminderTitle} canceled successfully`);
this.reminderStatus = `Reminder canceled.`;
this.reminderId = -1;
}).catch((err: BusinessError) => {
Logger.info(`Failed to cancel reminder: ${err.code}`);
this.reminderStatus = `Cancellation error: ${err.code}`;
});
}
@Builder
addForm() {
Column({ space: 8 }) {
// Title
Column({ space: 2 }) {
Text('Title')
.fontSize(8)
.fontColor(Color.Gray)
.width('80%')
.textAlign(TextAlign.Start)
TextInput()
.onChange((val: string) => this.reminderTitle = val)
.inputStyle(this.reminderTitle)
}
// Content
Column({ space: 2 }) {
Text('Content')
.fontSize(8)
.fontColor(Color.Gray)
.width('80%')
.textAlign(TextAlign.Start)
TextInput()
.onChange((val: string) => this.reminderContent = val)
.inputStyle(this.reminderContent)
}
// Time
Column({ space: 2 }) {
Text('Time in seconds')
.fontSize(8)
.fontColor(Color.Gray)
.width('80%')
.textAlign(TextAlign.Start)
TextInput()
.onChange((val: string) => this.reminderTime = val)
.inputStyle(this.reminderTime)
}
Button('💾 Save Reminder').onClick(() => {
Logger.info('Save Reminder button clicked');
this.createReminder();
}).buttonsStyle()
}
}
}
@Extend(Button)
function buttonsStyle() {
.width('60%')
.height(25)
.borderRadius(10)
.fontSize(8)
}
@Extend(TextInput)
function inputStyle(value: string) {
.width('80%')
.height('14%')
.padding(6)
.borderWidth(1)
.borderColor(Color.Grey)
.fontSize(8)
.borderRadius(8)
.fontColor(value ? Color.Blue : Color.Grey)
}
Conclusion
QuickReminder demonstrates how HarmonyOS NEXT and ArkTS can be used to build responsive, lightweight reminder apps optimized for wearable devices. With timer-based reminders, native notifications, and simplified UI, it delivers essential functionality with minimal overhead. The modular structure and validation ensure a stable user experience.
References
Written by Emine INAN
This content originally appeared on DEV Community and was authored by HarmonyOS

HarmonyOS | Sciencx (2025-08-11T13:43:22+00:00) Building a Wearable Reminder App with HarmonyOS NEXT. Retrieved from https://www.scien.cx/2025/08/11/building-a-wearable-reminder-app-with-harmonyos-next/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.