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