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