目录

Active Update of ArkTS Widgets

This section provides the development guidelines for active update. For details about the update process, see Active Update.

Active Update by Widget Provider

The widget provider can call updateForm to actively update the widget. It is recommended that this API be used with the widget lifecycle callbacks onFormEvent, onUpdateForm, and onAddForm.

[h2]How to Develop

The following demonstrates how to add a widget to the home screen and update the widget by tapping the update button on the widget.

  1. Create a widget.

  2. Implement the widget layout, add an update button to the widget, and call the postCardAction API to trigger the onFormEvent callback.

    // entry/src/main/ets/updatebymessage/pages/UpdateByMessageCard.ets
    let storageUpdateByMsg = new LocalStorage();
    
    @Entry(storageUpdateByMsg)
    @Component
    struct UpdateByMessageCard {
      // Replace $r('app.string.default_title') and $r('app.string.DescriptionDefault') with the resource files you use.
      @LocalStorageProp('title') title: ResourceStr = $r('app.string.default_title');
      @LocalStorageProp('detail') detail: ResourceStr = $r('app.string.DescriptionDefault');
    
      build() {
        Column() {
          Column() {
            Text(this.title)
              .fontColor('#FFFFFF')
              .opacity(0.9)
              .fontSize(14)
              .margin({ top: '8%', left: '10%' })
            Text(this.detail)
              .fontColor('#FFFFFF')
              .opacity(0.6)
              .fontSize(12)
              .margin({ top: '5%', left: '10%' })
          }.width('100%').height('50%')
          .alignItems(HorizontalAlign.Start)
    
          Row() {
            // ...
            Button() {
              // Replace $r('app.string.update') with the resource file you use.
              Text($r('app.string.update'))
                .fontColor('#45A6F4')
                .fontSize(12)
            }
            .width(120)
            .height(32)
            .margin({ top: '30%', bottom: '10%' })
            .backgroundColor('#FFFFFF')
            .borderRadius(16)
            .onClick(() => {
              postCardAction(this, {
                action: 'message',
                params: { msgTest: 'messageEvent' }
              });
            })
          }.width('100%').height('40%')
          .justifyContent(FlexAlign.Center)
        }
        .width('100%')
        .height('100%')
        .alignItems(HorizontalAlign.Start)
        // Replace $r('app.media.CardEvent') with the resource file you use.
        .backgroundImage($r('app.media.CardEvent'))
        .backgroundImageSize(ImageSize.Cover)
      }
    }
  3. In the implementation of onFormEvent, call the updateForm API to update the widget data.

    // entry/src/main/ets/entryformability/EntryFormAbility.ts
    import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit';
    import { Configuration, Want } from '@kit.AbilityKit';
    import { BusinessError } from '@kit.BasicServicesKit';
    import { hilog } from '@kit.PerformanceAnalysisKit';
    
    
    // entry/src/main/ets/entryformability/EntryFormAbility.ts
    const TAG: string = 'EntryFormAbility';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    export default class EntryFormAbility extends FormExtensionAbility {
      onAddForm(want: Want): formBindingData.FormBindingData {
        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onAddForm');
        hilog.info(DOMAIN_NUMBER, TAG, want.parameters?.[formInfo.FormParam.NAME_KEY] as string);
        // Called when the widget host creates a widget. The widget provider should return the widget data binding class.
        let obj: Record<string, string> = {
          'title': 'titleOnAddForm',
          'detail': 'detailOnAddForm'
        };
        let formData: formBindingData.FormBindingData = formBindingData.createFormBindingData(obj);
        return formData;
      }
    
      onCastToNormalForm(formId: string): void {
        // ...
        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onCastToNormalForm');
      }
    
      onUpdateForm(formId: string): void {
        // If the widget supports interval-based updates, scheduled updates, or active update requests from the widget host, the widget provider needs to override this method to support data updates.
        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onUpdateForm');
        let obj: Record<string, string> = {
          'title': 'titleOnUpdateForm',
          'detail': 'detailOnUpdateForm'
        };
        let formData: formBindingData.FormBindingData = formBindingData.createFormBindingData(obj);
        formProvider.updateForm(formId, formData).catch((error: BusinessError) => {
          hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] updateForm, error:' + JSON.stringify(error));
        });
      }
    
      onChangeFormVisibility(newStatus: Record<string, number>): void {
        // ...
        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onChangeFormVisibility');
      }
    
      onFormEvent(formId: string, message: string): void {
        // ...
        hilog.info(DOMAIN_NUMBER, TAG, `FormAbility onFormEvent, formId = ${formId}, message: ${message}`);
        class FormDataClass {
          title: string = 'Title Update.'; // It matches the widget layout.
          detail: string = 'Description update success.'; // It matches the widget layout.
        }
    
        // Replace the following with the actual widget data to be updated.
        let formData = new FormDataClass();
        let formInfo: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData);
        formProvider.updateForm(formId, formInfo).then(() => {
          hilog.info(DOMAIN_NUMBER, TAG, 'FormAbility updateForm success.');
        }).catch((error: BusinessError) => {
          hilog.error(DOMAIN_NUMBER, TAG, `Operation updateForm failed. Cause: ${JSON.stringify(error)}`);
        });
      }
    
      onRemoveForm(formId: string): void {
        // ...
        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onRemoveForm');
        // ...
      }
    
      onConfigurationUpdate(config: Configuration) {
        // ...
        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onConfigurationUpdate:' + JSON.stringify(config));
      }
    
    
      onAcquireFormState(want: Want): formInfo.FormState {
        // ...
        return formInfo.FormState.READY;
      }
    
    }
  4. The resource file is as follows:

    // entry/src/main/resources/zh_CN/element/string.json
    {
       "string": [
       // ...
         {
           "name": "default_title",
           "value": "Title default."
         },
         {
           "name": "DescriptionDefault",
           "value": "Description default."
         },
         {
           "name": "update",
           "value": "Update."
         }
       ]
    }

