Context
When a singleton class instance is decorated with @State, changing its internal properties does not trigger a UI refresh.
For example: in Page2, after toggling the color and returning to the Index page, the UI does not update accordingly.
Description
-
@Stateallows UI to refresh when the state variable itself changes. - However, it does not track property changes inside a class instance.
- To observe inner property changes,
@Observedis needed. - Alternatively,
AppStoragecan provide application-level state sharing, and variables with@StorageLinkwill synchronize automatically.
Solution / Approach
Option 1: Use @Observed
- Decorate the singleton class with
@Observed. - This makes
@Statecapable of observing property changes within the instance.
@Observed
export class VoiceReadHelper {
private static instance: VoiceReadHelper | null = null
isSpeaking = false
private constructor() { }
static getInstance(): VoiceReadHelper {
if (VoiceReadHelper.instance == null) {
VoiceReadHelper.instance = new VoiceReadHelper()
}
return VoiceReadHelper.instance
}
}
Option 2: Use AppStorage + @StorageLink
- Save the singleton instance into
AppStoragewhen created. - Use
@StorageLinkin pages to bind the shared instance for two-way synchronization.
// VoiceReadHelper.ets
export class VoiceReadHelper {
private static instance: VoiceReadHelper | null = null
isSpeaking = false
private constructor() { }
static getInstance(): VoiceReadHelper {
if (VoiceReadHelper.instance == null) {
VoiceReadHelper.instance = new VoiceReadHelper()
AppStorage.setOrCreate('VoiceReadHelper', VoiceReadHelper.instance)
}
return VoiceReadHelper.instance
}
}
Copy codeCopy code
// Index.ets
import { VoiceReadHelper } from './VoiceReadHelper'
@Entry
@Component
struct Index {
@StorageLink('voiceReadHelper') voiceReadHelper: VoiceReadHelper = VoiceReadHelper.getInstance()
build() {
Column() {
Text()
.width(100)
.height(100)
.backgroundColor(this.voiceReadHelper.isSpeaking ? '#61CFBE' : '#8981F7')
.borderRadius(8)
.margin({ bottom: 100 })
.onClick(() => {
this.voiceReadHelper.isSpeaking = !this.voiceReadHelper.isSpeaking
})
Text('Go to Page2').onClick(() => {
this.getUIContext().getRouter().pushUrl({ url: 'pages/Page2' });
})
}
.height('100%')
.width('100%')
}
}
Copy codeCopy code
// Page2.ets
import { VoiceReadHelper } from './VoiceReadHelper'
@Entry
@Component
struct Page2 {
@StorageLink('voiceReadHelper') voiceReadHelper: VoiceReadHelper = VoiceReadHelper.getInstance()
build() {
Column() {
Text()
.width(100)
.height(100)
.backgroundColor(this.voiceReadHelper.isSpeaking ? '#61CFBE' : '#8981F7')
.borderRadius(8)
.margin({ bottom: 100 })
.onClick(() => {
this.voiceReadHelper.isSpeaking = !this.voiceReadHelper.isSpeaking
})
Text('This is Page2')
}
.height('100%')
.width('100%')
}
}
Key Takeaways
-
@Statecannot observe changes to properties inside a class instance. - Use
@Observedto make properties inside the class observable. - Use
AppStoragewith@StorageLinkfor application-wide state sharing and automatic synchronization across pages. - Applicable for:
- Theme switching
- Configuration management (e.g., user preferences)
- Message/notification systems
Additional Resources
https://developer.huawei.com/consumer/en/doc/harmonyos-guides/arkts-state-management-overview
https://developer.huawei.com/consumer/en/doc/harmonyos-guides/arkts-observed-and-objectlink
Top comments (0)