1# FoldSplitContainer
2
3
4**FoldSplitContainer** is a layout container designed to manage regions for two-panel and three-panel arrangements on a foldable device across various states, including the expanded state, the hover state, and the folded state.
5
6
7> **NOTE**
8>
9> This component is supported since API version 12. Updates will be marked with a superscript to indicate their earliest API version.
10
11
12## Child Components
13
14Not supported
15
16## FoldSplitContainer
17
18FoldSplitContainer({
19  primary: Callback<void>,
20  secondary: Callback<void>,
21  extra?: Callback<void>,
22  expandedLayoutOptions?: ExpandedRegionLayoutOptions,
23  hoverModeLayoutOptions?: HoverModeRegionLayoutOptions,
24  foldedLayoutOptions?: FoldedRegionLayoutOptions,
25  animationOptions?: AnimateParam,
26  onHoverStatusChange?: onHoverStatusChangeHandler
27})
28
29**Decorator**: \@Component
30
31**Atomic service API**: This API can be used in atomic services since API version 12.
32
33**System capability**: SystemCapability.ArkUI.ArkUI.Full
34
35**Parameters**
36
37| Name| Type| Mandatory| Decorator| Description|
38| -------- | -------- | -------- | -------- | -------- |
39| primary | ()=>void | No| @BuilderParam | Callback function for the primary region.|
40| secondary | ()=>void | No| @BuilderParam | Callback function for the secondary region.|
41| extra | ()=>void | No| @BuilderParam | Callback function for the extra region. If this parameter is not provided, there is no corresponding region.|
42| expandedLayoutOptions | [ExpandedRegionLayoutOptions](#expandedregionlayoutoptions) | No| @Prop | Layout information for the expanded state.|
43| hoverModeLayoutOptions | [HoverModeRegionLayoutOptions](#hovermoderegionlayoutoptions) | No| @Prop | Layout information for the hover state.|
44| foldedLayoutOptions | [FoldedRegionLayoutOptions](#foldedregionlayoutoptions) | No| @Prop | Layout information for the folded state.|
45| animationOptions | [AnimateParam](ts-explicit-animation.md#animateparam) \| null | No| @Prop | Animation settings. The value **null** indicates that the animation is disabled.|
46| onHoverStatusChange | [onHoverStatusChangeHandler](#onhoverstatuschangehandler) | No| - | Callback function triggered when the foldable device enters or exits the hover state.|
47
48## ExpandedRegionLayoutOptions
49
50**Decorator**: \@Prop
51
52**Atomic service API**: This API can be used in atomic services since API version 12.
53
54**System capability**: SystemCapability.ArkUI.ArkUI.Full
55
56Defines the layout information for the expanded state.
57
58| Name| Type| Mandatory| Description|
59| -------- | -------- | -------- | -------- |
60| isExtraRegionPerpendicular | boolean | No| Whether the extra region extends perpendicularly through the entire component from top to bottom. This setting takes effect only when **extra** is effective.<br>Default value: **true** |
61| verticalSplitRatio | number | No| Height ratio between the primary and secondary regions.<br/>Default value: **PresetSplitRatio.LAYOUT_1V1** |
62| horizontalSplitRatio | number | No| Width ratio between the primary and extra regions. This setting takes effect only when **extra** is effective.<br/>Default value: **PresetSplitRatio.LAYOUT_3V2** |
63| extraRegionPosition | [ExtraRegionPosition](#extraregionposition) | No| Position information of the extra region. This setting takes effect only when **isExtraRegionPerpendicular** is **false**.<br/>Default value: **ExtraRegionPosition.top** |
64
65## HoverModeRegionLayoutOptions
66
67**Decorator**: \@Prop
68
69**Atomic service API**: This API can be used in atomic services since API version 12.
70
71**System capability**: SystemCapability.ArkUI.ArkUI.Full
72
73Defines the layout information for the hover state.
74
75| Name| Type| Mandatory| Description|
76| -------- | -------- | -------- | -------- |
77| showExtraRegion | boolean | No| Whether to display the extra region in the half-folded state. Default value: **false**|
78| horizontalSplitRatio | number | No| Width ratio between the primary and extra regions. This setting takes effect only when **extra** is effective.<br/>Default value: **PresetSplitRatio.LAYOUT_3V2** |
79| extraRegionPosition | [ExtraRegionPosition](#extraregionposition) | No| Position information of the extra region. This setting takes effect only when **showExtraRegion** is set.<br/>Default value: **ExtraRegionPosition.top** |
80
81> **NOTE**
82>
83> 1. When the device is in the hover state, there is an avoid area, and layout calculations need to account for the impact of the avoid area on the layout.
84> 2. In the hover state, the upper half screen is used for display, and the lower half is used for interaction.
85
86## FoldedRegionLayoutOptions
87
88**Decorator**: \@Prop
89
90**Atomic service API**: This API can be used in atomic services since API version 12.
91
92**System capability**: SystemCapability.ArkUI.ArkUI.Full
93
94Defines the layout information for the folded state.
95
96| Name| Type| Mandatory| Description|
97| -------- | -------- | -------- | -------- |
98| verticalSplitRatio | number | Yes| Height ratio between the primary and secondary regions. Default value: **PresetSplitRatio.LAYOUT_1V1**|
99
100## onHoverStatusChangeHandler
101
102**Atomic service API**: This API can be used in atomic services since API version 12.
103
104Implements a handler for the **onHoverStatusChange** event.
105
106| Name| Type| Mandatory| Description|
107| -------- | -------- | -------- | -------- |
108| callback | (status: [HoverModeStatus](#hovermodestatus)) => void | Yes| Callback function triggered when the foldable device enters or exits the hover state.|
109
110## HoverModeStatus
111
112**Atomic service API**: This API can be used in atomic services since API version 12.
113
114**System capability**: SystemCapability.ArkUI.ArkUI.Full
115
116Provides the layout information of the folded state.
117
118| Name| Type| Mandatory| Description|
119| -------- | -------- | -------- | -------- |
120| foldStatus | [FoldStatus<sup>10+</sup>](../js-apis-display.md#foldstatus10) | Yes| Fold status of the device.|
121| isHoverMode | boolean | Yes| Whether the application is in the hover state.|
122| appRotation | number | Yes| Rotation angle of the application.|
123| windowStatusType | [WindowStatusType<sup>11+</sup>](../js-apis-window.md#windowstatustype11) | Yes| Window mode.|
124
125## ExtraRegionPosition
126
127**Atomic service API**: This API can be used in atomic services since API version 12.
128
129Provides the position information of the extra region.
130
131| Name| Value| Description|
132| -------- | -------- | -------- |
133| top | 1 | The extra region is in the upper half of the component.|
134| bottom | 2 | The extra region is in the lower half of the component.|
135
136## PresetSplitRatio
137
138**Atomic service API**: This API can be used in atomic services since API version 12.
139
140Enumerates the split ratios.
141
142| Name| Value| Description|
143| -------- | -------- | -------- |
144| LAYOUT_1V1 | 1/1 | 1:1.|
145| LAYOUT_3V2 | 3/2 | 3:2.|
146| LAYOUT_2V3 | 2/3 | 2:3.|
147
148## Example
149
150### Example 1
151
152```ts
153import { FoldSplitContainer } from '@kit.ArkUI';
154
155@Entry
156@Component
157struct TwoColumns {
158  @Builder
159  privateRegion() {
160    Text("Primary")
161      .backgroundColor('rgba(255, 0, 0, 0.1)')
162      .fontSize(28)
163      .textAlign(TextAlign.Center)
164      .height('100%')
165      .width('100%')
166  }
167
168  @Builder
169  secondaryRegion() {
170    Text("Secondary")
171      .backgroundColor('rgba(0, 255, 0, 0.1)')
172      .fontSize(28)
173      .textAlign(TextAlign.Center)
174      .height('100%')
175      .width('100%')
176  }
177
178  build() {
179    RelativeContainer() {
180      FoldSplitContainer({
181        primary: () => {
182          this.privateRegion()
183        },
184        secondary: () => {
185          this.secondaryRegion()
186        }
187      })
188    }
189    .height('100%')
190    .width('100%')
191  }
192}
193```
194
195| Folded| Expanded| Hover|
196| ----- | ------ | ------ |
197| ![](figures/foldsplitcontainer-1.png) | ![](figures/foldsplitcontainer-2.png) | ![](figures/foldsplitcontainer-3.png) |
198
199
200### Example 2
201
202```ts
203import { FoldSplitContainer } from '@kit.ArkUI';
204
205@Entry
206@Component
207struct ThreeColumns {
208  @Builder
209  privateRegion() {
210    Text("Primary")
211      .backgroundColor('rgba(255, 0, 0, 0.1)')
212      .fontSize(28)
213      .textAlign(TextAlign.Center)
214      .height('100%')
215      .width('100%')
216  }
217
218  @Builder
219  secondaryRegion() {
220    Text("Secondary")
221      .backgroundColor('rgba(0, 255, 0, 0.1)')
222      .fontSize(28)
223      .textAlign(TextAlign.Center)
224      .height('100%')
225      .width('100%')
226  }
227
228  @Builder
229  extraRegion() {
230    Text("Extra")
231      .backgroundColor('rgba(0, 0, 255, 0.1)')
232      .fontSize(28)
233      .textAlign(TextAlign.Center)
234      .height('100%')
235      .width('100%')
236  }
237
238  build() {
239    RelativeContainer() {
240      FoldSplitContainer({
241        primary: () => {
242          this.privateRegion()
243        },
244        secondary: () => {
245          this.secondaryRegion()
246        },
247        extra: () => {
248          this.extraRegion()
249        }
250      })
251    }
252    .height('100%')
253    .width('100%')
254  }
255}
256```
257
258| Folded| Expanded| Hover|
259| ----- | ------ | ------ |
260| ![](figures/foldsplitcontainer-4.png) | ![](figures/foldsplitcontainer-5.png) | ![](figures/foldsplitcontainer-6.png) |
261
262### Example 3
263
264```ts
265import {
266  FoldSplitContainer,
267  PresetSplitRatio,
268  ExtraRegionPosition,
269  ExpandedRegionLayoutOptions,
270  HoverModeRegionLayoutOptions,
271  FoldedRegionLayoutOptions
272} from '@kit.ArkUI';
273
274@Component
275struct Region {
276  @Prop title: string;
277  @BuilderParam content: () => void;
278  @Prop compBackgroundColor: string;
279
280  build() {
281    Column({ space: 8 }) {
282      Text(this.title)
283        .fontSize("24fp")
284        .fontWeight(600)
285
286      Scroll() {
287        this.content()
288      }
289      .layoutWeight(1)
290      .width("100%")
291    }
292    .backgroundColor(this.compBackgroundColor)
293    .width("100%")
294    .height("100%")
295    .padding(12)
296  }
297}
298
299const noop = () => {
300};
301
302@Component
303struct SwitchOption {
304  @Prop label: string = ""
305  @Prop value: boolean = false
306  public onChange: (checked: boolean) => void = noop;
307
308  build() {
309    Row() {
310      Text(this.label)
311      Blank()
312      Toggle({ type: ToggleType.Switch, isOn: this.value })
313        .onChange((isOn) => {
314          this.onChange(isOn);
315        })
316    }
317    .backgroundColor(Color.White)
318    .borderRadius(8)
319    .padding(8)
320    .width("100%")
321  }
322}
323
324interface RadioOptions {
325  label: string;
326  value: Object | undefined | null;
327  onChecked: () => void;
328}
329
330@Component
331struct RadioOption {
332  @Prop label: string;
333  @Prop value: Object | undefined | null;
334  @Prop options: Array<RadioOptions>;
335
336  build() {
337    Row() {
338      Text(this.label)
339      Blank()
340      Column({ space: 4 }) {
341        ForEach(this.options, (option: RadioOptions) => {
342          Row() {
343            Radio({
344              group: this.label,
345              value: JSON.stringify(option.value),
346            })
347              .checked(this.value === option.value)
348              .onChange((checked) => {
349                if (checked) {
350                  option.onChecked();
351                }
352              })
353            Text(option.label)
354          }
355        })
356      }
357      .alignItems(HorizontalAlign.Start)
358    }
359    .alignItems(VerticalAlign.Top)
360    .backgroundColor(Color.White)
361    .borderRadius(8)
362    .padding(8)
363    .width("100%")
364  }
365}
366
367@Entry
368@Component
369struct Index {
370  @State expandedRegionLayoutOptions: ExpandedRegionLayoutOptions = {
371    horizontalSplitRatio: PresetSplitRatio.LAYOUT_3V2,
372    verticalSplitRatio: PresetSplitRatio.LAYOUT_1V1,
373    isExtraRegionPerpendicular: true,
374    extraRegionPosition: ExtraRegionPosition.TOP
375  };
376  @State foldingRegionLayoutOptions: HoverModeRegionLayoutOptions = {
377    horizontalSplitRatio: PresetSplitRatio.LAYOUT_3V2,
378    showExtraRegion: false,
379    extraRegionPosition: ExtraRegionPosition.TOP
380  };
381  @State foldedRegionLayoutOptions: FoldedRegionLayoutOptions = {
382    verticalSplitRatio: PresetSplitRatio.LAYOUT_1V1
383  };
384
385  @Builder
386  MajorRegion() {
387    Region({
388      title: "Folded state settings",
389      compBackgroundColor: "rgba(255, 0, 0, 0.1)",
390    }) {
391      Column({ space: 4 }) {
392        RadioOption({
393          label: "Height ratio",
394          value: this.foldedRegionLayoutOptions.verticalSplitRatio,
395          options: [
396            {
397              label: "1:1",
398              value: PresetSplitRatio.LAYOUT_1V1,
399              onChecked: () => {
400                this.foldedRegionLayoutOptions.verticalSplitRatio = PresetSplitRatio.LAYOUT_1V1
401              }
402            },
403            {
404              label: "2:3",
405              value: PresetSplitRatio.LAYOUT_2V3,
406              onChecked: () => {
407                this.foldedRegionLayoutOptions.verticalSplitRatio = PresetSplitRatio.LAYOUT_2V3
408              }
409            },
410            {
411              label: "3:2",
412              value: PresetSplitRatio.LAYOUT_3V2,
413              onChecked: () => {
414                this.foldedRegionLayoutOptions.verticalSplitRatio = PresetSplitRatio.LAYOUT_3V2
415              }
416            },
417            {
418              label: "Not set",
419              value: undefined,
420              onChecked: () => {
421                this.foldedRegionLayoutOptions.verticalSplitRatio = undefined
422              }
423            }
424          ]
425        })
426      }
427      .constraintSize({ minHeight: "100%" })
428    }
429  }
430
431  @Builder
432  MinorRegion() {
433    Region({
434      title: "Hover state settings",
435      compBackgroundColor: "rgba(0, 255, 0, 0.1)"
436    }) {
437      Column({ space: 4 }) {
438        RadioOption({
439          label: "Width ratio",
440          value: this.foldingRegionLayoutOptions.horizontalSplitRatio,
441          options: [
442            {
443              label: "1:1",
444              value: PresetSplitRatio.LAYOUT_1V1,
445              onChecked: () => {
446                this.foldingRegionLayoutOptions.horizontalSplitRatio = PresetSplitRatio.LAYOUT_1V1
447              }
448            },
449            {
450              label: "2:3",
451              value: PresetSplitRatio.LAYOUT_2V3,
452              onChecked: () => {
453                this.foldingRegionLayoutOptions.horizontalSplitRatio = PresetSplitRatio.LAYOUT_2V3
454              }
455            },
456            {
457              label: "3:2",
458              value: PresetSplitRatio.LAYOUT_3V2,
459              onChecked: () => {
460                this.foldingRegionLayoutOptions.horizontalSplitRatio = PresetSplitRatio.LAYOUT_3V2
461              }
462            },
463            {
464              label: "Not set",
465              value: undefined,
466              onChecked: () => {
467                this.foldingRegionLayoutOptions.horizontalSplitRatio = undefined
468              }
469            },
470          ]
471        })
472
473        SwitchOption({
474          label: "Show extra region",
475          value: this.foldingRegionLayoutOptions.showExtraRegion,
476          onChange: (checked) => {
477            this.foldingRegionLayoutOptions.showExtraRegion = checked;
478          }
479        })
480
481        if (this.foldingRegionLayoutOptions.showExtraRegion) {
482          RadioOption({
483            label: "Extra region location,"
484            value: this.foldingRegionLayoutOptions.extraRegionPosition,
485            options: [
486              {
487                label: "Top,"
488                value: ExtraRegionPosition.TOP,
489                onChecked: () => {
490                  this.foldingRegionLayoutOptions.extraRegionPosition = ExtraRegionPosition.TOP
491                }
492              },
493              {
494                label: "Bottom,"
495                value: ExtraRegionPosition.BOTTOM,
496                onChecked: () => {
497                  this.foldingRegionLayoutOptions.extraRegionPosition = ExtraRegionPosition.BOTTOM
498                }
499              },
500              {
501                label: "Not set",
502                value: undefined,
503                onChecked: () => {
504                  this.foldingRegionLayoutOptions.extraRegionPosition = undefined
505                }
506              },
507            ]
508          })
509        }
510      }
511      .constraintSize({ minHeight: "100%" })
512    }
513  }
514
515  @Builder
516  ExtraRegion() {
517    Region({
518      title: "Expanded state settings,"
519      compBackgroundColor: "rgba(0, 0, 255, 0.1)"
520    }) {
521      Column({ space: 4 }) {
522        RadioOption({
523          label: "Width ratio,"
524          value: this.expandedRegionLayoutOptions.horizontalSplitRatio,
525          options: [
526            {
527              label: "1:1",
528              value: PresetSplitRatio.LAYOUT_1V1,
529              onChecked: () => {
530                this.expandedRegionLayoutOptions.horizontalSplitRatio = PresetSplitRatio.LAYOUT_1V1
531              }
532            },
533            {
534              label: "2:3",
535              value: PresetSplitRatio.LAYOUT_2V3,
536              onChecked: () => {
537                this.expandedRegionLayoutOptions.horizontalSplitRatio = PresetSplitRatio.LAYOUT_2V3
538              }
539            },
540            {
541              label: "3:2",
542              value: PresetSplitRatio.LAYOUT_3V2,
543              onChecked: () => {
544                this.expandedRegionLayoutOptions.horizontalSplitRatio = PresetSplitRatio.LAYOUT_3V2
545              }
546            },
547            {
548              label: "Not set",
549              value: undefined,
550              onChecked: () => {
551                this.expandedRegionLayoutOptions.horizontalSplitRatio = undefined
552              }
553            },
554          ]
555        })
556
557        RadioOption({
558          label: "Height ratio",
559          value: this.expandedRegionLayoutOptions.verticalSplitRatio,
560          options: [
561            {
562              label: "1:1",
563              value: PresetSplitRatio.LAYOUT_1V1,
564              onChecked: () => {
565                this.expandedRegionLayoutOptions.verticalSplitRatio = PresetSplitRatio.LAYOUT_1V1
566              }
567            },
568            {
569              label: "2:3",
570              value: PresetSplitRatio.LAYOUT_2V3,
571              onChecked: () => {
572                this.expandedRegionLayoutOptions.verticalSplitRatio = PresetSplitRatio.LAYOUT_2V3
573              }
574            },
575            {
576              label: "3:2",
577              value: PresetSplitRatio.LAYOUT_3V2,
578              onChecked: () => {
579                this.expandedRegionLayoutOptions.verticalSplitRatio = PresetSplitRatio.LAYOUT_3V2
580              }
581            },
582            {
583              label: "Not set",
584              value: undefined,
585              onChecked: () => {
586                this.expandedRegionLayoutOptions.verticalSplitRatio = undefined
587              }
588            }
589          ]
590        })
591
592        SwitchOption({
593          label: "Show extra region perpendicularly,"
594          value: this.expandedRegionLayoutOptions.isExtraRegionPerpendicular,
595          onChange: (checked) => {
596            this.expandedRegionLayoutOptions.isExtraRegionPerpendicular = checked;
597          }
598        })
599
600        if (!this.expandedRegionLayoutOptions.isExtraRegionPerpendicular) {
601          RadioOption({
602            label: "Extra region location,"
603            value: this.expandedRegionLayoutOptions.extraRegionPosition,
604            options: [
605              {
606                label: "Top,"
607                value: ExtraRegionPosition.TOP,
608                onChecked: () => {
609                  this.expandedRegionLayoutOptions.extraRegionPosition = ExtraRegionPosition.TOP
610                }
611              },
612              {
613                label: "Bottom,"
614                value: ExtraRegionPosition.BOTTOM,
615                onChecked: () => {
616                  this.expandedRegionLayoutOptions.extraRegionPosition = ExtraRegionPosition.BOTTOM
617                }
618              },
619              {
620                label: "Not set",
621                value: undefined,
622                onChecked: () => {
623                  this.expandedRegionLayoutOptions.extraRegionPosition = undefined
624                }
625              },
626            ]
627          })
628        }
629      }
630      .constraintSize({ minHeight: "100%" })
631    }
632  }
633
634  build() {
635    Column() {
636      FoldSplitContainer({
637        primary: () => {
638          this.MajorRegion()
639        },
640        secondary: () => {
641          this.MinorRegion()
642        },
643        extra: () => {
644          this.ExtraRegion()
645        },
646        expandedLayoutOptions: this.expandedRegionLayoutOptions,
647        hoverModeLayoutOptions: this.foldingRegionLayoutOptions,
648        foldedLayoutOptions: this.foldedRegionLayoutOptions,
649      })
650    }
651    .width("100%")
652    .height("100%")
653  }
654}
655```
656
657| Folded| Expanded| Hover|
658| ----- | ------ | ------ |
659| ![](figures/foldsplitcontainer-7.png) | ![](figures/foldsplitcontainer-8.png) | ![](figures/foldsplitcontainer-11.png) |
660|                                       | ![](figures/foldsplitcontainer-9.png) | ![](figures/foldsplitcontainer-12.png) |
661|                                       | ![](figures/foldsplitcontainer-10.png) | ![](figures/foldsplitcontainer-13.png) |
662