[h2]Running Result

Batch Update By Widget Provider

Starting from API version 22, widget providers can batch update widget content. They can call the reloadForms and reloadAllForms APIs in the main process of the application to notify the FormExtension process of batch updates. These APIs can be called only in UIAbility.

[h2]How to Develop

The following demonstrates how to add multiple widgets of an application to the home screen and update the widgets in batches by tapping the update button within the UIAbility.

  1. Create a widget.

  2. Implement the widget layout and add two Text components that can be updated to the widget.

    // entry/src/main/ets/reloadbyuiability/pages/ReloadByUIAbilityCard.ets
    let storageReloadForm = new LocalStorage();
    
    @Entry(storageReloadForm)
    @Component
    struct ReloadByUIAbilityCard {
      // Create two Text components to be updated. The initial content of the first Text is 'Title default' and that of the second Text is 'Description default'. For details about the resource file definition, see step 5.
      @LocalStorageProp('title') title: ResourceStr = $r('app.string.default_title');
      @LocalStorageProp('detail') detail: ResourceStr = $r('app.string.DescriptionDefault');
    
      build() {
        Column() {
          Column() {
            Text(this.title)
              .fontSize(14)
              .margin({ top: '8%', left: '10%' })
            Text(this.detail)
              .fontSize(12)
              .margin({ top: '5%', left: '10%' })
          }.width('100%').height('50%')
          .alignItems(HorizontalAlign.Start)
        }
        .width('100%')
        .height('100%')
        .alignItems(HorizontalAlign.Start)
      }
    }
  3. Implement the onUpdateForm callback in FormExtensionAbility and use the updateForm API to define the widget update logic.

    // entry/src/main/ets/entryformability/EntryFormAbility.ets
    import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit';
    import { Want } from '@kit.AbilityKit';
    import { BusinessError } from '@kit.BasicServicesKit';
    import { hilog } from '@kit.PerformanceAnalysisKit';
    
    const TAG: string = 'EntryFormAbility';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    export default class EntryFormAbility extends FormExtensionAbility {
      onAddForm(want: Want) {
        const formData = '';
        return formBindingData.createFormBindingData(formData);
      }
    
      onCastToNormalForm(formId: string): void {
        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onCastToNormalForm');
      }
    
      onUpdateForm(formId: string) {
    
        class FormDataClass {
          title: string = 'Title: ' + Math.random();
          detail: string = 'Description: ' + Math.random();
        }
    
        let formData = new FormDataClass();
        let formInfo: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData);
        // Update the widget data.
        formProvider.updateForm(formId, formInfo).then(() => {
          hilog.info(DOMAIN_NUMBER, TAG, 'FormAbility updateForm success.');
        }).catch((error: BusinessError) => {
          hilog.error(DOMAIN_NUMBER, TAG, `Operation updateForm failed. code: ${error.code}, message: ${error.message}`);
        });
      }
    
      onFormEvent(formId: string, message: string) {
        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onFormEvent');
      }
    
      onRemoveForm(formId: string) {
        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onRemoveForm');
      }
    
      onAcquireFormState(want: Want) {
        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onAcquireFormState');
        return formInfo.FormState.READY;
      }
    }
  4. Add two batch update buttons to the UIAbility. After the buttons are tapped, the reloadForms or reloadAllForms API is called to trigger the onUpdateForm callback for batch updates.

    // entry/src/main/ets/pages/index.ets
    import { common } from '@kit.AbilityKit';
    import { BusinessError } from '@kit.BasicServicesKit';
    import { formProvider } from '@kit.FormKit';
    
    @Entry
    @Component
    struct Index {
      build() {
        Column({ space: 20 }) {
          Button('reloadForms')
            .onClick(() => {
              try {
                let context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
                let moduleName: string = 'entry';
                let abilityName: string = 'EntryFormAbility';
                let formName: string = 'ReloadByUIAbility';
                formProvider.reloadForms(context, moduleName, abilityName, formName).then((reloadNum: number) => {
                  console.info(`reloadForms success, reload number: ${reloadNum}`);
                }).catch((error: BusinessError) => {
                  console.error(`promise error, code: ${error.code}, message: ${error.message}`);
                });
              } catch (error) {
                console.error(`catch error, code: ${(error as BusinessError).code}, message: ${(error as BusinessError).message}`);
              }
            })
          Button('reloadAllForms')
            .onClick(() => {
              try {
                let context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
                formProvider.reloadAllForms(context).then((reloadNum: number) => {
                  console.info(`reloadAllForms success, reload number: ${reloadNum}`);
                }).catch((error: BusinessError) => {
                  console.error(`promise error, code: ${error.code}, message: ${error.message}`);
                });
              } catch (error) {
                console.error(`catch error, code: ${(error as BusinessError).code}, message: ${(error as BusinessError).message}`);
              }
            })
        }
        .height('100%')
        .width('100%')
        .justifyContent(FlexAlign.Center)
      }
    }
  5. The resource file is as follows:

    // entry/src/main/resources/base/element/string.json
    {
       "string": [
       // ...
         {
           "name": "default_title",
           "value": "Title default."
         },
         {
           "name": "DescriptionDefault",
           "value": "Description default."
         }
       ]
    }

[h2]Running Result