• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Updating Widget Content by State
2
3
4There are cases where multiple copies of the same widget are added to the home screen to accommodate different needs. In these cases, the widget content needs to be dynamically updated based on the state. This topic exemplifies how this is implemented. In the following example, two copies of the weather widget are added to the home screen: one for displaying the weather of London, and the other Beijing, both configured to be updated at 07:00 every morning. The widget provider detects the target city, and then displays the city-specific weather information on the widgets.
5
6
7- Widget configuration file: Configure the widget to be automatically updated every 30 minutes.
8
9  ```json
10  {
11    "forms": [
12      {
13        "name": "WidgetUpdateByStatus",
14        "description": "$string:UpdateByStatusFormAbility_desc",
15        "src": "./ets/widgetupdatebystatus/pages/WidgetUpdateByStatusCard.ets",
16        "uiSyntax": "arkts",
17        "window": {
18          "designWidth": 720,
19          "autoDesignWidth": true
20        },
21        "colorMode": "auto",
22        "isDefault": true,
23        "updateEnabled": true,
24        "scheduledUpdateTime": "10:30",
25        "updateDuration": 1,
26        "defaultDimension": "2*2",
27        "supportDimensions": [
28          "2*2"
29        ]
30      }
31    ]
32  }
33  ```
34
35- Widget page: A widget has different states and needs to be updated by state. When the state changes, **postCardAction** is called to notify the EntryFormAbility.
36
37  ```ts
38  let storageUpdateByStatus = new LocalStorage();
39
40  @Entry(storageUpdateByStatus)
41  @Component
42  struct WidgetUpdateByStatusCard {
43    @LocalStorageProp('textA') textA: Resource = $r('app.string.to_be_refreshed');
44    @LocalStorageProp('textB') textB: Resource = $r('app.string.to_be_refreshed');
45    @State selectA: boolean = false;
46    @State selectB: boolean = false;
47
48    build() {
49      Column() {
50        Column() {
51          Row() {
52            Checkbox({ name: 'checkbox1', group: 'checkboxGroup' })
53              .padding(0)
54              .select(false)
55              .margin({ left: 26 })
56              .onChange((value: boolean) => {
57                this.selectA = value;
58                postCardAction(this, {
59                  action: 'message',
60                  params: {
61                    selectA: JSON.stringify(value)
62                  }
63                });
64              })
65            Text($r('app.string.status_a'))
66              .fontColor('#000000')
67              .opacity(0.9)
68              .fontSize(14)
69              .margin({ left: 8 })
70          }
71          .width('100%')
72          .padding(0)
73          .justifyContent(FlexAlign.Start)
74
75          Row() {
76            Checkbox({ name: 'checkbox2', group: 'checkboxGroup' })
77              .padding(0)
78              .select(false)
79              .margin({ left: 26 })
80              .onChange((value: boolean) => {
81                this.selectB = value;
82                postCardAction(this, {
83                  action: 'message',
84                  params: {
85                    selectB: JSON.stringify(value)
86                  }
87                });
88              })
89            Text($r('app.string.status_b'))
90              .fontColor('#000000')
91              .opacity(0.9)
92              .fontSize(14)
93              .margin({ left: 8 })
94          }
95          .width('100%')
96          .position({ y: 32 })
97          .padding(0)
98          .justifyContent(FlexAlign.Start)
99        }
100        .position({ y: 12 })
101
102        Column() {
103          Row() { // Content that is updated only in state A
104            Text($r('app.string.status_a'))
105              .fontColor('#000000')
106              .opacity(0.4)
107              .fontSize(12)
108
109            Text(this.textA)
110              .fontColor('#000000')
111              .opacity(0.4)
112              .fontSize(12)
113          }
114          .margin({ top: '12px', left: 26, right: '26px' })
115
116          Row() { // Content that is updated only in state B
117            Text($r('app.string.status_b'))
118              .fontColor('#000000')
119              .opacity(0.4)
120              .fontSize(12)
121            Text(this.textB)
122              .fontColor('#000000')
123              .opacity(0.4)
124              .fontSize(12)
125          }.margin({ top: '12px', bottom: '21px', left: 26, right: '26px' })
126        }
127        .margin({ top: 80 })
128        .width('100%')
129        .alignItems(HorizontalAlign.Start)
130      }.width('100%').height('100%')
131      .backgroundImage($r('app.media.CardUpdateByStatus'))
132      .backgroundImageSize(ImageSize.Cover)
133    }
134  }
135  ```
136
137- EntryFormAbility: The widget state data is stored in the local database. When the update event callback is triggered, the current widget state is obtained through **formId**, and then content is updated based on the state obtained.
138
139  ```ts
140  import { Want } from '@kit.AbilityKit';
141  import { preferences } from '@kit.ArkData';
142  import { BusinessError } from '@kit.BasicServicesKit';
143  import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit';
144  import { hilog } from '@kit.PerformanceAnalysisKit';
145
146  const TAG: string = 'UpdateByStatusFormAbility';
147  const DOMAIN_NUMBER: number = 0xFF00;
148
149  export default class UpdateByStatusFormAbility extends FormExtensionAbility {
150    onAddForm(want: Want): formBindingData.FormBindingData {
151      let formId: string = '';
152      let isTempCard: boolean;
153      if (want.parameters) {
154        formId = want.parameters[formInfo.FormParam.IDENTITY_KEY].toString();
155        isTempCard = want.parameters[formInfo.FormParam.TEMPORARY_KEY] as boolean;
156        if (isTempCard === false) { // If the widget is a normal one, the widget information is persisted.
157          hilog.info(DOMAIN_NUMBER, TAG, 'Not temp card, init db for:' + formId);
158          let promise: Promise<preferences.Preferences> = preferences.getPreferences(this.context, 'myStore');
159          promise.then(async (storeDB: preferences.Preferences) => {
160            hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded to get preferences.');
161            await storeDB.put('A' + formId, 'false');
162            await storeDB.put('B' + formId, 'false');
163            await storeDB.flush();
164          }).catch((err: BusinessError) => {
165            hilog.info(DOMAIN_NUMBER, TAG, `Failed to get preferences. ${JSON.stringify(err)}`);
166          });
167        }
168    }
169      let formData: Record<string, Object | string> = {};
170      return formBindingData.createFormBindingData(formData);
171    }
172
173    onRemoveForm(formId: string): void {
174      hilog.info(DOMAIN_NUMBER, TAG, 'onRemoveForm, formId:' + formId);
175      let promise = preferences.getPreferences(this.context, 'myStore');
176      promise.then(async (storeDB) => {
177        hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded to get preferences.');
178        await storeDB.delete('A' + formId);
179        await storeDB.delete('B' + formId);
180      }).catch((err: BusinessError) => {
181      hilog.info(DOMAIN_NUMBER, TAG, `Failed to get preferences. ${JSON.stringify(err)}`);
182      });
183    }
184
185    // If the widget is a temporary one, it is recommended that the widget information be persisted when the widget is converted to a normal one.
186    onCastToNormalForm(formId: string): void {
187      hilog.info(DOMAIN_NUMBER, TAG, 'onCastToNormalForm, formId:' + formId);
188      let promise: Promise<preferences.Preferences> = preferences.getPreferences(this.context, 'myStore');
189      promise.then(async (storeDB: preferences.Preferences) => {
190        hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded to get preferences.');
191        await storeDB.put('A' + formId, 'false');
192        await storeDB.put('B' + formId, 'false');
193        await storeDB.flush();
194      }).catch((err: BusinessError) => {
195      hilog.info(DOMAIN_NUMBER, TAG, `Failed to get preferences. ${JSON.stringify(err)}`);
196      });
197    }
198
199    onUpdateForm(formId: string): void {
200      let promise: Promise<preferences.Preferences> = preferences.getPreferences(this.context, 'myStore');
201      promise.then(async (storeDB: preferences.Preferences) => {
202        hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded to get preferences from onUpdateForm.');
203        let stateA = await storeDB.get('A' + formId, 'false');
204        let stateB = await storeDB.get('B' + formId, 'false');
205        // Update textA in state A.
206        if (stateA === 'true') {
207          let param: Record<string, string> = {
208            'textA': 'AAA'
209          };
210          let formInfo: formBindingData.FormBindingData = formBindingData.createFormBindingData(param);
211          await formProvider.updateForm(formId, formInfo);
212        }
213        // Update textB in state B.
214        if (stateB === 'true') {
215          let param: Record<string, string> = {
216            'textB': 'BBB'
217          };
218          let formInfo: formBindingData.FormBindingData = formBindingData.createFormBindingData(param);
219        await formProvider.updateForm(formId, formInfo);
220        }
221        hilog.info(DOMAIN_NUMBER, TAG, `Update form success stateA:${stateA} stateB:${stateB}.`);
222      }).catch((err: BusinessError) => {
223        hilog.info(DOMAIN_NUMBER, TAG, `Failed to get preferences. ${JSON.stringify(err)}`);
224      });
225    }
226
227    onFormEvent(formId: string, message: string): void {
228      // Store the widget state.
229      hilog.info(DOMAIN_NUMBER, TAG, 'onFormEvent formId:' + formId + 'msg:' + message);
230      let promise: Promise<preferences.Preferences> = preferences.getPreferences(this.context, 'myStore');
231      promise.then(async (storeDB: preferences.Preferences) => {
232        hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded to get preferences.');
233        let msg: Record<string, string> = JSON.parse(message);
234        if (msg.selectA !== undefined) {
235          hilog.info(DOMAIN_NUMBER, TAG, 'onFormEvent selectA info:' + msg.selectA);
236          await storeDB.put('A' + formId, msg.selectA);
237        }
238        if (msg.selectB !== undefined) {
239          hilog.info(DOMAIN_NUMBER, TAG, 'onFormEvent selectB info:' + msg.selectB);
240          await storeDB.put('B' + formId, msg.selectB);
241        }
242        await storeDB.flush();
243      }).catch((err: BusinessError) => {
244        hilog.info(DOMAIN_NUMBER, TAG, `Failed to get preferences. ${JSON.stringify(err)}`);
245      });
246    }
247  }
248  ```
249
250
251> **NOTE**
252>
253> When the local database is used for widget information persistence, it is recommended that [TEMPORARY_KEY](../reference/apis-form-kit/js-apis-app-form-formInfo.md#formparam) be used in the [onAddForm](../reference/apis-form-kit/js-apis-app-form-formExtensionAbility.md#formextensionabilityonaddform) lifecycle callback to determine whether the currently added widget is a normal one. If the widget is a normal one, the widget information is directly persisted. If the widget is a temporary one, the widget information is persisted when the widget is converted to a normal one ([onCastToNormalForm](../reference/apis-form-kit/js-apis-app-form-formExtensionAbility.md#formextensionabilityoncasttonormalform)). In addition, the persistent widget information needs to be deleted when the widget is destroyed ([onRemoveForm](../reference/apis-form-kit/js-apis-app-form-formExtensionAbility.md#formextensionabilityonremoveform)), preventing the database size from continuously increasing due to repeated widget addition and deletion.
254