DEV Community

HarmonyOS
HarmonyOS

Posted on

Introduction to Form Kit, Creating Dynamic Widget on Wearables

Read the original article:Introduction to Form Kit, Creating Dynamic Widget on Wearables

Context

Form Kit delivers a development framework and APIs to integrate application data into system interfaces such as the home screen. It enables key user information or common operations to be converted into service widgets (known as 'widgets'), which users can place on their home screen for instant information access and direct service engagement. In this article we will be creating simple dynamic widget that will send current count message to the app upon click on the widget. Following is the end result:

kbs.gif

Description

kbs--de168e5e6f59404d9133cd079e09b4d9-11857.png

Fundamental Concepts

  • Widget Host: The container displaying widget content (e.g., home screen). Manages widget placement, interactions, and lifecycle (add/delete/display).
  • Widget Provider: The application or Quick App supplying the widget. Handles UI design, data updates, and interaction logic.
  • Widget Manager: A system service acting as the bridge between providers and hosts. Manages device-wide widget data, processes queries, and notifies providers of events (add/delete/refresh/click).

To add widgets in wearables swipe from right of the wearables to the left and select the region to add the widget.

kbs--85536a9abccf4aacaffd1d0acf832727-3e527.png

kbs--5cb113fcacde4ec8824518f91d34ec54-3a811.png

Note: adding widget on wearables emulator is only supported after 6.0.0(20) version.

ArkTS Widget Types

ArkTS widgets are categorized into two types: dynamic and static. While both share the same underlying framework and rendering pipeline, static widgets diverge by caching the final rendered frame as an image. After this snapshot is taken, the widget host releases all runtime resources tied to the widget instance to minimize memory usage. However, frequent updates to static widgets trigger repeated resource allocation and deallocation cycles, inadvertently increasing power consumption.

Widget Type Capabilities Use Case Trade-offs
Static Widget Limited to UI components and layout. Ideal for displaying unchanging content (e.g., icons/text) with optional navigation via FormLink. Ultra-low memory footprint but lacks interactivity; updates incur resource-recreation overhead.
Dynamic Widget Full suite: UI components, layouts, event handling, and custom animations. Suited for interactive or real-time content (e.g., live data, animations). Rich functionality at the cost of sustained memory usage and higher system resource demands.

Dynamic Widget

For interactive ArkTS widgets, the postCardAction API bridges Card.ets and FormExtensionAbility. It handles router, message, call, and touch events.

Use-cases

  • router: tapping the widget opens a page inside the app.
  • call: tapping starts UIAbility in the background so it can launch long-running tasks such as music playback.
  • message: tapping triggers FormExtensionAbility, which informs the app through onFormEvent.

Static Widget

Static widgets rely on the FormLink component for provider-side interaction; it supports router, message and call events (details in FormLink).

Solution

1.Create dynamic widget by right clicking on the module.

kbs--0a9d1d6a8ad342739dbfe64a67559fb9-37dc0.png

2.Select Template:

kbs--1430f539ced64830a00f58c782b25578-a4c4.png

3.Select dimension for the widget and the language with service name:

kbs--6673368db9794423880400e3b063e084-6013.png

Widget Directory:

kbs--f54a9b49117c44b49e9313f72f409c46-1fed4.png

4.Widget Card:

   @Entry
   @Component
   struct Dynamic_widgetCard {
     @State counter: number = 0;
     /*
      * The title.
      */
     readonly title: string = 'Counter';
     /*
      * The action type.
      */
     readonly actionType: string = 'router';
     /*
      * The ability name.
      */
     readonly abilityName: string = 'EntryAbility';
     /*
      * The message.
      */
     readonly message: string = 'The counter is: ';
     /*
      * The width percentage setting.
      */
     readonly fullWidthPercent: string = '100%';
     /*
      * The height percentage setting.
      */
     readonly fullHeightPercent: string = '100%';

     build() {
       Row() {
         Column() {
           Text(this.title)
             .fontSize($r('app.float.font_size'))
             .fontWeight(FontWeight.Medium)
             .fontColor($r('sys.color.font'))
           Row() {
             SymbolGlyph($r('sys.symbol.minus'))
               .fontSize(20)
               .fontWeight(FontWeight.Medium)
               .fontColor([$r('sys.color.font')])
               .onClick(() => {
                 this.counter -= 1;
               })
             Text(`${this.counter}`)
               .fontSize($r('app.float.font_size'))
               .fontWeight(FontWeight.Medium)
               .fontColor($r('sys.color.font'))
             SymbolGlyph($r('sys.symbol.plus'))
               .fontColor([$r('sys.color.font')])
               .fontSize(20)
               .fontWeight(FontWeight.Medium)
               .onClick(() => {
                 this.counter += 1;
               })
           }
           .justifyContent(FlexAlign.Center)
           .alignItems(VerticalAlign.Center)
           .width(this.fullWidthPercent)
         }
         .width(this.fullWidthPercent)
       }
       .height(this.fullHeightPercent)
       .backgroundColor($r('sys.color.comp_background_primary'))
       .onClick(() => {
         postCardAction(this, {
           action: this.actionType,
           abilityName: this.abilityName,
           params: {
             message: `${this.message}: ${this.counter}`
           }
         });
       })
     }
   }
