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