1# Tabs
2
3The **Tabs** component is a container component that allows users to switch between content views through tabs. Each tab page corresponds to a content view.
4
5>  **NOTE**
6>
7>  This component is supported since API version 7. Updates will be marked with a superscript to indicate their earliest API version.
8>
9>  Since API version 11, this component supports the safe area attribute by default, with the default attribute value being **expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM]))**. You can override this attribute to change the default behavior. In earlier versions, you need to use the [expandSafeArea](ts-universal-attributes-expand-safe-area.md) attribute to implement the safe area feature.
10
11
12## Child Components
13
14Custom components cannot be used as child components. Only the [TabContent](ts-container-tabcontent.md) child component is allowed, with support for [if/else](../../../quick-start/arkts-rendering-control-ifelse.md) and [ForEach](../../../quick-start/arkts-rendering-control-foreach.md) rendering control. In addition, the **if/else** and **ForEach** statements support **TabContent** components only, but not custom components.
15
16>  **NOTE**
17>
18>  If the child component has the **visibility** attribute set to **None** or **Hidden**, it is hidden but takes up space in the layout.
19
20
21## APIs
22
23Tabs(value?: {barPosition?: BarPosition, index?: number, controller?: TabsController})
24
25**Atomic service API**: This API can be used in atomic services since API version 11.
26
27**System capability**: SystemCapability.ArkUI.ArkUI.Full
28
29**Parameters**
30
31| Name        | Type                             | Mandatory  | Description                                    |
32| ----------- | --------------------------------- | ---- | ---------------------------------------- |
33| barPosition | [BarPosition](#barposition)| No   | Position of the **Tabs** component.<br>Default value: **BarPosition.Start**  |
34| index       | number                            | No   | Index of the currently displayed tab.<br>Default value: **0**<br>**NOTE**<br><br>A value less than 0 evaluates to the default value.<br>The value ranges from 0 to the number of **TabContent** subnodes minus 1.<br>When the tab is switched by changing the index, the tab switching animation does not take effect. When **changeIndex** of **TabController** is used for tab switching, the tab switching animation is enabled by default. You can disable the animation by setting **animationDuration** to **0**.<br>Since API version 10, this parameter supports two-way binding through [$$](../../../quick-start/arkts-two-way-sync.md).|
35| controller  | [TabsController](#tabscontroller) | No   | Tab controller.                              |
36
37## BarPosition
38
39Enumerates the positions of the **Tabs** component.
40
41**Atomic service API**: This API can be used in atomic services since API version 11.
42
43**System capability**: SystemCapability.ArkUI.ArkUI.Full
44
45| Name   | Description                                      |
46| ----- | ---------------------------------------- |
47| Start | If the **vertical** attribute is set to **true**, the tab is on the left of the container. If the **vertical** attribute is set to **false**, the tab is on the top of the container.|
48| End   | If the **vertical** attribute is set to **true**, the tab is on the right of the container. If the **vertical** attribute is set to **false**, the tab is at the bottom of the container.|
49
50
51## Attributes
52
53In addition to the [universal attributes](ts-universal-attributes-size.md), the following attributes are supported.
54
55### vertical
56
57vertical(value: boolean)
58
59Sets whether to use vertical tabs.
60
61**Atomic service API**: This API can be used in atomic services since API version 11.
62
63**System capability**: SystemCapability.ArkUI.ArkUI.Full
64
65**Parameters**
66
67| Name| Type   | Mandatory| Description                                                        |
68| ------ | ------- | ---- | ------------------------------------------------------------ |
69| value  | boolean | Yes  | Whether to use vertical tabs.<br>The value **true** means to use vertical tabs, and **false** means to use horizontal tabs.<br>Default value: **false**<br>If set to have a height of **auto**, horizontal tabs auto-adapt the height to child components, which is calculated as follows: Tab bar height + Divider width + Tab content height + Top and bottom paddings + Top and bottom border widths.<br>If set to have a width of **auto**, vertical tabs auto-adapt the width to child components, which is calculated as follows: Tab bar width + Divider width + Tab content width + Left and right paddings + Left and right border widths.<br>To avoid animation jitter when switching between tabs, maintain a consistent size for child components on each tab.|
70
71### scrollable
72
73scrollable(value: boolean)
74
75Sets whether the tabs are scrollable.
76
77**Atomic service API**: This API can be used in atomic services since API version 11.
78
79**System capability**: SystemCapability.ArkUI.ArkUI.Full
80
81**Parameters**
82
83| Name| Type   | Mandatory| Description                                                        |
84| ------ | ------- | ---- | ------------------------------------------------------------ |
85| value  | boolean | Yes  | Whether the tabs are scrollable.<br>**true** (default): The tabs are scrollable.<br> **false**: The tabs are not scrollable.|
86
87### barMode
88
89barMode(value: BarMode, options?: ScrollableBarModeOptions)
90
91Sets the tab bar layout mode.
92
93**Atomic service API**: This API can be used in atomic services since API version 11.
94
95**System capability**: SystemCapability.ArkUI.ArkUI.Full
96
97**Parameters**
98
99| Name               | Type                                                        | Mandatory| Description                                                        |
100| --------------------- | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ |
101| value                 | [BarMode](#barmode)                                  | Yes  | Layout mode.<br>Default value: **BarMode.Fixed**                                                |
102| options<sup>10+</sup> | [ScrollableBarModeOptions](#scrollablebarmodeoptions10)| No  | Layout style.<br>**NOTE**<br>This value is effective only when the tab bar is in scrollable mode.|
103
104### barMode<sup>10+</sup>
105
106barMode(value: BarMode.Fixed)
107
108Sets the tab bar layout mode to **BarMode.Fixed**.
109
110**Atomic service API**: This API can be used in atomic services since API version 11.
111
112**System capability**: SystemCapability.ArkUI.ArkUI.Full
113
114**Parameters**
115
116| Name   | Type                            | Mandatory| Description                                   |
117| -------- | -------------------------------- | ---- | ------------------------------------ |
118| value    | [BarMode.Fixed](#barmode)| Yes  | The width of each tab is determined by equally dividing the number of tabs by the bar width (or bar height in the vertical layout).  |
119
120### barMode<sup>10+</sup>
121
122barMode(value: BarMode.Scrollable, options: ScrollableBarModeOptions)
123
124Sets the tab bar layout mode to **BarMode.Scrollable**.
125
126**Atomic service API**: This API can be used in atomic services since API version 11.
127
128**System capability**: SystemCapability.ArkUI.ArkUI.Full
129
130**Parameters**
131
132| Name   | Type                             | Mandatory| Description                                   |
133| -------- | --------------------------------- | ---- | ------------------------------------- |
134| value    | [BarMode.Scrollable](#barmode)| Yes  | The width of each tab is determined by the actual layout. The tabs are scrollable in the following case: In horizontal layout, the total width exceeds the tab bar width; in horizontal layout, the total height exceeds the tab bar height.       |
135| options | [ScrollableBarModeOptions](#scrollablebarmodeoptions10)| Yes  | Layout style.|
136
137### barWidth
138
139barWidth(value: Length)
140
141Sets the width of the tab bar. A value less than 0 or greater than the width of the **Tabs** component evaluates to the default value.
142
143**Atomic service API**: This API can be used in atomic services since API version 11.
144
145**System capability**: SystemCapability.ArkUI.ArkUI.Full
146
147**Parameters**
148
149| Name| Type                                     | Mandatory| Description                                                        |
150| ------ | ----------------------------------------- | ---- | ------------------------------------------------------------ |
151| value  | [Length](ts-types.md#length)<sup>8+</sup> | Yes  | Width of the tab bar.<br>Default value:<br>If neither [SubTabBarStyle](ts-container-tabcontent.md#subtabbarstyle9) nor [BottomTabBarStyle](ts-container-tabcontent.md#bottomtabbarstyle9) is set, and the **vertical** attribute is **false**, the default value is the width of the **Tabs** component.<br>If neither **SubTabBarStyle** nor **BottomTabBarStyle** is set, and the **vertical** attribute is **true**, the default value is 56 vp.<br>If **SubTabBarStyle** is set, and the **vertical** attribute is **false**, the default value is the width of the **Tabs** component.<br>If **SubTabBarStyle** is set, and the **vertical** attribute is **true**, the default value is 56 vp.<br>If **BottomTabBarStyle** is set, and the **vertical** attribute is **true**, the default value is 96 vp.<br>If **BottomTabBarStyle** is set, and the **vertical** attribute is **false**, the default value is the width of the **Tabs** component.|
152
153### barHeight
154
155barHeight(value: Length)
156
157Sets the height of the tab bar. A value less than 0 or greater than the height of the **Tabs** component evaluates to the default value.
158
159**Atomic service API**: This API can be used in atomic services since API version 11.
160
161**System capability**: SystemCapability.ArkUI.ArkUI.Full
162
163**Parameters**
164
165| Name| Type                                     | Mandatory| Description                                                        |
166| ------ | ----------------------------------------- | ---- | ------------------------------------------------------------ |
167| value  | [Length](ts-types.md#length)<sup>8+</sup> | Yes  | Height of the tab bar.<br>Default value:<br>If the tab bar has the **vertical** attribute set to **false** and does not have a style specified, the default value is 56 vp.<br>If the tab bar has the **vertical** attribute set to **true** and does not have a style specified, the default value is the height of the **Tabs** component.<br>If [SubTabBarStyle](ts-container-tabcontent.md#subtabbarstyle9) is set, and the **vertical** attribute is **false**, the default value is 56 vp.<br>If **SubTabBarStyle** is set, and the **vertical** attribute is **true**, the default value is the height of the **Tabs** component.<br>If [BottomTabBarStyle](ts-container-tabcontent.md#bottomtabbarstyle9) is set, and the **vertical** attribute is **true**, the default value is the height of the **Tabs** component.<br>If **BottomTabBarStyle** is set, and the **vertical** attribute is **false**, the default value is 56 vp in versions earlier than API version 12 and 52 vp since API version 12.|
168
169### animationDuration
170
171animationDuration(value: number)
172
173Sets the length of time required to complete the tab switching animation, which is initiated by clicking a specific tab or by calling the **changeIndex** API of **TabsController**. This parameter cannot be set in percentage.
174
175**Atomic service API**: This API can be used in atomic services since API version 11.
176
177**System capability**: SystemCapability.ArkUI.ArkUI.Full
178
179**Parameters**
180
181| Name| Type  | Mandatory| Description                                                        |
182| ------ | ------ | ---- | ------------------------------------------------------------ |
183| value  | number | Yes  | Length of time required to complete the tab switching animation, which is initiated by clicking a specific tab or by calling the **changeIndex** API of **TabsController**.<br>The default value varies.<br>API version 10 and earlier versions: If this parameter is set to **null** or is not set, the default value **0** is used, which means that no tab switching animation is displayed when a specific tab is clicked or the **changeIndex** API of **TabsController** is called. If this parameter is set to **undefined** or a value less than 0, the default value **300** is used.<br>API version 11 and later versions: If this parameter is set to an invalid value or is not set, the default value **0** is used when the tab bar is set to **BottomTabBarStyle**; the default value **300** is used when the tab bar is set to any other style.<br>Unit: ms|
184
185### animationMode<sup>12+</sup>
186
187animationMode(mode: Optional\<AnimationMode\>)
188
189Sets the animation mode for switching between tabs.
190
191**Atomic service API**: This API can be used in atomic services since API version 12.
192
193**System capability**: SystemCapability.ArkUI.ArkUI.Full
194
195**Parameters**
196
197| Name| Type  | Mandatory| Description                                                        |
198| ------ | ------ | ---- | ------------------------------------------------------------ |
199| mode  | Optional\<[AnimationMode](#animationmode12)\>| Yes  | Animation mode for switching between tabs.<br>The default value varies.<br>The default value is **AnimationMode.CONTENT_FIRST**, indicating that the content of the target page is loaded before the switching animation starts in the process of switching between tabs.|
200
201### barPosition<sup>9+</sup>
202
203barPosition(value: BarPosition)
204
205Position of the **Tabs** component.
206
207**Atomic service API**: This API can be used in atomic services since API version 11.
208
209**System capability**: SystemCapability.ArkUI.ArkUI.Full
210
211**Parameters**
212
213| Name| Type                              | Mandatory| Description                 |
214| ----- | ---------------------------------- | ---- | -------------------- |
215| value | [BarPosition](#barposition)| Yes | Position of the **Tabs** component.<br>Default value: **BarPosition.Start**  |
216
217### divider<sup>10+</sup>
218
219divider(value: DividerStyle | null)
220
221Sets the divider style for the **TabBar** and **TabContent** components.
222
223**Atomic service API**: This API can be used in atomic services since API version 11.
224
225**System capability**: SystemCapability.ArkUI.ArkUI.Full
226
227**Parameters**
228
229| Name| Type                                                     | Mandatory| Description                                                        |
230| ------ | --------------------------------------------------------- | ---- | ------------------------------------------------------------ |
231| value  | [DividerStyle](#dividerstyle10) \| null | Yes  | Divider style. By default, the divider is not displayed.<br>**DividerStyle**: divider style.<br>**null**: The divider is not displayed.|
232
233### fadingEdge<sup>10+</sup>
234
235fadingEdge(value: boolean)
236
237Sets whether the tab fades out when it exceeds the container width. It is recommended that this attribute be used together with the **barBackgroundColor** attribute. If the **barBackgroundColor** attribute is not defined, the tab fades out in white when it exceeds the container width by default.
238
239**Atomic service API**: This API can be used in atomic services since API version 11.
240
241**System capability**: SystemCapability.ArkUI.ArkUI.Full
242
243**Parameters**
244
245| Name| Type   | Mandatory| Description                                              |
246| ------ | ------- | ---- | -------------------------------------------------- |
247| value  | boolean | Yes  | Whether the tab fades out when it exceeds the container width.<br>Default value: **true**|
248
249### barOverlap<sup>10+</sup>
250
251barOverlap(value: boolean)
252
253Sets whether the tab bar is superimposed on the **TabContent** component after having its background blurred.
254
255**Atomic service API**: This API can be used in atomic services since API version 11.
256
257**System capability**: SystemCapability.ArkUI.ArkUI.Full
258
259**Parameters**
260
261| Name| Type   | Mandatory| Description                                                        |
262| ------ | ------- | ---- | ------------------------------------------------------------ |
263| value  | boolean | Yes  | Whether the tab bar is superimposed on the **TabContent** component after having its background blurred.<br>Default value: **false**|
264
265### barBackgroundColor<sup>10+</sup>
266
267barBackgroundColor(value: ResourceColor)
268
269Background color of the tab bar.
270
271**Atomic service API**: This API can be used in atomic services since API version 11.
272
273**System capability**: SystemCapability.ArkUI.ArkUI.Full
274
275**Parameters**
276
277| Name| Type                                      | Mandatory| Description                                |
278| ------ | ------------------------------------------ | ---- | ------------------------------------ |
279| value  | [ResourceColor](ts-types.md#resourcecolor) | Yes  | Background color of the tab bar.<br>Default value: **Color.Transparent**|
280
281### barBackgroundBlurStyle<sup>11+</sup>
282
283barBackgroundBlurStyle(value: BlurStyle)
284
285Sets the background blur style of the tab bar.
286
287**Atomic service API**: This API can be used in atomic services since API version 11.
288
289**System capability**: SystemCapability.ArkUI.ArkUI.Full
290
291**Parameters**
292
293| Name| Type                                        | Mandatory| Description                                    |
294| ------ | -------------------------------------------- | ---- | ---------------------------------------- |
295| value  | [BlurStyle](ts-universal-attributes-background.md#blurstyle9) | Yes  | Background blur style of the tab bar.<br>Default value: **BlurStyle.NONE**|
296
297### barGridAlign<sup>10+</sup>
298
299barGridAlign(value: BarGridColumnOptions)
300
301Sets the visible area of the tab bar in grid mode. For details, see **BarGridColumnOptions**. This attribute is effective only in horizontal mode. It is not applicable to [XS, XL, and XXL devices](../../../ui/arkts-layout-development-grid-layout.md#grid-breakpoints).
302
303**Atomic service API**: This API can be used in atomic services since API version 11.
304
305**System capability**: SystemCapability.ArkUI.ArkUI.Full
306
307**Parameters**
308
309| Name| Type                                                   | Mandatory| Description                              |
310| ------ | ------------------------------------------------------- | ---- | ---------------------------------- |
311| value  | [BarGridColumnOptions](#bargridcolumnoptions10) | Yes  | Visible area of the tab bar in grid mode.|
312
313### edgeEffect<sup>12+</sup>
314
315edgeEffect(edgeEffect: Optional&lt;EdgeEffect&gt;)
316
317Sets the edge effect used when the boundary of the scrolling area is reached.
318
319**Atomic service API**: This API can be used in atomic services since API version 12.
320
321**System capability**: SystemCapability.ArkUI.ArkUI.Full
322
323**Parameters**
324
325| Name| Type                                         | Mandatory| Description                                        |
326| ------ | --------------------------------------------- | ---- | -------------------------------------------- |
327| edgeEffect  | Optional&lt;[EdgeEffect](ts-appendix-enums.md#edgeeffect)&gt; | Yes  | Effect used when the boundary of the scrolling area is reached.<br>Default value: **EdgeEffect.Spring**|
328
329## DividerStyle<sup>10+</sup>
330
331Describes the divider style.
332
333**Atomic service API**: This API can be used in atomic services since API version 11.
334
335**System capability**: SystemCapability.ArkUI.ArkUI.Full
336
337| Name         | Type                                    | Mandatory  | Description                                      |
338| ----------- | ---------------------------------------- | ---- | ---------------------------------------- |
339| strokeWidth | [Length](ts-types.md#length)             | Yes   | Width of the divider. It cannot be set in percentage.<br>Default value: **0.0**<br>Unit: vp          |
340| color       | [ResourceColor](ts-types.md#resourcecolor) | No   | Color of the divider.<br>Default value: **#33182431**               |
341| startMargin | [Length](ts-types.md#length)             | No   | Distance between the divider and the top of the sidebar. It cannot be set in percentage.<br>Default value: **0.0**<br>Unit: vp|
342| endMargin   | [Length](ts-types.md#length)             | No   | Distance between the divider and the bottom of the sidebar. It cannot be set in percentage.<br>Default value: **0.0**<br>Unit: vp|
343
344## BarGridColumnOptions<sup>10+</sup>
345
346Implements a **BarGridColumnOptions** object for setting the visible area of the tab bar in grid mode, including the column margin and gutter, as well as the number of columns occupied by tabs under small, medium, and large screen sizes.
347
348**Atomic service API**: This API can be used in atomic services since API version 11.
349
350**System capability**: SystemCapability.ArkUI.ArkUI.Full
351
352| Name         | Type                                    | Mandatory  | Description                                      |
353| ----------- | ---------------------------------------- | ---- | ---------------------------------------- |
354| margin | [Dimension](ts-types.md#dimension10)             | No   | Column margin in grid mode. It cannot be set in percentage.<br>Default value: **24.0**<br>Unit: vp                       |
355| gutter      | [Dimension](ts-types.md#dimension10) | No   | Column gutter (that is, gap between columns) in grid mode. It cannot be set in percentage.<br>Default value: **24.0**<br>Unit: vp                    |
356| sm | number            | No   | Number of columns occupied by a tab on a screen whose width is greater than or equal to 320 vp but less than 600 vp.<br>The value must be a non-negative even number. The default value is **-1**, indicating that the tab takes up the entire width of the tab bar.|
357| md   | number          | No   | Number of columns occupied by a tab on a screen whose width is greater than or equal to 600 vp but less than 800 vp.<br>The value must be a non-negative even number. The default value is **-1**, indicating that the tab takes up the entire width of the tab bar.|
358| lg   | number           | No   | Number of columns occupied by a tab on a screen whose width is greater than or equal to 840 vp but less than 1024 vp.<br>The value must be a non-negative even number. The default value is **-1**, indicating that the tab takes up the entire width of the tab bar.|
359
360## ScrollableBarModeOptions<sup>10+</sup>
361
362Implements a **ScrollableBarModeOptions** object.
363
364**Atomic service API**: This API can be used in atomic services since API version 11.
365
366**System capability**: SystemCapability.ArkUI.ArkUI.Full
367
368| Name         | Type                                    | Mandatory  | Description                                      |
369| ----------- | ---------------------------------------- | ---- | ---------------------------------------- |
370| margin | [Dimension](ts-types.md#dimension10)          | No   | Left and right margin of the tab bar in scrollable mode. It cannot be set in percentage.<br>Default value: **0.0**<br>Unit: vp                   |
371| nonScrollableLayoutStyle      | [LayoutStyle](#layoutstyle10) | No   | Tab layout mode of the tab bar when not scrolling in scrollable mode.<br>Default value: **LayoutStyle.ALWAYS_CENTER**          |
372
373## BarMode
374
375Enumerates layout modes of the tab bar.
376
377**Atomic service API**: This API can be used in atomic services since API version 11.
378
379**System capability**: SystemCapability.ArkUI.ArkUI.Full
380
381| Name       | Value| Description                                      |
382| ---------- | -- | ---------------------------------------- |
383| Scrollable | 0  | The width of each tab is determined by the actual layout. The tabs are scrollable in the following case: In horizontal layout, the total width exceeds the tab bar width; in horizontal layout, the total height exceeds the tab bar height.|
384| Fixed      | 1  | The width of each tab is determined by equally dividing the number of tabs by the bar width (or bar height in the vertical layout).|
385
386## AnimationMode<sup>12+</sup>
387
388Enumerates the animation modes for switching between tabs.
389
390**Atomic service API**: This API can be used in atomic services since API version 12.
391
392**System capability**: SystemCapability.ArkUI.ArkUI.Full
393
394| Name         | Value | Description                                      |
395| ------------- | -- | ---------------------------------------- |
396| CONTENT_FIRST | 0  | Load the content of the target page before starting the switching animation.|
397| ACTION_FIRST  | 1  | Start the switching animation before loading the content of the target page. For the settings to take effect, the height and width of tabs must be set to **auto**.|
398| NO_ANIMATION  | 2  | The switching animation is disabled.|
399
400## LayoutStyle<sup>10+</sup>
401
402Enumerates the tab layout styles of the tab bar when not scrolling in scrollable mode.
403
404**Atomic service API**: This API can be used in atomic services since API version 11.
405
406**System capability**: SystemCapability.ArkUI.ArkUI.Full
407
408| Name        | Value| Description                                      |
409| ---------- | -- | ---------------------------------------- |
410| ALWAYS_CENTER | 0 | When the tab content exceeds the tab bar width, the tabs are scrollable.<br>Otherwise, the tabs are compactly centered and not scrollable.|
411| ALWAYS_AVERAGE_SPLIT | 1 | When the tab content exceeds the tab bar width, the tabs are scrollable.<br>Otherwise, the tabs are not scrollable, and the tab bar width is distributed evenly between all tabs.<br>This option is valid only in horizontal mode, and is equivalent to **LayoutStyle.ALWAYS_CENTER** otherwise.|
412| SPACE_BETWEEN_OR_CENTER      | 2 | When the tab content exceeds the tab bar width, the tabs are scrollable.<br>When the tab content exceeds half of the tab bar width but still within the tab bar width, the tabs are compactly centered and not scrollable.<br>When the tab content does not exceed half of the tab bar width, the tabs are centered within half of the tab bar width, with even spacing between, and not scrollable.|
413
414## Events
415
416In addition to the [universal events](ts-universal-events-click.md), the following events are supported.
417
418### onChange
419
420onChange(event: (index: number) =&gt; void)
421
422Triggered when a tab is switched.
423
424This event is triggered when any of the following conditions is met:
425
4261. The **TabContent** component supports sliding, and the user slides on the tab bar.
427
4282. The [Controller](#tabscontroller) API is called.
429
4303. The attribute value is updated using a [state variable](../../../quick-start/arkts-state.md).
431
4324. A tab is clicked.
433
434**Atomic service API**: This API can be used in atomic services since API version 11.
435
436**System capability**: SystemCapability.ArkUI.ArkUI.Full
437
438**Parameters**
439
440| Name| Type  | Mandatory| Description                                  |
441| ------ | ------ | ---- | -------------------------------------- |
442| index  | number | Yes  | Index of the active tab. The index starts from 0.|
443
444### onTabBarClick<sup>10+</sup>
445
446onTabBarClick(event: (index: number) =&gt; void)
447
448Triggered when a tab is clicked.
449
450**Atomic service API**: This API can be used in atomic services since API version 11.
451
452**System capability**: SystemCapability.ArkUI.ArkUI.Full
453
454**Parameters**
455
456| Name| Type  | Mandatory| Description                                |
457| ------ | ------ | ---- | ------------------------------------ |
458| index  | number | Yes  | Index of the clicked tab. The index starts from 0.|
459
460### onAnimationStart<sup>11+</sup>
461
462onAnimationStart(handler: (index: number, targetIndex: number, event: TabsAnimationEvent) => void)
463
464Triggered when the tab switching animation starts. The **index** parameter indicates the index before the animation starts (not the one after).
465
466**Atomic service API**: This API can be used in atomic services since API version 12.
467
468**System capability**: SystemCapability.ArkUI.ArkUI.Full
469
470**Parameters**
471
472| Name     | Type                                                  | Mandatory| Description                                                        |
473| ----------- | ------------------------------------------------------ | ---- | ------------------------------------------------------------ |
474| index       | number                                                 | Yes  | Index of the currently displayed element.                                        |
475| targetIndex | number                                                 | Yes  | Index of the target element to switch to.                                    |
476| event       | [TabsAnimationEvent](#tabsanimationevent11) | Yes  | Extra information of the animation, including the offset of the currently displayed element and target element relative to the start position of the **Tabs** along the main axis, and the hands-off velocity.|
477
478### onAnimationEnd<sup>11+</sup>
479
480onAnimationEnd(handler: (index: number, event: TabsAnimationEvent) => void)
481
482Triggered when the tab switching animation ends. This event is triggered when the tab switching animation ends, whether it is caused by gesture interruption or not. The **index** parameter indicates the index after the animation ends.
483
484**Atomic service API**: This API can be used in atomic services since API version 12.
485
486**System capability**: SystemCapability.ArkUI.ArkUI.Full
487
488**Parameters**
489
490| Name| Type                                                  | Mandatory| Description                                                        |
491| ------ | ------------------------------------------------------ | ---- | ------------------------------------------------------------ |
492| index  | number                                                 | Yes  | Index of the currently displayed element.                                        |
493| event  | [TabsAnimationEvent](#tabsanimationevent11) | Yes  | Extra information of the animation, which is the offset of the currently displayed element relative to the start position of the **Tabs** along the main axis.|
494
495### onGestureSwipe<sup>11+</sup>
496
497onGestureSwipe(handler: (index: number, event: TabsAnimationEvent) => void)
498
499Triggered on a frame-by-frame basis when the tab is switched by a swipe.
500
501**Atomic service API**: This API can be used in atomic services since API version 12.
502
503**System capability**: SystemCapability.ArkUI.ArkUI.Full
504
505**Parameters**
506
507| Name| Type                                                  | Mandatory| Description                                                        |
508| ------ | ------------------------------------------------------ | ---- | ------------------------------------------------------------ |
509| index  | number                                                 | Yes  | Index of the currently displayed element.                                        |
510| event  | [TabsAnimationEvent](#tabsanimationevent11) | Yes  | Extra information of the animation, which is the offset of the currently displayed element relative to the start position of the **Tabs** along the main axis.|
511
512### customContentTransition<sup>11+</sup>
513
514customContentTransition(delegate: (from: number, to: number) => TabContentAnimatedTransition \| undefined)
515
516Sets the custom tab switching animation.
517
518Instructions:
519
5201. When the custom tab switching animation is used, the default switching animation of the **Tabs** component is disabled, and tabs cannot be switched through swiping.<br>2. The value **undefined** means not to use the custom tab switching animation, in which case the default switching animation is used.<br>3. The custom tab switching animation cannot be interrupted.<br>4. Currently, the custom tab switching animation can be triggered only by clicking a tab or by calling the **TabsController.changeIndex()** API.<br>5. When the custom tab switching animation is used, the **Tabs** component supports all events except **onGestureSwipe**.<br>6. Notes about the **onChange** and **onAnimationEnd** events: If the second custom animation is triggered during the execution of the first custom animation, the **onChange** and **onAnimationEnd** events of the first custom animation will be triggered when the second custom animation starts.<br>7. When the custom animation is used, the stack layout is used for pages involved in the animation. If the **zIndex** attribute is not set for related pages, the **zIndex** values of all pages are the same. In this case, the pages are rendered in the order in which they are added to the component tree (that is, the sequence of page indexes). In light of this, to control the rendering levels of pages, set the **zIndex** attribute of the pages.
521
522**Widget capability**: This API can be used in ArkTS widgets since API version 11.
523
524**Atomic service API**: This API can be used in atomic services since API version 12.
525
526**System capability**: SystemCapability.ArkUI.ArkUI.Full
527
528**Parameters**
529
530| Name| Type  | Mandatory| Description                           |
531| ------ | ------ | ---- | ------------------------------- |
532| from   | number | Yes  | Index of the currently displayed tab before the animation starts.|
533| to     | number | Yes  | Index of the target tab before the animation starts.|
534
535**Return value**
536
537| Type                                                        | Description                    |
538| ------------------------------------------------------------ | ------------------------ |
539| [TabContentAnimatedTransition](#tabcontentanimatedtransition11) \| undefined | Information about the custom tab switching animation.|
540
541### onContentWillChange<sup>12+</sup>
542
543onContentWillChange(handler: (currentIndex: number, comingIndex: number) => boolean)
544
545Triggered when a new page is about to be displayed.
546
547Specifically, this event is triggered in the following cases:
548
5491. When the user swipes on the **TabContent** component (provided that it supports swiping) to switch to a new page.
550
5512. When **TabsController.changeIndex** is called to switch to a new page.
552
5533. When the **index** attribute is changed to switch to a new page.
554
5554. When the user clicks a tab on the tab bar to switch to a new page.
556
5575. When the user presses the left or right arrow key on the keyboard to switch to a new page while the tab bar is focused.
558
559**Atomic service API**: This API can be used in atomic services since API version 12.
560
561**System capability**: SystemCapability.ArkUI.ArkUI.Full
562
563**Parameters**
564
565| Name      | Type  | Mandatory| Description                                      |
566| ------------ | ------ | ---- | ------------------------------------------ |
567| currentIndex | number | Yes  | Index of the active tab. The index starts from 0.|
568| comingIndex  | number | Yes  | Index of the new tab to be displayed.             |
569
570**Return value**
571
572| Type   | Description                                                        |
573| ------- | ------------------------------------------------------------ |
574| boolean | The value **true** means that the tab can switch to the new page.<br>The value **false** means that the tab cannot switch to the new page and will remain on the current page.|
575
576## TabsAnimationEvent<sup>11+</sup>
577
578Describes the animation information of the **Tabs** component.
579
580**Atomic service API**: This API can be used in atomic services since API version 12.
581
582**System capability**: SystemCapability.ArkUI.ArkUI.Full
583
584| Name           | Type     | Read Only| Optional| Description                                      |
585| ------------- | ---------- | ---- | ---- | ------------------------ |
586| currentOffset | number | No| No| Offset of the currently displayed element relative to the start position of the **Tabs** component along the main axis.<br> Unit: vp<br>Default value: **0**|
587| targetOffset | number | No| No| Offset of the target element relative to the start position of the **Tabs** component along the main axis.<br> Unit: vp<br>Default value: **0**|
588| velocity | number | No| No| Hands-off velocity at the beginning of the animation. Unit: VP/S<br>Default value: **0**|
589
590## TabContentAnimatedTransition<sup>11+</sup>
591
592Provides the information about the custom tab page switching animation.
593
594**Widget capability**: This API can be used in ArkTS widgets since API version 11.
595
596**Atomic service API**: This API can be used in atomic services since API version 12.
597
598**System capability**: SystemCapability.ArkUI.ArkUI.Full
599
600| Name           | Type        | Mandatory  | Description                                      |
601| ------------- | ---------------------- | ---- |---------------------- |
602| timeout | number | No| Timeout for the custom switching animation. The timer starts when the switching begins. If this timeframe passes without you calling the **finishTransition** API in [TabContentTransitionProxy](#tabcontenttransitionproxy11), the component will assume that the custom animation has ended and will proceed directly with subsequent operations. Unit: ms<br>Default value: **1000**|
603| transition | (proxy: [TabContentTransitionProxy](#tabcontenttransitionproxy11)) => void | Yes| Content of the custom switching animation.|
604
605## TabContentTransitionProxy<sup>11+</sup>
606
607Implements the proxy object returned during the execution of the custom switching animation of the **Tabs** component. You can use this object to obtain the start and target pages for the custom tab switching animation. In addition, you can call the **finishTransition** API of this object to notify the **Tabs** component of the ending of the custom animation.
608
609**Widget capability**: This API can be used in ArkTS widgets since API version 11.
610
611**Atomic service API**: This API can be used in atomic services since API version 12.
612
613**System capability**: SystemCapability.ArkUI.ArkUI.Full
614
615### Attributes
616
617| Name | Type    | Read Only| Optional| Description                        |
618| ----- | ------- | ---- | ---- | --------------------------- |
619| from | number | No| No| Index of the starting page of the custom animation.|
620| to | number | No| No| Index of the target tab to switch to.|
621
622### finishTransition
623
624finishTransition(): void
625
626Notifies the **Tabs** component that the custom animation has finished playing.
627
628**Widget capability**: This API can be used in ArkTS widgets since API version 11.
629
630**Atomic service API**: This API can be used in atomic services since API version 12.
631
632**System capability**: SystemCapability.ArkUI.ArkUI.Full
633
634## TabsController
635
636Defines a tab controller, which is used to control switching of tabs. One **TabsController** cannot control multiple **Tabs** components.
637
638**Atomic service API**: This API can be used in atomic services since API version 11.
639
640**System capability**: SystemCapability.ArkUI.ArkUI.Full
641
642### Objects to Import
643
644```ts
645let controller: TabsController = new TabsController()
646```
647
648### constructor
649
650constructor()
651
652A constructor used to create a **TabsController** object.
653
654**Atomic service API**: This API can be used in atomic services since API version 11.
655
656**System capability**: SystemCapability.ArkUI.ArkUI.Full
657
658### changeIndex
659
660changeIndex(value: number): void
661
662Switches to the specified tab.
663
664**Atomic service API**: This API can be used in atomic services since API version 11.
665
666**System capability**: SystemCapability.ArkUI.ArkUI.Full
667
668**Parameters**
669
670| Name  | Type  | Mandatory  | Description                                    |
671| ----- | ------ | ---- | ---------------------------------------- |
672| value | number | Yes   | Index of the tab. The value starts from 0.<br>**NOTE**<br>If this parameter is set to a value less than 0 or greater than the maximum number, the default value **0** is used.|
673
674### preloadItems<sup>12+</sup>
675
676preloadItems(indices: Optional\<Array\<number>>): Promise\<void>
677
678Preloads child nodes. After this API is called, all specified child nodes will be loaded at once. Therefore, for performance considerations, it is recommended that you load child nodes in batches.
679
680**Atomic service API**: This API can be used in atomic services since API version 12.
681
682**System capability**: SystemCapability.ArkUI.ArkUI.Full
683
684**Parameters**
685
686| Name  | Type  | Mandatory  | Description                                    |
687| ----- | ------ | ---- | ---------------------------------------- |
688| indices | Optional\<Array\<number>> | Yes| Array of indexes of the child nodes to preload.<br>The default value is an empty array.|
689
690**Return value**
691
692| Type                                                        | Description                    |
693| ------------------------------------------------------------ | ------------------------ |
694| Promise\<void> | Promise used to return the value.|
695
696**Error codes**
697
698For details about the error codes, see [Universal Error Codes](../../errorcode-universal.md).
699
700| ID  | Error Message                                     |
701| --------   | -------------------------------------------- |
702| 401 | Parameter invalid. Possible causes: 1. The parameter type is not Array\<number>; 2. The parameter is an empty array; 3. The parameter contains an invalid index. |
703
704## Example
705
706### Example 1
707
708This example uses **onChange** to implement the linkage between **tabBar** and **TabContent**.
709
710```ts
711// xxx.ets
712@Entry
713@Component
714struct TabsExample {
715  @State fontColor: string = '#182431'
716  @State selectedFontColor: string = '#007DFF'
717  @State currentIndex: number = 0
718  private controller: TabsController = new TabsController()
719
720  @Builder tabBuilder(index: number, name: string) {
721    Column() {
722      Text(name)
723        .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor)
724        .fontSize(16)
725        .fontWeight(this.currentIndex === index ? 500 : 400)
726        .lineHeight(22)
727        .margin({ top: 17, bottom: 7 })
728      Divider()
729        .strokeWidth(2)
730        .color('#007DFF')
731        .opacity(this.currentIndex === index ? 1 : 0)
732    }.width('100%')
733  }
734
735  build() {
736    Column() {
737      Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.controller }) {
738        TabContent() {
739          Column().width('100%').height('100%').backgroundColor('#00CB87')
740        }.tabBar(this.tabBuilder(0, 'green'))
741
742        TabContent() {
743          Column().width('100%').height('100%').backgroundColor('#007DFF')
744        }.tabBar(this.tabBuilder(1, 'blue'))
745
746        TabContent() {
747          Column().width('100%').height('100%').backgroundColor('#FFBF00')
748        }.tabBar(this.tabBuilder(2, 'yellow'))
749
750        TabContent() {
751          Column().width('100%').height('100%').backgroundColor('#E67C92')
752        }.tabBar(this.tabBuilder(3, 'pink'))
753      }
754      .vertical(false)
755      .barMode(BarMode.Fixed)
756      .barWidth(360)
757      .barHeight(56)
758      .animationDuration(400)
759      .onChange((index: number) => {
760        this.currentIndex = index
761      })
762      .width(360)
763      .height(296)
764      .margin({ top: 52 })
765      .backgroundColor('#F1F3F5')
766    }.width('100%')
767  }
768}
769```
770
771![tabs2](figures/tabs2.gif)
772
773### Example 2
774
775This example uses **divider** to present dividers in different styles.
776
777```ts
778// xxx.ets
779@Entry
780@Component
781struct TabsDivider1 {
782  private controller1: TabsController = new TabsController()
783  @State dividerColor: string = 'red'
784  @State strokeWidth: number = 2
785  @State startMargin: number = 0
786  @State endMargin: number = 0
787  @State nullFlag: boolean = false
788
789  build() {
790    Column() {
791      Tabs({ controller: this.controller1 }) {
792        TabContent() {
793          Column().width('100%').height('100%').backgroundColor(Color.Pink)
794        }.tabBar('pink')
795
796        TabContent() {
797          Column().width('100%').height('100%').backgroundColor(Color.Yellow)
798        }.tabBar('yellow')
799
800        TabContent() {
801          Column().width('100%').height('100%').backgroundColor(Color.Blue)
802        }.tabBar('blue')
803
804        TabContent() {
805          Column().width('100%').height('100%').backgroundColor(Color.Green)
806        }.tabBar('green')
807
808        TabContent() {
809          Column().width('100%').height('100%').backgroundColor(Color.Red)
810        }.tabBar('red')
811      }
812      .vertical(true)
813      .scrollable(true)
814      .barMode(BarMode.Fixed)
815      .barWidth(70)
816      .barHeight(200)
817      .animationDuration(400)
818      .onChange((index: number) => {
819        console.info(index.toString())
820      })
821      .height('200vp')
822      .margin({ bottom: '12vp' })
823      .divider(this.nullFlag ? null : {
824        strokeWidth: this.strokeWidth,
825        color: this.dividerColor,
826        startMargin: this.startMargin,
827        endMargin: this.endMargin
828      })
829
830      Button ('Regular Divider').width('100%').margin({ bottom: '12vp'})
831        .onClick(() => {
832          this.nullFlag = false;
833          this.strokeWidth = 2;
834          this.dividerColor = 'red';
835          this.startMargin = 0;
836          this.endMargin = 0;
837        })
838      Button('Empty Divider').width('100%').margin({ bottom: '12vp' })
839        .onClick(() => {
840          this.nullFlag = true
841        })
842      Button('Change to Blue').width('100%').margin({ bottom: '12vp'})
843        .onClick(() => {
844          this.dividerColor = 'blue'
845        })
846      Button('Increase Width').width('100%').margin({ bottom: '12vp' })
847        .onClick(() => {
848          this.strokeWidth += 2
849        })
850      Button('Decrease Width').width('100%').margin({ bottom: '12vp'})
851        .onClick(() => {
852          if (this.strokeWidth > 2) {
853            this.strokeWidth -= 2
854          }
855        })
856      Button ('Increase Top Margin').width ('100%').margin ({ bottom:'12vp'})
857        .onClick(() => {
858          this.startMargin += 2
859        })
860      Button ('Decrease Top Margin').width ('100%').margin ({ bottom:'12vp' })
861        .onClick(() => {
862          if (this.startMargin > 2) {
863            this.startMargin -= 2
864          }
865        })
866      Button ('Increase Bottom Margin').width ('100%').margin ({ bottom:'12vp'})
867        .onClick(() => {
868          this.endMargin += 2
869        })
870      Button ('Decrease Bottom Margin').width ('100%').margin ({ bottom:'12vp' })
871        .onClick(() => {
872          if (this.endMargin > 2) {
873            this.endMargin -= 2
874          }
875        })
876    }.padding({ top: '24vp', left: '24vp', right: '24vp' })
877  }
878}
879```
880
881![tabs3](figures/tabs3.gif)
882
883### Example 3
884
885This example uses **fadingEdge** to specify whether to fade out tabs.
886
887```ts
888// xxx.ets
889@Entry
890@Component
891struct TabsOpaque {
892  @State message: string = 'Hello World'
893  private controller: TabsController = new TabsController()
894  private controller1: TabsController = new TabsController()
895  @State selfFadingFade: boolean = true;
896
897  build() {
898    Column() {
899      Button (Set Tab to Fade').width ('100%').margin ({bottom: '12vp'})
900        .onClick((event?: ClickEvent) => {
901          this.selfFadingFade = true;
902        })
903      Button (Set Tab Not to Fade').width ('100%').margin ({bottom: '12vp'})
904        .onClick((event?: ClickEvent) => {
905          this.selfFadingFade = false;
906        })
907      Tabs({ barPosition: BarPosition.End, controller: this.controller }) {
908        TabContent() {
909          Column().width('100%').height('100%').backgroundColor(Color.Pink)
910        }.tabBar('pink')
911
912        TabContent() {
913          Column().width('100%').height('100%').backgroundColor(Color.Yellow)
914        }.tabBar('yellow')
915
916        TabContent() {
917          Column().width('100%').height('100%').backgroundColor(Color.Blue)
918        }.tabBar('blue')
919
920        TabContent() {
921          Column().width('100%').height('100%').backgroundColor(Color.Green)
922        }.tabBar('green')
923
924        TabContent() {
925          Column().width('100%').height('100%').backgroundColor(Color.Green)
926        }.tabBar('green')
927
928        TabContent() {
929          Column().width('100%').height('100%').backgroundColor(Color.Green)
930        }.tabBar('green')
931
932        TabContent() {
933          Column().width('100%').height('100%').backgroundColor(Color.Green)
934        }.tabBar('green')
935
936        TabContent() {
937          Column().width('100%').height('100%').backgroundColor(Color.Green)
938        }.tabBar('green')
939      }
940      .vertical(false)
941      .scrollable(true)
942      .barMode(BarMode.Scrollable)
943      .barHeight(80)
944      .animationDuration(400)
945      .onChange((index: number) => {
946        console.info(index.toString())
947      })
948      .fadingEdge(this.selfFadingFade)
949      .height('30%')
950      .width('100%')
951
952      Tabs({ barPosition: BarPosition.Start, controller: this.controller1 }) {
953        TabContent() {
954          Column().width('100%').height('100%').backgroundColor(Color.Pink)
955        }.tabBar('pink')
956
957        TabContent() {
958          Column().width('100%').height('100%').backgroundColor(Color.Yellow)
959        }.tabBar('yellow')
960
961        TabContent() {
962          Column().width('100%').height('100%').backgroundColor(Color.Blue)
963        }.tabBar('blue')
964
965        TabContent() {
966          Column().width('100%').height('100%').backgroundColor(Color.Green)
967        }.tabBar('green')
968
969        TabContent() {
970          Column().width('100%').height('100%').backgroundColor(Color.Green)
971        }.tabBar('green')
972
973        TabContent() {
974          Column().width('100%').height('100%').backgroundColor(Color.Green)
975        }.tabBar('green')
976      }
977      .vertical(true)
978      .scrollable(true)
979      .barMode(BarMode.Scrollable)
980      .barHeight(200)
981      .barWidth(80)
982      .animationDuration(400)
983      .onChange((index: number) => {
984        console.info(index.toString())
985      })
986      .fadingEdge(this.selfFadingFade)
987      .height('30%')
988      .width('100%')
989    }
990    .padding({ top: '24vp', left: '24vp', right: '24vp' })
991  }
992}
993```
994
995![tabs4](figures/tabs4.gif)
996
997### Example 4
998
999This example uses **barOverlap** to specify whether the tab bar is superimposed on the **TabContent** component after having its background blurred.
1000
1001```ts
1002// xxx.ets
1003@Entry
1004@Component
1005struct barBackgroundColorTest {
1006  private controller: TabsController = new TabsController()
1007  @State barOverlap: boolean = true;
1008  @State barBackgroundColor: string = '#88888888';
1009
1010  build() {
1011    Column() {
1012      Button ("Change barOverlap").width ('100%').margin ({ bottom:'12vp'})
1013        .onClick((event?: ClickEvent) => {
1014          if (this.barOverlap) {
1015            this.barOverlap = false;
1016          } else {
1017            this.barOverlap = true;
1018          }
1019        })
1020
1021      Tabs({ barPosition: BarPosition.Start, index: 0, controller: this.controller }) {
1022        TabContent() {
1023          Column() {
1024            Text(`barOverlap ${this.barOverlap}`).fontSize(16).margin({ top: this.barOverlap ? '56vp' : 0 })
1025            Text(`barBackgroundColor ${this.barBackgroundColor}`).fontSize(16)
1026          }.width('100%').width('100%').height('100%')
1027          .backgroundColor(Color.Pink)
1028        }
1029        .tabBar(new BottomTabBarStyle($r('sys.media.ohos_app_icon'), "1"))
1030
1031        TabContent() {
1032          Column() {
1033            Text(`barOverlap ${this.barOverlap}`).fontSize(16).margin({ top: this.barOverlap ? '56vp' : 0 })
1034            Text(`barBackgroundColor ${this.barBackgroundColor}`).fontSize(16)
1035          }.width('100%').width('100%').height('100%')
1036          .backgroundColor(Color.Yellow)
1037        }
1038        .tabBar(new BottomTabBarStyle($r('sys.media.ohos_app_icon'), "2"))
1039
1040        TabContent() {
1041          Column() {
1042            Text(`barOverlap ${this.barOverlap}`).fontSize(16).margin({ top: this.barOverlap ? '56vp' : 0 })
1043            Text(`barBackgroundColor ${this.barBackgroundColor}`).fontSize(16)
1044          }.width('100%').width('100%').height('100%')
1045          .backgroundColor(Color.Green)
1046        }
1047        .tabBar(new BottomTabBarStyle($r('sys.media.ohos_app_icon'), "3"))
1048      }
1049      .vertical(false)
1050      .barMode(BarMode.Fixed)
1051      .height('60%')
1052      .barOverlap(this.barOverlap)
1053      .scrollable(true)
1054      .animationDuration(10)
1055      .barBackgroundColor(this.barBackgroundColor)
1056    }
1057    .height(500)
1058    .padding({ top: '24vp', left: '24vp', right: '24vp' })
1059  }
1060}
1061```
1062
1063![tabs5](figures/tabs5.gif)
1064
1065### Example 5
1066
1067This example uses **barGridAlign** to set the visible area of the tab bar in grid mode.
1068
1069```ts
1070// xxx.ets
1071@Entry
1072@Component
1073struct TabsExample5 {
1074  private controller: TabsController = new TabsController()
1075  @State gridMargin: number = 10
1076  @State gridGutter: number = 10
1077  @State sm: number = -2
1078  @State clickedContent: string = "";
1079
1080  build() {
1081    Column() {
1082      Row() {
1083        Button("gridMargin+10 " + this.gridMargin)
1084          .width('47%')
1085          .height(50)
1086          .margin({ top: 5 })
1087          .onClick((event?: ClickEvent) => {
1088            this.gridMargin += 10
1089          })
1090          .margin({ right: '6%', bottom: '12vp' })
1091        Button("gridMargin-10 " + this.gridMargin)
1092          .width('47%')
1093          .height(50)
1094          .margin({ top: 5 })
1095          .onClick((event?: ClickEvent) => {
1096            this.gridMargin -= 10
1097          })
1098          .margin({ bottom: '12vp' })
1099      }
1100
1101      Row() {
1102        Button("gridGutter+10 " + this.gridGutter)
1103          .width('47%')
1104          .height(50)
1105          .margin({ top: 5 })
1106          .onClick((event?: ClickEvent) => {
1107            this.gridGutter += 10
1108          })
1109          .margin({ right: '6%', bottom: '12vp' })
1110        Button("gridGutter-10 " + this.gridGutter)
1111          .width('47%')
1112          .height(50)
1113          .margin({ top: 5 })
1114          .onClick((event?: ClickEvent) => {
1115            this.gridGutter -= 10
1116          })
1117          .margin({ bottom: '12vp' })
1118      }
1119
1120      Row() {
1121        Button("sm+2 " + this.sm)
1122          .width('47%')
1123          .height(50)
1124          .margin({ top: 5 })
1125          .onClick((event?: ClickEvent) => {
1126            this.sm += 2
1127          })
1128          .margin({ right: '6%' })
1129        Button("sm-2 " + this.sm).width('47%').height(50).margin({ top: 5 })
1130          .onClick((event?: ClickEvent) => {
1131            this.sm -= 2
1132          })
1133      }
1134
1135      Text ("Tab clicks: "+ this.clickedContent).width ('100%').height (200).margin ({ top: 5 })
1136
1137
1138      Tabs({ barPosition: BarPosition.End, controller: this.controller }) {
1139        TabContent() {
1140          Column().width('100%').height('100%').backgroundColor(Color.Pink)
1141        }.tabBar(BottomTabBarStyle.of($r("sys.media.ohos_app_icon"), "1"))
1142
1143        TabContent() {
1144          Column().width('100%').height('100%').backgroundColor(Color.Green)
1145        }.tabBar(BottomTabBarStyle.of($r("sys.media.ohos_app_icon"), "2"))
1146
1147        TabContent() {
1148          Column().width('100%').height('100%').backgroundColor(Color.Blue)
1149        }.tabBar(BottomTabBarStyle.of($r("sys.media.ohos_app_icon"), "3"))
1150      }
1151      .width('350vp')
1152      .animationDuration(300)
1153      .height('60%')
1154      .barGridAlign({ sm: this.sm, margin: this.gridMargin, gutter: this.gridGutter })
1155      .backgroundColor(0xf1f3f5)
1156      .onTabBarClick((index: number) => {
1157        this.clickedContent += "index " + index + " was clicked\n";
1158      })
1159    }
1160    .width('100%')
1161    .height(500)
1162    .margin({ top: 5 })
1163    .padding('10vp')
1164  }
1165}
1166```
1167
1168![tabs5](figures/tabs6.gif)
1169
1170### Example 6
1171
1172This example implements the **ScrollableBarModeOptions** parameter of **barMode**. This parameter is effective only in **Scrollable** mode.
1173
1174```ts
1175// xxx.ets
1176@Entry
1177@Component
1178struct TabsExample6 {
1179  private controller: TabsController = new TabsController()
1180  @State scrollMargin: number = 0
1181  @State layoutStyle: LayoutStyle = LayoutStyle.ALWAYS_CENTER
1182  @State text: string = "Text"
1183
1184  build() {
1185    Column() {
1186      Row() {
1187        Button("scrollMargin+10 " + this.scrollMargin)
1188          .width('47%')
1189          .height(50)
1190          .margin({ top: 5 })
1191          .onClick((event?: ClickEvent) => {
1192            this.scrollMargin += 10
1193          })
1194          .margin({ right: '6%', bottom: '12vp' })
1195        Button("scrollMargin-10 " + this.scrollMargin)
1196          .width('47%')
1197          .height(50)
1198          .margin({ top: 5 })
1199          .onClick((event?: ClickEvent) => {
1200            this.scrollMargin -= 10
1201          })
1202          .margin({ bottom: '12vp' })
1203      }
1204
1205      Row() {
1206        Button ("Add Text")
1207          .width('47%')
1208          .height(50)
1209          .margin({ top: 5 })
1210          .onClick((event?: ClickEvent) => {
1211            this.text += 'Add Text'
1212          })
1213          .margin({ right: '6%', bottom: '12vp' })
1214        Button ("Reset Text")
1215          .width('47%')
1216          .height(50)
1217          .margin({ top: 5 })
1218          .onClick((event?: ClickEvent) => {
1219            this.text = "Text"
1220          })
1221          .margin({ bottom: '12vp' })
1222      }
1223
1224      Row() {
1225        Button("layoutStyle.ALWAYS_CENTER")
1226          .width('100%')
1227          .height(50)
1228          .margin({ top: 5 })
1229          .fontSize(15)
1230          .onClick((event?: ClickEvent) => {
1231            this.layoutStyle = LayoutStyle.ALWAYS_CENTER;
1232          })
1233          .margin({ bottom: '12vp' })
1234      }
1235
1236      Row() {
1237        Button("layoutStyle.ALWAYS_AVERAGE_SPLIT")
1238          .width('100%')
1239          .height(50)
1240          .margin({ top: 5 })
1241          .fontSize(15)
1242          .onClick((event?: ClickEvent) => {
1243            this.layoutStyle = LayoutStyle.ALWAYS_AVERAGE_SPLIT;
1244          })
1245          .margin({ bottom: '12vp' })
1246      }
1247
1248      Row() {
1249        Button("layoutStyle.SPACE_BETWEEN_OR_CENTER")
1250          .width('100%')
1251          .height(50)
1252          .margin({ top: 5 })
1253          .fontSize(15)
1254          .onClick((event?: ClickEvent) => {
1255            this.layoutStyle = LayoutStyle.SPACE_BETWEEN_OR_CENTER;
1256          })
1257          .margin({ bottom: '12vp' })
1258      }
1259
1260      Tabs({ barPosition: BarPosition.End, controller: this.controller }) {
1261        TabContent() {
1262          Column().width('100%').height('100%').backgroundColor(Color.Pink)
1263        }.tabBar(SubTabBarStyle.of(this.text))
1264
1265        TabContent() {
1266          Column().width('100%').height('100%').backgroundColor(Color.Green)
1267        }.tabBar(SubTabBarStyle.of(this.text))
1268
1269        TabContent() {
1270          Column().width('100%').height('100%').backgroundColor(Color.Blue)
1271        }.tabBar(SubTabBarStyle.of(this.text))
1272      }
1273      .animationDuration(300)
1274      .height('60%')
1275      .backgroundColor(0xf1f3f5)
1276      .barMode(BarMode.Scrollable, { margin: this.scrollMargin, nonScrollableLayoutStyle: this.layoutStyle })
1277    }
1278    .width('100%')
1279    .height(500)
1280    .margin({ top: 5 })
1281    .padding('24vp')
1282  }
1283}
1284```
1285
1286![tabs5](figures/tabs7.gif)
1287
1288### Example 7
1289
1290This example uses **customContentTransition** to implement a custom tab switching animation.
1291
1292```ts
1293// xxx.ets
1294interface itemType {
1295  text: string,
1296  backgroundColor: Color
1297}
1298
1299@Entry
1300@Component
1301struct TabsCustomAnimationExample {
1302  @State data: itemType[] = [
1303    {
1304      text: 'Red',
1305      backgroundColor: Color.Red
1306    },
1307    {
1308      text: 'Yellow',
1309      backgroundColor: Color.Yellow
1310    },
1311    {
1312      text: 'Blue',
1313      backgroundColor: Color.Blue
1314    }]
1315  @State opacityList: number[] = []
1316  @State scaleList: number[] = []
1317
1318  private durationList: number[] = []
1319  private timeoutList: number[] = []
1320  private customContentTransition: (from: number, to: number) => TabContentAnimatedTransition = (from: number, to: number) => {
1321    let tabContentAnimatedTransition = {
1322      timeout: this.timeoutList[from],
1323      transition: (proxy: TabContentTransitionProxy) => {
1324        this.scaleList[from] = 1.0
1325        this.scaleList[to] = 0.5
1326        this.opacityList[from] = 1.0
1327        this.opacityList[to] = 0.5
1328        animateTo({
1329          duration: this.durationList[from],
1330          onFinish: () => {
1331            proxy.finishTransition()
1332          }
1333        }, () => {
1334          this.scaleList[from] = 0.5
1335          this.scaleList[to] = 1.0
1336          this.opacityList[from] = 0.5
1337          this.opacityList[to] = 1.0
1338        })
1339      }
1340    } as TabContentAnimatedTransition
1341    return tabContentAnimatedTransition
1342  }
1343
1344  aboutToAppear(): void {
1345    let duration = 1000
1346    let timeout = 1000
1347    for (let i = 1; i <= this.data.length; i++) {
1348      this.opacityList.push(1.0)
1349      this.scaleList.push(1.0)
1350      this.durationList.push(duration * i)
1351      this.timeoutList.push(timeout * i)
1352    }
1353  }
1354
1355  build() {
1356    Column() {
1357      Tabs() {
1358        ForEach(this.data, (item: itemType, index: number) => {
1359          TabContent() {}
1360          .tabBar(item.text)
1361          .backgroundColor(item.backgroundColor)
1362          // Customize the opacity and scale animation.
1363          .opacity(this.opacityList[index])
1364          .scale({ x: this.scaleList[index], y: this.scaleList[index] })
1365        })
1366      }
1367      .backgroundColor(0xf1f3f5)
1368      .width('100%')
1369      .height(500)
1370      .customContentTransition(this.customContentTransition)
1371    }
1372  }
1373}
1374```
1375
1376![tabs5](figures/tabs8.gif)
1377### Example 8
1378
1379This example uses **onContentWillChange** to switch to a new page on swiping.
1380
1381```ts
1382//xxx.ets
1383@Entry
1384@Component
1385struct TabsExample {
1386  @State currentIndex: number = 2
1387  private controller: TabsController = new TabsController()
1388  @Builder tabBuilder(title: string,targetIndex: number) {
1389    Column(){
1390      Text(title).fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B')
1391    }.width('100%')
1392    .height(50)
1393    .justifyContent(FlexAlign.Center)
1394  }
1395  build() {
1396    Column() {
1397      Tabs({ barPosition: BarPosition.End, controller: this.controller, index: this.currentIndex }) {
1398        TabContent() {
1399          Column(){
1400            Text('Content of the Home tab')
1401          }.width('100%').height('100%').backgroundColor('#00CB87').justifyContent(FlexAlign.Center)
1402        }.tabBar(this.tabBuilder('Home',0))
1403
1404        TabContent() {
1405          Column(){
1406            Text('Content of the Discover tab')
1407          }.width('100%').height('100%').backgroundColor('#007DFF').justifyContent(FlexAlign.Center)
1408        }.tabBar (this.tabBuilder ('Discover', 1))
1409
1410        TabContent() {
1411          Column(){
1412            Text('Content of the Recommended tab')
1413          }.width('100%').height('100%').backgroundColor('#FFBF00').justifyContent(FlexAlign.Center)
1414        }.tabBar (this.tabBuilder ('Recommended', 2))
1415
1416        TabContent() {
1417          Column(){
1418            Text('Content of the Me tab')
1419          }.width('100%').height('100%').backgroundColor('#E67C92').justifyContent(FlexAlign.Center)
1420        }.tabBar(this.tabBuilder('Me',3))
1421      }
1422      .vertical(false)
1423      .barMode(BarMode.Fixed)
1424      .barWidth(360)
1425      .barHeight(60)
1426      .animationDuration(0)
1427      .onChange((index: number) => {
1428        this.currentIndex = index
1429      })
1430      .width(360)
1431      .height(600)
1432      .backgroundColor('#F1F3F5')
1433      .scrollable(true)
1434      .onContentWillChange((currentIndex, comingIndex) => {
1435        if (comingIndex == 2) {
1436          return false
1437        }
1438        return true
1439      })
1440
1441      Button('Change Index').width('50%').margin({ top: 20 })
1442        .onClick(()=>{
1443          this.currentIndex = (this.currentIndex + 1) % 4
1444        })
1445
1446      Button('changeIndex').width('50%').margin({ top: 20 })
1447        .onClick(()=>{
1448          this.currentIndex = (this.currentIndex + 1) % 4
1449          this.controller.changeIndex(this.currentIndex)
1450        })
1451    }.width('100%')
1452  }
1453}
1454```
1455
1456![tabs9](figures/tabs9.gif)
1457### Example 9
1458
1459This example uses **onChange**, **onAnimationStart**, **onAnimationEnd**, and **onGestureSwipe** APIs to customize the tab bar switching animation.
1460
1461```ts
1462// xxx.ets
1463import { ComponentUtils } from '@kit.ArkUI'
1464
1465@Entry
1466@Component
1467struct TabsExample {
1468  @State currentIndex: number = 0
1469  @State animationDuration: number = 300
1470  @State indicatorLeftMargin: number = 0
1471  @State indicatorWidth: number = 0
1472  private tabsWidth: number = 0
1473  private componentUtils: ComponentUtils = this.getUIContext().getComponentUtils()
1474
1475  @Builder
1476  tabBuilder(index: number, name: string) {
1477    Column() {
1478      Text(name)
1479        .fontSize(16)
1480        .fontColor(this.currentIndex === index ? '#007DFF' : '#182431')
1481        .fontWeight(this.currentIndex === index ? 500 : 400)
1482        .id(index.toString())
1483        .onAreaChange((oldValue: Area,newValue: Area) => {
1484          if (this.currentIndex === index && (this.indicatorLeftMargin === 0 || this.indicatorWidth === 0)){
1485            if (newValue.position.x != undefined) {
1486              let positionX = Number.parseFloat(newValue.position.x.toString())
1487              this.indicatorLeftMargin = Number.isNaN(positionX) ? 0 : positionX
1488            }
1489            let width = Number.parseFloat(newValue.width.toString())
1490            this.indicatorWidth = Number.isNaN(width) ? 0 : width
1491          }
1492        })
1493    }.width('100%')
1494  }
1495
1496  build() {
1497    Stack({ alignContent: Alignment.TopStart }) {
1498      Tabs({ barPosition: BarPosition.Start }) {
1499        TabContent() {
1500          Column().width('100%').height('100%').backgroundColor('#00CB87')
1501        }.tabBar(this.tabBuilder(0, 'green'))
1502
1503        TabContent() {
1504          Column().width('100%').height('100%').backgroundColor('#007DFF')
1505        }.tabBar(this.tabBuilder(1, 'blue'))
1506
1507        TabContent() {
1508          Column().width('100%').height('100%').backgroundColor('#FFBF00')
1509        }.tabBar(this.tabBuilder(2, 'yellow'))
1510
1511        TabContent() {
1512          Column().width('100%').height('100%').backgroundColor('#E67C92')
1513        }.tabBar(this.tabBuilder(3, 'pink'))
1514      }
1515      .onAreaChange((oldValue: Area,newValue: Area)=> {
1516        let width = Number.parseFloat(newValue.width.toString())
1517        this.tabsWidth = Number.isNaN(width) ? 0 : width
1518      })
1519      .barWidth('100%')
1520      .barHeight(56)
1521      .width('100%')
1522      .height(296)
1523      .backgroundColor('#F1F3F5')
1524      .animationDuration(this.animationDuration)
1525      .onChange((index: number) => {
1526        this.currentIndex = index // Listen for index changes to switch the tab page content.
1527      })
1528      .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {
1529        // Triggered when the tab switching animation starts. The underline moves with the active tab, along with a width gradient.
1530        this.currentIndex = targetIndex
1531        let targetIndexInfo = this.getTextInfo(targetIndex)
1532        this.startAnimateTo(this.animationDuration, targetIndexInfo.left, targetIndexInfo.width)
1533      })
1534      .onAnimationEnd((index: number,event: TabsAnimationEvent) => {
1535        // Triggered when the tab switching animation ends. The underline animation stops.
1536        let currentIndicatorInfo = this.getCurrentIndicatorInfo(index,event)
1537        this.startAnimateTo(0,currentIndicatorInfo.left,currentIndicatorInfo.width)
1538      })
1539      .onGestureSwipe((index: number,event: TabsAnimationEvent) => {
1540        // Triggered on a frame-by-frame basis when the tab is switched by a swipe.
1541        let currentIndicatorInfo = this.getCurrentIndicatorInfo(index,event)
1542        this.currentIndex = currentIndicatorInfo.index
1543        this.indicatorLeftMargin = currentIndicatorInfo.left
1544        this.indicatorWidth = currentIndicatorInfo.width
1545      })
1546
1547      Column()
1548        .height(2)
1549        .width(this.indicatorWidth)
1550        .margin({ left: this.indicatorLeftMargin, top:48})
1551        .backgroundColor('#007DFF')
1552    }.width('100%')
1553  }
1554
1555  private getTextInfo(index: number): Record<string, number> {
1556    let rectangle = this.componentUtils.getRectangleById(index.toString())
1557    return { 'left': px2vp(rectangle.windowOffset.x), 'width': px2vp(rectangle.size.width) }
1558  }
1559
1560  private getCurrentIndicatorInfo(index: number, event: TabsAnimationEvent): Record<string, number> {
1561    let nextIndex = index
1562    if (index > 0 && event.currentOffset > 0) {
1563      nextIndex--
1564    } else if (index < 3 && event.currentOffset < 0) {
1565      nextIndex++
1566    }
1567    let indexInfo = this.getTextInfo(index)
1568    let nextIndexInfo = this.getTextInfo(nextIndex)
1569    let swipeRatio = Math.abs(event.currentOffset / this.tabsWidth)
1570    let currentIndex = swipeRatio > 0.5 ? nextIndex : index  // When the scroll distance exceeds half of the page, the tab bar switches to the next page.
1571    let currentLeft = indexInfo.left + (nextIndexInfo.left - indexInfo.left) * swipeRatio
1572    let currentWidth = indexInfo.width + (nextIndexInfo.width - indexInfo.width) * swipeRatio
1573    return { 'index': currentIndex, 'left': currentLeft, 'width': currentWidth }
1574  }
1575
1576  private startAnimateTo(duration: number, leftMargin: number, width: number) {
1577    animateTo({
1578      duration: duration, // Animation duration.
1579      curve: Curve.Linear, // Animation curve.
1580      iterations: 1, // Number of playback times.
1581      playMode: PlayMode.Normal // Animation playback mode.
1582      onFinish: () => {
1583        console.info('play end')
1584      }
1585    }, () => {
1586      this.indicatorLeftMargin = leftMargin
1587      this.indicatorWidth = width
1588    })
1589  }
1590}
1591```
1592
1593![tabs10](figures/tabs10.gif)
1594
1595### Example 10
1596
1597In this example, the **preloadItems** API is used to preload specified child nodes.
1598
1599```ts
1600// xxx.ets
1601import { BusinessError } from '@kit.BasicServicesKit'
1602
1603@Entry
1604@Component
1605struct TabsPreloadItems {
1606  @State currentIndex: number = 1
1607  private tabsController: TabsController = new TabsController()
1608
1609  build() {
1610    Column() {
1611      Tabs({ index: this.currentIndex, controller: this.tabsController }) {
1612        TabContent() {
1613          MyComponent({ color: '#00CB87' })
1614        }.tabBar(SubTabBarStyle.of('green'))
1615
1616        TabContent() {
1617          MyComponent({ color: '#007DFF' })
1618        }.tabBar(SubTabBarStyle.of('blue'))
1619
1620        TabContent() {
1621          MyComponent({ color: '#FFBF00' })
1622        }.tabBar(SubTabBarStyle.of('yellow'))
1623
1624        TabContent() {
1625          MyComponent({ color: '#E67C92' })
1626        }.tabBar(SubTabBarStyle.of('pink'))
1627      }
1628      .width(360)
1629      .height(296)
1630      .backgroundColor('#F1F3F5')
1631      .onChange((index: number) => {
1632        this.currentIndex = index
1633      })
1634
1635      Button('preload items: [0, 2, 3]')
1636        .margin(5)
1637        .onClick(() => {
1638          // Preload child nodes 0, 2, and 3 to improve the performance when users swipe or click to switch to these nodes.
1639          this.tabsController.preloadItems([0, 2, 3])
1640            .then(() => {
1641              console.info('preloadItems success.')
1642            })
1643            .catch((error: BusinessError) => {
1644              console.error('preloadItems failed, error code: ' + error.code + ', error message: ' + error.message)
1645            })
1646        })
1647    }
1648  }
1649}
1650
1651@Component
1652struct MyComponent {
1653  private color: string = ""
1654
1655  aboutToAppear(): void {
1656    console.info('aboutToAppear backgroundColor:' + this.color)
1657  }
1658
1659  aboutToDisappear(): void {
1660    console.info('aboutToDisappear backgroundColor:' + this.color)
1661  }
1662
1663  build() {
1664    Column()
1665      .width('100%')
1666      .height('100%')
1667      .backgroundColor(this.color)
1668  }
1669}
1670```
1671