Enter fullscreen mode Exit fullscreen mode

5.The postCardAction API used for interaction between the widget internal and the provider application and can be called only in the widget. Following code sample redirects to modules given ability with message.

   postCardAction(this, {
           action: this.actionType,
           abilityName: this.abilityName,
           params: {
             message: `${this.message}: ${this.counter}`
           }
         });
Enter fullscreen mode Exit fullscreen mode

6.In the metadata configuration of FormExtensionAbility, you can reference the resource index for the widgets configuration. For example, setting resource to $profile:form_config uses resources/base/profile/form_config.json in the development view as the widgets profile configuration file. The form_config.json file is generated automatically when you create a widget.

   {
                 "name": "EntryFormAbility",
                 "srcEntry": "./ets/entryformability/EntryFormAbility.ets",
                 "label": "$string:EntryFormAbility_label",
                 "description": "$string:EntryFormAbility_desc",
                 "type": "form",
                 "metadata": [
                 {
                     "name": "ohos.extension.form",
                     "resource": "$profile:form_config"
                   }
                   ]
    }
Enter fullscreen mode Exit fullscreen mode

see https://developer.huawei.com/consumer/en/doc/harmonyos-guides/arkts-ui-widget-configuration#fields-in-configuration-file for full form_config.json configuration information.

7.Read the params send by postCardAction when app is in the foreground and save the value in tha AppStorage:

   onForeground(): void {
     const want: Want = this.lastRequestWant;
     if (want.parameters) {
       const message = want.parameters['message'] ?? '';
       if (message != '') {
         AppStorage.setOrCreate('message', message);
       }
       console.log(`message => ${JSON.stringify(want.parameters, null, 2)}`);
     }
     // Ability has brought to foreground
     hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
   }
Enter fullscreen mode Exit fullscreen mode

8.Link the data using StorageLink in the component:

   @Entry
   @Component
   struct Index {
     @StorageLink('message') message: string = "Index Page";

     build() {
       RelativeContainer() {
         Text(this.message)
           .id('HelloWorld')
           .fontSize(14)
           .fontWeight(FontWeight.Bold)
           .alignRules({
             center: { anchor: '__container__', align: VerticalAlign.Center },
             middle: { anchor: '__container__', align: HorizontalAlign.Center }
           })
       }
       .height('100%')
       .width('100%')
     }
   }
Enter fullscreen mode Exit fullscreen mode

Key Takeaways

  • Core Purpose of Form Kit: Form Kit is a development framework designed to integrate app data into system interfaces (like home screens) through service widgets.

  • Widget Functionality: Enables developers to convert key user information or common app operations into interactive home screen widgets, providing users with instant information access.

  • Article Focus:

The guide demonstrated how to create a

simple dynamic widget

with:

  • Real-time interactivity: Widget clicks trigger actions
  • Data synchronization: Sends current count data to the app
  • Dynamic updates: Widget state changes based on user interaction

    • End Result:

A functional widget that:

  • Displays a counter
  • Updates on each user click
  • Communicates the current count back to the host application

    • Developer Value:

Teaches practical implementation of:

  • Widget-to-app communication
  • State management in system-level components
  • Dynamic UI updates for home screen

    • User Experience Benefit: Reduces friction by allowing one-tap access to app functionality from the home screen, bypassing full app launches.

Additional Resources

Written by Taskhyn Maksim

Top comments (0)