1# Custom Dialog Box (CustomDialog)
2
3A custom dialog box is a dialog box you customize by using APIs of the **CustomDialogController** class. You can set the style and content to your preference for a custom dialog box.
4
5> **NOTE**
6>
7> The APIs of this module are supported since API version 7. Updates will be marked with a superscript to indicate their earliest API version.
8
9
10## APIs
11
12CustomDialogController(value: CustomDialogControllerOptions)
13
14Defines a custom dialog box.
15
16> **NOTE**
17>
18> No parameters of the custom dialog box can be dynamically updated.
19
20**System capability**: SystemCapability.ArkUI.ArkUI.Full
21
22**Parameters**
23
24| Name| Type                                                        | Mandatory| Description                  |
25| ------ | ------------------------------------------------------------ | ---- | ---------------------- |
26| value  | [CustomDialogControllerOptions](#customdialogcontrolleroptions) | Yes  | Parameters of the custom dialog box.|
27
28## CustomDialogControllerOptions
29
30| Name                          | Type                                    | Mandatory  | Description                                    |
31| ----------------------------- | ---------------------------------------- | ---- | ---------------------------------------- |
32| builder                       | [CustomDialog](../../../ui/arkts-common-components-custom-dialog.md) | Yes   | Builder of the custom dialog box content.<br>**NOTE**<br>If the builder uses a callback as the input parameter, as in **build: custombuilder({ callback: ()=> {...}})**, pay attention to the binding of **this**.<br>To listen for data changes in the builder, use the @Link decorator; other decorators, such as @Prop and @ObjectLink, do not apply.<br>**Atomic service API**: This API can be used in atomic services since API version 11.|
33| cancel                        | () =&gt; void                  | No   | Callback invoked when the dialog box is closed after the Back button or mask is touched or the Esc key is pressed.<br>**Atomic service API**: This API can be used in atomic services since API version 11.|
34| autoCancel                    | boolean                                  | No   | Whether to close the dialog box when the mask is touched. The value **true** means to close the dialog box when the mask is touched, and **false** means the opposite.<br>Default value: **true**<br>**Atomic service API**: This API can be used in atomic services since API version 11.|
35| alignment                     | [DialogAlignment](ts-methods-alert-dialog-box.md#dialogalignment) | No   | Alignment mode of the dialog box in the vertical direction.<br>Default value: **DialogAlignment.Default**<br>**Atomic service API**: This API can be used in atomic services since API version 11.|
36| offset                        | [Offset](ts-types.md#offset)             | No   | Offset of the dialog box relative to the alignment position.<br>**Atomic service API**: This API can be used in atomic services since API version 11.                  |
37| customStyle                   | boolean                                  | No   | Whether to use a custom style for the dialog box.<br>When this parameter is set to **false** (default value):<br>1. The rounded corner radius is 32 vp.<br>2. If the width and height of the dialog box are not set, the dialog box automatically adapts its width to the grid system and its height to the child components<br>3. The set width of the dialog box cannot exceed the maximum width in the default style (100% width for a custom node), and the set height cannot exceed the maximum height (100% height for a custom node).<br>When this parameter is set to **true**:<br>1. The rounded corner radius is 0, and the background color is transparent.<br>2. The width, height, border width, border style, border color, and shadow width cannot be set for the dialog box.<br>**Atomic service API**: This API can be used in atomic services since API version 11.|
38| gridCount<sup>8+</sup>        | number                                   | No   | Number of [grid columns](../../../ui/arkts-layout-development-grid-layout.md) occupied by the dialog box.<br>The default value is subject to the window size, and the maximum value is the maximum number of columns supported by the system. If this parameter is set to an invalid value, the default value is used.<br>**Atomic service API**: This API can be used in atomic services since API version 11.|
39| maskColor<sup>10+</sup>       | [ResourceColor](ts-types.md#resourcecolor) | No   | Custom mask color.<br>Default value: **0x33000000**<br>**Atomic service API**: This API can be used in atomic services since API version 11.             |
40| maskRect<sup>10+</sup>        | [Rectangle](ts-methods-alert-dialog-box.md#rectangle8) | No    | Mask area of the dialog box. Events outside the mask area are transparently transmitted, and events within the mask area are not.<br>Default value: **{ x: 0, y: 0, width: '100%', height: '100%' }**<br>**NOTE**<br>**maskRect** does not take effect when **showInSubWindow** is set to **true**.<br>**Atomic service API**: This API can be used in atomic services since API version 11.|
41| openAnimation<sup>10+</sup>   | [AnimateParam](ts-explicit-animation.md#animateparam) | No   | Parameters for defining the open animation of the dialog box.<br>**NOTE**<br>**tempo**: The default value is **1**; any other value is handled as the default value.<br>**iterations**: The default value is **1**, indicating that the animation is played once; any other value is handled as the default value.<br>**playMode**: The default value is **PlayMode.Normal**; any other value is handled as the default value.<br>**Atomic service API**: This API can be used in atomic services since API version 11.|
42| closeAnimation<sup>10+</sup>  | [AnimateParam](ts-explicit-animation.md#animateparam) | No   | Parameters for defining the close animation of the dialog box.<br>**NOTE**<br>**tempo**: The default value is **1**; any other value is handled as the default value.<br>**iterations**: The default value is **1**, indicating that the animation is played once; any other value is handled as the default value.<br>**playMode**: The default value is **PlayMode.Normal**; any other value is handled as the default value.<br>For page transition, you are advised to use the default close animation.<br>**Atomic service API**: This API can be used in atomic services since API version 11.|
43| showInSubWindow<sup>10+</sup> | boolean                                  | No   | Whether to show the dialog box in a sub-window when the dialog box needs to be displayed outside the main window.<br>Default value: **false**<br>**NOTE**<br>A dialog box whose **showInSubWindow** attribute is **true** cannot trigger the display of another dialog box whose **showInSubWindow** attribute is also **true**.<br>**Atomic service API**: This API can be used in atomic services since API version 11.|
44| backgroundColor<sup>10+</sup> | [ResourceColor](ts-types.md#resourcecolor)      | No  | Background color of the dialog box.<br>**NOTE**<br>If the content builder also has the background color set, the background color set here will be overridden by the background color of the content builder.<br>**Atomic service API**: This API can be used in atomic services since API version 11.|
45| cornerRadius<sup>10+</sup>    | [BorderRadiuses](ts-types.md#borderradiuses9) \| [Dimension](ts-types.md#dimension10) | No  | Radius of the rounded corners of the background.<br>You can set separate radiuses for the four rounded corners.<br>Default value: **{ topLeft: '32vp', topRight: '32vp', bottomLeft: '32vp', bottomRight: '32vp' }**<br>**NOTE**<br>This attribute must be used together with the [borderRadius](ts-universal-attributes-border.md#borderradius) attribute.<br>**Atomic service API**: This API can be used in atomic services since API version 11.|
46| isModal<sup>11+</sup> | boolean | No| Whether the dialog box is a modal. A modal dialog box has a mask applied, while a non-modal dialog box does not.<br>Default value: **true**|
47| onWillDismiss<sup>12+</sup> | Callback<[DismissDialogAction](ts-methods-alert-dialog-box.md#dismissdialogaction12)> | No| Callback for interactive closure of the dialog box.<br>**NOTE**<br>1. If this callback is registered, the dialog box will not be closed immediately after the user touches the mask or the Back button, presses the Esc key, or swipes left or right on the screen. The **reason** parameter in the callback is used to determine whether the dialog box can be closed. The reason returned by the component does not support the value **CLOSE_BUTTON**.<br>2. In the **onWillDismiss** callback, another **onWillDismiss** callback is not allowed.|
48| borderWidth<sup>12+</sup> | [Dimension](ts-types.md#dimension10) \| [EdgeWidths](ts-types.md#edgewidths9)  | No| Border width of the dialog box.<br>You can set the width for all four sides or set separate widths for individual sides.<br>Default value: **0**<br> When set to a percentage, the value defines the border width as a percentage of the parent dialog box's width.<br>If the left and right borders are greater than its width, or the top and bottom borders are greater than its height, the dialog box may not display as expected.|
49| borderColor<sup>12+</sup> | [ResourceColor](ts-types.md#resourcecolor) \| [EdgeColors](ts-types.md#edgecolors9)  | No| Border color of the dialog box.<br>Default value: **Color.Black**<br> **borderColor** must be used with **borderWidth** in pairs.|
50| borderStyle<sup>12+</sup> | [BorderStyle](ts-appendix-enums.md#borderstyle) \| [EdgeStyles](ts-types.md#edgestyles9)  | No| Border style of the dialog box.<br>Default value: **BorderStyle.Solid**<br> **borderStyle** must be used with **borderWidth** in pairs.|
51| width<sup>12+</sup> | [Dimension](ts-types.md#dimension10) | No  | Width of the dialog box.<br>**NOTE**<br>- Default maximum width of the dialog box: 400 vp<br>- When this parameter is set to a percentage, the reference width of the dialog box is the width of the window where the dialog box is located. You can decrease or increase the width as needed.|
52| height<sup>12+</sup> | [Dimension](ts-types.md#dimension10)   | No| Height of the dialog box.<br>**NOTE**<br>- Default maximum height of the dialog box: 0.9 x (Window height – Safe area)<br>- When this parameter is set to a percentage, the reference height of the dialog box is the height of the window where the dialog box is located minus the safe area. You can decrease or increase the height as needed.|
53| shadow<sup>12+</sup> | [ShadowOptions](ts-universal-attributes-image-effect.md#shadowoptions) \| [ShadowStyle](ts-universal-attributes-image-effect.md#shadowstyle10)   | No| Shadow of the dialog box.<br> Default value on 2-in-1 devices: **ShadowStyle.OUTER_FLOATING_MD** when the dialog box is focused and **ShadowStyle.OUTER_FLOATING_SM** otherwise|
54| backgroundBlurStyle<sup>12+</sup> | [BlurStyle](ts-appendix-enums.md#blurstyle9)                 | No  | Background blur style of the dialog box.<br>Default value: **BlurStyle.COMPONENT_ULTRA_THICK**|
55
56> **NOTE**
57>
58> - Pressing the Back or ESC key closes the dialog box.
59> - Use the custom dialog box to contain simple information only. Do not use it as a page. If the dialog box's height is too large, it may be partly blocked by the soft keyboard (if any), which is automatically raised when displayed. When the dialog box avoids the soft keyboard, there is a 16 vp safe spacing between the two.
60
61## CustomDialogController
62
63**Atomic service API**: This API can be used in atomic services since API version 11.
64
65### Objects to Import
66
67```ts
68dialogController : CustomDialogController | null = new CustomDialogController(CustomDialogControllerOptions)
69```
70> **NOTE**
71>
72> **CustomDialogController** is effective only when it is a member variable of the **@CustomDialog** and **@Component** decorated struct and is defined in the **@Component** decorated struct. For details, see the following example.
73
74### open
75open(): void
76
77
78Opens the content of the custom dialog box. This API can be called multiple times. If the dialog box displayed in a subwindow, no new subwindow is allowed.
79
80
81### close
82close(): void
83
84
85Closes the custom dialog box. If the dialog box is closed, this API does not take effect.
86
87## Example
88
89### Example 1
90
91```ts
92// xxx.ets
93@CustomDialog
94struct CustomDialogExampleTwo {
95  controllerTwo?: CustomDialogController
96  build() {
97    Column() {
98      Text('I'm the second dialog box.')
99        .fontSize(30)
100        .height(100)
101      Button ('Close Second Dialog Box')
102        .onClick(() => {
103          if (this.controllerTwo != undefined) {
104            this.controllerTwo.close()
105          }
106        })
107        .margin(20)
108    }
109  }
110}
111@CustomDialog
112struct CustomDialogExample {
113  @Link textValue: string
114  @Link inputValue: string
115  dialogControllerTwo: CustomDialogController | null = new CustomDialogController({
116    builder: CustomDialogExampleTwo(),
117    alignment: DialogAlignment.Bottom,
118    onWillDismiss:(dismissDialogAction: DismissDialogAction)=> {
119      console.info("reason=" + JSON.stringify(dismissDialogAction.reason))
120      console.log("dialog onWillDismiss")
121      if (dismissDialogAction.reason == DismissReason.PRESS_BACK) {
122        dismissDialogAction.dismiss()
123      }
124      if (dismissDialogAction.reason == DismissReason.TOUCH_OUTSIDE) {
125        dismissDialogAction.dismiss()
126      }
127    },
128    offset: { dx: 0, dy: -25 } })
129  controller?: CustomDialogController
130  // You can pass in multiple other controllers in the CustomDialog to open one or more other CustomDialogs in the CustomDialog. In this case, you must place the controller pointing to the self behind all controllers.
131  cancel: () => void = () => {
132  }
133  confirm: () => void = () => {
134  }
135
136  build() {
137    Column() {
138      Text('Change text').fontSize(20).margin({ top: 10, bottom: 10 })
139      TextInput({ placeholder: '', text: this.textValue }).height(60).width('90%')
140        .onChange((value: string) => {
141          this.textValue = value
142        })
143      Text('Are you sure you want to change the text?').fontSize(16).margin({ bottom: 10 })
144      Flex({ justifyContent: FlexAlign.SpaceAround }) {
145        Button('No')
146          .onClick(() => {
147            if (this.controller != undefined) {
148              this.controller.close()
149              this.cancel()
150            }
151          }).backgroundColor(0xffffff).fontColor(Color.Black)
152        Button('Yes')
153          .onClick(() => {
154            if (this.controller != undefined) {
155              this.inputValue = this.textValue
156              this.controller.close()
157              this.confirm()
158            }
159          }).backgroundColor(0xffffff).fontColor(Color.Red)
160      }.margin({ bottom: 10 })
161
162      Button ('Open Second Dialog Box')
163        .onClick(() => {
164          if (this.dialogControllerTwo != null) {
165            this.dialogControllerTwo.open()
166          }
167        })
168        .margin(20)
169    }.borderRadius(10)
170    // When using the border or cornerRadius attribute, use it together with the borderRadius attribute.
171  }
172}
173@Entry
174@Component
175struct CustomDialogUser {
176  @State textValue: string = ''
177  @State inputValue: string = 'Click Me'
178  dialogController: CustomDialogController | null = new CustomDialogController({
179    builder: CustomDialogExample({
180      cancel: ()=> { this.onCancel() },
181      confirm: ()=> { this.onAccept() },
182      textValue: $textValue,
183      inputValue: $inputValue
184    }),
185    cancel: this.exitApp,
186    autoCancel: true,
187    onWillDismiss:(dismissDialogAction: DismissDialogAction)=> {
188      console.info("reason=" + JSON.stringify(dismissDialogAction.reason))
189      console.log("dialog onWillDismiss")
190      if (dismissDialogAction.reason == DismissReason.PRESS_BACK) {
191        dismissDialogAction.dismiss()
192      }
193      if (dismissDialogAction.reason == DismissReason.TOUCH_OUTSIDE) {
194        dismissDialogAction.dismiss()
195      }
196    },
197    alignment: DialogAlignment.Bottom,
198    offset: { dx: 0, dy: -20 },
199    gridCount: 4,
200    customStyle: false,
201    cornerRadius: 10,
202  })
203
204  // Set dialogController to null when the custom component is about to be destructed.
205  aboutToDisappear() {
206    this.dialogController = null // Set dialogController to null.
207  }
208
209  onCancel() {
210    console.info('Callback when the first button is clicked')
211  }
212
213  onAccept() {
214    console.info('Callback when the second button is clicked')
215  }
216
217  exitApp() {
218    console.info('Click the callback in the blank area')
219  }
220  build() {
221    Column() {
222      Button(this.inputValue)
223        .onClick(() => {
224          if (this.dialogController != null) {
225            this.dialogController.open()
226          }
227        }).backgroundColor(0x317aff)
228    }.width('100%').margin({ top: 5 })
229  }
230}
231```
232
233![en-us_image_custom](figures/en-us_image_custom.gif)
234
235### Example 2
236
237```ts
238// xxx.ets
239@CustomDialog
240struct CustomDialogExample {
241  controller?: CustomDialogController
242  cancel: () => void = () => {
243  }
244  confirm: () => void = () => {
245  }
246  build() {
247    Column() {
248      Text('Dialog box outside the main window')
249        .fontSize(30)
250        .height(100)
251      Button ('Close')
252        .onClick(() => {
253          if (this.controller != undefined) {
254            this.controller.close()
255          }
256        })
257        .margin(20)
258    }
259  }
260}
261@Entry
262@Component
263struct CustomDialogUser {
264  dialogController: CustomDialogController | null = new CustomDialogController({
265    builder: CustomDialogExample({
266      cancel: ()=> { this.onCancel() },
267      confirm: ()=> { this.onAccept() }
268    }),
269    cancel: this.existApp,
270    autoCancel: true,
271    onWillDismiss:(dismissDialogAction: DismissDialogAction)=> {
272      console.info("reason=" + JSON.stringify(dismissDialogAction.reason))
273      console.log("dialog onWillDismiss")
274      if (dismissDialogAction.reason == DismissReason.PRESS_BACK) {
275        dismissDialogAction.dismiss()
276      }
277      if (dismissDialogAction.reason == DismissReason.TOUCH_OUTSIDE) {
278        dismissDialogAction.dismiss()
279      }
280    },
281    alignment: DialogAlignment.Center,
282    offset: { dx: 0, dy: -20 },
283    gridCount: 4,
284    showInSubWindow: true,
285    isModal: true,
286    customStyle: false,
287    cornerRadius: 10,
288  })
289  // Set dialogController to null when the custom component is about to be destroyed.
290  aboutToDisappear() {
291    this.dialogController = null // Set dialogController to null.
292  }
293
294  onCancel() {
295    console.info('Callback when the first button is clicked')
296  }
297
298  onAccept() {
299    console.info('Callback when the second button is clicked')
300  }
301
302  existApp() {
303    console.info('Click the callback in the blank area')
304  }
305
306  build() {
307    Column() {
308      Button('Click Me')
309        .onClick(() => {
310          if (this.dialogController != null) {
311            this.dialogController.open()
312          }
313        }).backgroundColor(0x317aff)
314    }.width('100%').margin({ top: 5 })
315  }
316}
317```
318
319![en-us_image_custom-showinsubwindow](figures/en-us_image_custom-showinsubwindow.jpg)
320