Keeping a bindSheet Semi-Modal Page Visible While Hiding Its Host Component
Requirement Description
In a HarmonyOS ArkUI application, a semi-modal page is bound to a host component using bindSheet. When the user taps the host component, the semi-modal page (sheet) appears as expected.
However, the host component itself can later be hidden based on business logic. The requirement is:
Even when the host component becomes hidden, the bound semi-modal page should remain visible until the user closes it.
In the current implementation, when the host component is hidden via conditional rendering, both the host component and its bound semi-modal page disappear at the same time, which does not meet the UX requirement.
Background Knowledge
1. Semi-modal page (bindSheet)
The semi-modal page is a modal, non-fullscreen popup that partially overlays the parent view while keeping part of the underlying UI visible. It is implemented via the bindSheet sheet transition attribute. (Huawei Developer)
Key points:
-
bindSheetbinds a sheet to a host component. - The sheet is typically shown when the host component is tapped.
- The sheet’s size can be customized via
SheetOptions.height.
If the bound host component is removed from the component tree, the sheet also disappears because its lifecycle is tied to that host.
2. Visibility vs Conditional Rendering (if)
Visibility control is a universal ArkUI attribute that controls whether a component is visible, hidden but occupying space, or completely removed from layout. (Gitee)
-
Visibility.Visible– Component is visible. -
Visibility.Hidden– Component is not visible but still occupies layout space. -
Visibility.None– Component is not visible and does not occupy layout space, but it still exists in the component tree.
By contrast:
- Conditional rendering with
if/elseadds or removes components from the component tree entirely. - When the host component of
bindSheetis removed viaiflogic, the sheet is removed as well.
For scenarios where a component frequently switches between shown and hidden (like a host for bindSheet), using visibility is preferred over conditional rendering for both performance and lifecycle stability. (Gitee)
Implementation Steps
1.Identify the problem pattern
The original implementation uses an if (isShow) block to decide whether the host component (bound to bindSheet) exists:
if (this.isShow) {
Text('Component bound to bindSheet')
.bindSheet($$this.sheetShow, this.SheetBuilder(), {
height: 500
})
}
Copy codeCopy code
2.Once isShow is set to false, the host component is removed from the component tree, and the semi-modal page disappears as well.
3.Keep the host component in the component tree
Instead of removing the component using if, keep the component always rendered and control its visibility via the visibility attribute:
Text('Component bound to bindSheet')
.visibility(this.isShow ? Visibility.Visible : Visibility.None)
.bindSheet($$this.sheetShow, this.SheetBuilder(), {
height: 500
})
Copy codeCopy code
- When
isShowisfalse, the host component is not visible and does not occupy space. - But it still exists in the component tree, so
bindSheetcontinues to work and the semi-modal remains visible until explicitly closed.
4.Control the timing
After opening the semi-modal page, you can hide the host component after a delay (for example, 3 seconds), while leaving the sheet visible:
Text('Click')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.sheetShow = true;
setTimeout(() => {
this.isShow = false; // Now only visibility changes, not existence.
}, 3000);
})
Copy codeCopy code
5.Apply the same pattern for HarmonyOS wearable
On HarmonyOS 6 / HarmonyOS wearable, the same core idea works:
- The UI is adapted to a watch form-factor (smaller height, percentages instead of large absolute
vp). -
bindSheet+visibilitystill control semi-modal and host independently. - Only layout and height values need to be tuned (for example, using
"60%"of screen height instead of500).
Code Snippet / Configuration
1. Problematic Sample (Host Removed with if)
@Entry
@Component
struct Index {
@State isShow: boolean = true;
@State sheetShow: boolean = false;
@Builder
SheetBuilder() {
Text('Semi-modal content')
.margin({ top: 16 })
}
build() {
Column() {
Text('Click')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.sheetShow = !this.sheetShow;
setTimeout(() => {
// Host component is removed from the tree
this.isShow = false;
}, 3000);
});
if (this.isShow) {
Text('Component bound to bindSheet')
.bindSheet($$this.sheetShow, this.SheetBuilder(), {
height: 500
});
}
}
.padding(24)
.height('100%')
.width('100%')
}
}Copy codeCopy code
Effect: After 3 seconds, both the host text and the semi-modal page disappear.
2. Corrected Sample (Host Hidden via visibility)
@Entry
@Component
struct Index {
@State isShow: boolean = true;
@State sheetShow: boolean = false;
@Builder
SheetBuilder() {
Column() {
Text('Semi-modal content')
.margin({ top: 16 })
Button('Close')
.margin({ top: 16 })
.onClick(() => {
this.sheetShow = false;
// Optionally show host again
this.isShow = true;
})
}
.width('100%')
.padding(16)
}
build() {
Column() {
Text('Click')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.sheetShow = true;
setTimeout(() => {
// Only hide visually, keep in component tree
this.isShow = false;
}, 3000);
});
Text('Component bound to bindSheet')
.visibility(this.isShow ? Visibility.Visible : Visibility.None)
.bindSheet($$this.sheetShow, this.SheetBuilder(), {
height: 500
})
}
.padding(24)
.height('100%')
.width('100%')
}
}Copy codeCopy code
Complete Index.ets
@Entry
@Component
struct Index {
@State sheetShow: boolean = false;
@State hostVisible: boolean = true;
@Builder
SheetContent() {
Column() {
Text('Semi-modal content')
.fontSize(16)
.margin({ top: 12 })
Button('Close')
.margin({ top: 12 })
.fontSize(14)
.onClick(() => {
this.sheetShow = false;
this.hostVisible = true;
})
}
.width('100%')
.padding(16)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
build() {
Column() {
Text('Show sheet')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.sheetShow = true;
// Hide host component after 3 seconds, but keep sheet visible
setTimeout(() => {
this.hostVisible = false;
}, 3000);
})
.margin({ bottom: 24 })
// Host component bound to bindSheet, only visibility is toggled
Text('Host component')
.fontSize(14)
.visibility(this.hostVisible ? Visibility.Visible : Visibility.None)
.bindSheet($$this.sheetShow, this.SheetContent(), {
// For wearable, use a percentage of screen height
height: '60%'
})
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.padding(12)
}
}Copy codeCopy code
Effect:
- After tapping "Click", the semi-modal page appears.
- Three seconds later, the host text becomes invisible (and no longer occupies space), but the semi-modal page remains visible.
- The user can close the semi-modal page from inside the sheet.
This pattern is directly reusable on HarmonyOS wearable; only UI sizes should be adjusted.
Test Results
- Verified that:
- The semi-modal page opens correctly when the host component is tapped.
- After the host component’s
visibilityswitches toVisibility.None, the semi-modal stays visible. - Closing the semi-modal from within the sheet works and can optionally restore the host’s visibility.
- Static analysis (Code Check) passes with no issues; Code Check clearing screenshot was captured in the original article.
Limitations or Considerations
-
This approach is ideal when:
- The host component and semi-modal page frequently toggle between visible and hidden.
- You want to avoid destroying and recreating the component tree for performance reasons. (Gitee)
-
For components that:
- Are rarely shown, or
- Consume a large amount of memory,
it may still be better to use conditional rendering (if/else) to fully destroy them when not needed, to save memory. (Gitee)
-
The sample environment from the original case:
- Supports API Version 19 Release or later.
- Built with HarmonyOS 5.1.1 Release SDK or later.
- Compiled and run using DevEco Studio 5.1.1 Release or later.
-
For HarmonyOS 6 / HarmonyOS wearable, the same visibility logic applies; you mainly need to:
- Update SDK/API version.
- Re-adjust layout (e.g., using percentages or
SheetSizefor watch screens). (Gitee)
Related Documents or Links
- Sheet transition reference:
Sheet Transition Doc. (Huawei Developer)
- Visibility attribute reference:
Visibility Doc. (Gitee)
- Enum definitions (including
Visibility):
ArkUI Enums Reference. (Gitee)
- ArkTS introduction:
ArkTS Overview Guide. (Huawei Developer)

Top comments (0)