Context
How to pass the screenshot of a component to other components for display?
Description
- Goal: Capture a screenshot of a component (using componentSnapshot.get()) and display it in a custom dialog.
- Issue: The CustomDialogController is initialized early (with pixMap = undefined). Later updates to pixMap do not propagate to the dialog because the controller’s builder uses stale initial values.
- Key Methods:
- componentSnapshot.get(id, options) captures the component as PixelMap.
-
@Link/@Propdecorators are intended for parent-child data synchronization.
Solution / Approach
import { image } from '@kit.ImageKit'
import { componentSnapshot } from '@kit.ArkUI'
@Entry
@Component
struct Index {
@State items: string[] =
['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32']
@State pixMap: image.PixelMap | undefined = undefined
@State mText: string = 'I am the Content'
// In the dialogController, I tried passing pixMap, options, and buffer, but it didn't work.
dialogController: CustomDialogController =
new CustomDialogController({ builder: CustomDialogComponent({ pixelMap: this.pixMap, text: this.mText }) })
build() {
Column() {
Text('I am the title.')
.width('100%')
.height(50)
.textAlign(TextAlign.Center)
.fontColor(Color.Black)
.backgroundColor(Color.Gray)
Scroll() {
Column() {
ForEach(this.items, (item: string) => {
Text('I am content.' + item).width('100%').height(50).fontColor(Color.Red)
})
}.width('100%')
}.width('100%').layoutWeight(1).id('mainScroll')
Blank().width('100%').height(1).backgroundColor(Color.Black)
// This can be used as a comparative test.
Image(this.pixMap).width('100%').layoutWeight(0.5).objectFit(ImageFit.Contain)
Button('Click to take a screenshot').width(100).height(45).onClick(() => {
componentSnapshot.get('mainScroll', { scale: 0.8, waitUntilRenderFinished: true })
.then((pixMap: image.PixelMap) => {
// If using the pixMap method directly, just this one line is sufficient, and comparative testing is also possible.
this.pixMap = pixMap
this.dialogController.open()
this.mText = 'Screenshot successful'
})
.catch((err: Error) => {
console.info('Screenshot failed:' + JSON.stringify(err))
})
})
}.width('100%').height('100%').backgroundColor(Color.White)
}
}
Popup component code:
@CustomDialog
struct CustomDialogComponent {
controller?: CustomDialogController
// Using @Prop or @Link here is not feasible without passing the buffer.
@Link pixelMap: image.PixelMap | undefined /*= undefined*/
@Prop text: string | undefined
build() {
Column() {
Text(this.text).width('100%').height(50).textAlign(TextAlign.Center).fontColor(Color.Green)
Image(this.pixelMap).width(100).objectFit(ImageFit.Contain)
Button('Close')
.width(100)
.height(45)
.backgroundColor('#ffcc0000')
.onClick(() => {
this.controller?.close()
})
.margin({ top: 10 })
}.backgroundColor(Color.White)
}
}
Key Takeaways:
- The issue in the original code was due to the
CustomDialogControllerbeing created too early (with initial undefined PixelMap) and not being updated when the PixelMap becomes available. - By conditionally rendering the
CustomDialogcomponent and using@Propto pass the PixelMap, we ensure that the dialog is built with the latest captured image. - This approach avoids the complexity of managing a
CustomDialogControllerand allows for a more straightforward data flow.
Top comments (0)