Building a Wearable Reminder App with HarmonyOS NEXT

Read the original article:Building a Wearable Reminder App with HarmonyOS NEXT

Photo by Donald Wu on Unsplash

Introduction

In this guide, we’ll create QuickReminder,a simple, efficient reminder app built with HarmonyOS NEXT and ArkTS. Ta…


This content originally appeared on DEV Community and was authored by HarmonyOS

Read the original article:Building a Wearable Reminder App with HarmonyOS NEXT

Photo by Donald Wu on Unsplash

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

App Preview

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

https://developer.huawei.com/consumer/en/doc/harmonyos-guides-V13/agent-powered-reminder-V13?source=post_page-----6b14d470ca54---------------------------------------

Written by Emine INAN


This content originally appeared on DEV Community and was authored by HarmonyOS


Print Share Comment Cite Upload Translate Updates
APA

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/

MLA
" » Building a Wearable Reminder App with HarmonyOS NEXT." HarmonyOS | Sciencx - Monday August 11, 2025, https://www.scien.cx/2025/08/11/building-a-wearable-reminder-app-with-harmonyos-next/
HARVARD
HarmonyOS | Sciencx Monday August 11, 2025 » Building a Wearable Reminder App with HarmonyOS NEXT., viewed ,<https://www.scien.cx/2025/08/11/building-a-wearable-reminder-app-with-harmonyos-next/>
VANCOUVER
HarmonyOS | Sciencx - » Building a Wearable Reminder App with HarmonyOS NEXT. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/08/11/building-a-wearable-reminder-app-with-harmonyos-next/
CHICAGO
" » Building a Wearable Reminder App with HarmonyOS NEXT." HarmonyOS | Sciencx - Accessed . https://www.scien.cx/2025/08/11/building-a-wearable-reminder-app-with-harmonyos-next/
IEEE
" » Building a Wearable Reminder App with HarmonyOS NEXT." HarmonyOS | Sciencx [Online]. Available: https://www.scien.cx/2025/08/11/building-a-wearable-reminder-app-with-harmonyos-next/. [Accessed: ]
rf:citation
» Building a Wearable Reminder App with HarmonyOS NEXT | HarmonyOS | Sciencx | 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.

You must be logged in to translate posts. Please log in or register.