tommybuonomo/dotsindicator

Dots indicators for Android ViewPager and Compose

Kotlin

3,604

240 commits

updated Oct 11, 2026

See the code

README

Pager Dots Indicator

Maven Central Android Arsenal awesome

Dots Indicator is an Android library for Jetpack Compose and classic XML views 🚀

Compose support is built into the same artifact and exposes four indicator types: Shift, Spring, Worm, and Balloon.

Don't forget to star the project if you like it! star == heart

Feel free to submit issues and enhancement requests!

Installation

Add Maven Central and the library dependency:

repositories {
    google()
    mavenCentral()
}

dependencies {
    implementation("com.tbuonomo:dotsindicator:5.1.2")
}

Jetpack Compose

DotsIndicator works directly with androidx.compose.foundation.pager.PagerState.

ShiftIndicatorType

ezgif com-crop (1)

DotsIndicator(
    dotCount = pageCount,
    type = ShiftIndicatorType(
        dotsGraphic = DotGraphic(color = MaterialTheme.colorScheme.primary)
    ),
    pagerState = pagerState
)

SpringIndicatorType

ezgif com-crop (2)

DotsIndicator(
    dotCount = pageCount,
    type = SpringIndicatorType(
        dotsGraphic = DotGraphic(
            size = 16.dp,
            borderWidth = 2.dp,
            borderColor = MaterialTheme.colorScheme.primary,
            color = Color.Transparent
        ),
        selectorDotGraphic = DotGraphic(
            size = 14.dp,
            color = MaterialTheme.colorScheme.primary
        )
    ),
    pagerState = pagerState
)

WormIndicatorType

ezgif com-crop (3)

DotsIndicator(
    dotCount = pageCount,
    type = WormIndicatorType(
        dotsGraphic = DotGraphic(
            size = 16.dp,
            borderWidth = 2.dp,
            borderColor = MaterialTheme.colorScheme.primary,
            color = Color.Transparent,
        ),
        wormDotGraphic = DotGraphic(
            size = 16.dp,
            color = MaterialTheme.colorScheme.primary,
        )
    ),
    pagerState = pagerState
)

BalloonIndicatorType

ezgif com-crop (4)

DotsIndicator(
    dotCount = pageCount,
    type = BalloonIndicatorType(
        dotsGraphic = DotGraphic(
            size = 8.dp,
            color = MaterialTheme.colorScheme.primary
        ),
        balloonSizeFactor = 2f
    ),
    dotSpacing = 20.dp,
    pagerState = pagerState
)

XML views

Classic Android views are still supported for ViewPager and ViewPager2 with attachTo(...).

DotsIndicator

ezgif com-crop 1 ezgif com-crop 3

XML layout

<com.tbuonomo.viewpagerdotsindicator.DotsIndicator
    android:id="@+id/dots_indicator"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:dotsColor="@color/material_white"
    app:dotsCornerRadius="8dp"
    app:dotsElevation="4dp"
    app:dotsSize="16dp"
    app:dotsSpacing="4dp"
    app:dotsWidthFactor="2.5"
    app:selectedDotColor="@color/md_blue_200"
    app:progressMode="true"
    app:dotsClickable="true" />

Custom attributes

AttributeDescription
dotsColorColor of the dots
selectedDotColorColor of the selected dot (defaults to dotsColor)
progressModeColors every previous dot with the selected dot color
dotsSizeSize of the dots (default: 16dp)
dotsSpacingSpace between dots (default: 8dp)
dotsWidthFactorWidth multiplier for the selected dot (default: 2.5)
dotsCornerRadiusDot corner radius (defaults to half of dotsSize)
dotsElevationElevation applied to the dots
dotsClickableEnables changing page when a dot is tapped (default: true)

Kotlin

val dotsIndicator = findViewById<DotsIndicator>(R.id.dots_indicator)
val viewPager = findViewById<ViewPager2>(R.id.view_pager)
viewPager.adapter = ViewPagerAdapter()
dotsIndicator.attachTo(viewPager)

Per-dot colors (programmatic)

Give each dot its own selected and/or unselected color. Indices beyond the array length fall back to the global selectedDotColor / dotsColor. Empty arrays use the global colors, and assigning null restores them. Assigning an array refreshes existing dots; after changing an array element, reassign the array to repaint. In progressMode, passed dots retain their own selected color. This API applies only to the classic View-based DotsIndicator.

// Each dot lights up in a different accent color when selected
dotsIndicator.selectedDotColors = intArrayOf(
    Color.parseColor("#FF5252"), // dot 0
    Color.parseColor("#FF6D00"), // dot 1
    Color.parseColor("#FFD600"), // dot 2
    Color.parseColor("#69F0AE"), // dot 3
    Color.parseColor("#40C4FF"), // dot 4
)

// Optional: custom unselected colors per dot
dotsIndicator.dotColors = intArrayOf(
    Color.parseColor("#FFCDD2"),
    Color.parseColor("#FFE0B2"),
    Color.parseColor("#FFF9C4"),
    Color.parseColor("#CCFF90"),
    Color.parseColor("#B3E5FC"),
)

Java-friendly setters are also available:

dotsIndicator.setSelectedDotColors(new int[]{Color.RED, Color.GREEN, Color.BLUE});
dotsIndicator.setDotColors(new int[]{Color.LTGRAY, Color.LTGRAY, Color.LTGRAY});

SpringDotsIndicator

ezgif com-crop 4 ezgif com-crop 5

XML layout

<com.tbuonomo.viewpagerdotsindicator.SpringDotsIndicator
    android:id="@+id/spring_dots_indicator"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:dampingRatio="0.5"
    app:dotsColor="@color/material_white"
    app:dotsStrokeColor="@color/material_yellow"
    app:dotsCornerRadius="2dp"
    app:dotsSize="16dp"
    app:dotsSpacing="6dp"
    app:dotsStrokeWidth="2dp"
    app:stiffness="300"
    app:dotsClickable="true" />

Custom attributes

AttributeDescription
dotsColorColor of the moving indicator dot
dotsStrokeColorColor of the stroke dots (defaults to the indicator color)
dotsSizeSize of the dots (default: 16dp)
dotsSpacingSpace between dots (default: 4dp)
dotsCornerRadiusDot corner radius (defaults to half of dotsSize)
dotsStrokeWidthStroke width for the background dots (default: 2dp)
dampingRatioSpring force damping ratio (default: 0.5)
stiffnessSpring force stiffness (default: 300)
dotsClickableEnables changing page when a dot is tapped (default: true)

Kotlin

val springDotsIndicator = findViewById<SpringDotsIndicator>(R.id.spring_dots_indicator)
val viewPager = findViewById<ViewPager2>(R.id.view_pager)
viewPager.adapter = ViewPagerAdapter()
springDotsIndicator.attachTo(viewPager)

WormDotsIndicator

ezgif com-crop 6 ezgif com-crop 7

XML layout

<com.tbuonomo.viewpagerdotsindicator.WormDotsIndicator
    android:id="@+id/worm_dots_indicator"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:dotsColor="@color/material_blueA200"
    app:dotsStrokeColor="@color/material_yellow"
    app:dotsCornerRadius="8dp"
    app:dotsSize="16dp"
    app:dotsSpacing="4dp"
    app:dotsStrokeWidth="2dp"
    app:dotsClickable="true" />

Custom attributes

AttributeDescription
dotsColorColor of the moving indicator dot
dotsStrokeColorColor of the stroke dots (defaults to the indicator color)
dotsSizeSize of the dots (default: 16dp)
dotsSpacingSpace between dots (default: 4dp)
dotsCornerRadiusDot corner radius (defaults to half of dotsSize)
dotsStrokeWidthStroke width for the background dots (default: 2dp)
dotsClickableEnables changing page when a dot is tapped (default: true)

Kotlin

val wormDotsIndicator = findViewById<WormDotsIndicator>(R.id.worm_dots_indicator)
val viewPager = findViewById<ViewPager2>(R.id.view_pager)
viewPager.adapter = ViewPagerAdapter()
wormDotsIndicator.attachTo(viewPager)

ViewPager and ViewPager2

attachTo(...) accepts both androidx.viewpager.widget.ViewPager and androidx.viewpager2.widget.ViewPager2:

dotsIndicator.attachTo(viewPager)
dotsIndicator.attachTo(viewPager2)

Help Maintenance

If you could help me to continue maintain this repo, buying me a cup of coffee will make my life really happy and get much energy out of it.

Buy Me A Coffee

Changelog

5.1.2

  • Restore Java 17 compatibility for the published library while retaining JDK 21 for builds (#236)
  • Verify release AAR bytecode and compile a consumer with JDK 17 in CI

5.1.1

  • Fix RTL crashes and direction issues across classic and Compose indicators, including Worm and Spring behavior in RTL layouts
  • Fix indicator state regressions: RTL rotation now resets correctly when returning to LTR, and fast-scroll dot colors no longer get stuck out of sync
  • Add Compose regression coverage for Shift, Balloon, Spring, and Worm indicators with computation, behavior, and screenshot tests in both LTR and RTL
  • Fix #207, #209, and #211

5.1.0

  • Fix import issues
  • Upgrade AGP versions
  • Migrate repo to Maven Central Repository

5.0

  • Add Jetpack Compose support with 4 types: ShiftIndicatorType, SpringIndicatorType, WormIndicatorType, BalloonIndicatorType

4.3

4.2

Fix #115 The library is now on MavenCentral. The library name moves from com.tbuonomo.andrui:viewpagerdotsindicator to com.tbuonomo:dotsindicator

4.1.2

Fix #55 and #56

4.1.1

Fix crash

4.1

  • Support RTL (fix #32 and #51)

4.0

  • Support of ViewPager2 (fix #40)
  • Convert all the project to Kotlin
  • Migration to AndroidX
  • Fix #37: findViewById, causing missing adapter error

3.0.3

  • Fix #20: Dots indicator initialises with the wrong number of dots initially

3.0.2

  • Add attribute selectedDotColor and progressMode to DotsIndicator
  • Fix RTL issues and improve DotsIndicator globally

2.1.0

  • Add attribute dotsStrokeColor to SpringDotsIndicator and WormDotsIndicator

License

Copyright 2016 Tommy Buonomo

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
android
android-library
compose
dots
dots-indicator
indicator
kotlin
kotlin-android
material-design
viewpager
viewpager-indicator

tommybuonomo/dotsindicator

Dots indicators for Android ViewPager and Compose

Kotlin

3,604

240 commits

updated Oct 11, 2026

See the code

README

Pager Dots Indicator

Maven Central Android Arsenal awesome

Dots Indicator is an Android library for Jetpack Compose and classic XML views 🚀

Compose support is built into the same artifact and exposes four indicator types: Shift, Spring, Worm, and Balloon.

Don't forget to star the project if you like it! star == heart

Feel free to submit issues and enhancement requests!

Installation

Add Maven Central and the library dependency:

repositories {
    google()
    mavenCentral()
}

dependencies {
    implementation("com.tbuonomo:dotsindicator:5.1.2")
}

Jetpack Compose

DotsIndicator works directly with androidx.compose.foundation.pager.PagerState.

ShiftIndicatorType

ezgif com-crop (1)

DotsIndicator(
    dotCount = pageCount,
    type = ShiftIndicatorType(
        dotsGraphic = DotGraphic(color = MaterialTheme.colorScheme.primary)
    ),
    pagerState = pagerState
)

SpringIndicatorType

ezgif com-crop (2)

DotsIndicator(
    dotCount = pageCount,
    type = SpringIndicatorType(
        dotsGraphic = DotGraphic(
            size = 16.dp,
            borderWidth = 2.dp,
            borderColor = MaterialTheme.colorScheme.primary,
            color = Color.Transparent
        ),
        selectorDotGraphic = DotGraphic(
            size = 14.dp,
            color = MaterialTheme.colorScheme.primary
        )
    ),
    pagerState = pagerState
)

WormIndicatorType

ezgif com-crop (3)

DotsIndicator(
    dotCount = pageCount,
    type = WormIndicatorType(
        dotsGraphic = DotGraphic(
            size = 16.dp,
            borderWidth = 2.dp,
            borderColor = MaterialTheme.colorScheme.primary,
            color = Color.Transparent,
        ),
        wormDotGraphic = DotGraphic(
            size = 16.dp,
            color = MaterialTheme.colorScheme.primary,
        )
    ),
    pagerState = pagerState
)

BalloonIndicatorType

ezgif com-crop (4)

DotsIndicator(
    dotCount = pageCount,
    type = BalloonIndicatorType(
        dotsGraphic = DotGraphic(
            size = 8.dp,
            color = MaterialTheme.colorScheme.primary
        ),
        balloonSizeFactor = 2f
    ),
    dotSpacing = 20.dp,
    pagerState = pagerState
)

XML views

Classic Android views are still supported for ViewPager and ViewPager2 with attachTo(...).

DotsIndicator

ezgif com-crop 1 ezgif com-crop 3

XML layout

<com.tbuonomo.viewpagerdotsindicator.DotsIndicator
    android:id="@+id/dots_indicator"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:dotsColor="@color/material_white"
    app:dotsCornerRadius="8dp"
    app:dotsElevation="4dp"
    app:dotsSize="16dp"
    app:dotsSpacing="4dp"
    app:dotsWidthFactor="2.5"
    app:selectedDotColor="@color/md_blue_200"
    app:progressMode="true"
    app:dotsClickable="true" />

Custom attributes

AttributeDescription
dotsColorColor of the dots
selectedDotColorColor of the selected dot (defaults to dotsColor)
progressModeColors every previous dot with the selected dot color
dotsSizeSize of the dots (default: 16dp)
dotsSpacingSpace between dots (default: 8dp)
dotsWidthFactorWidth multiplier for the selected dot (default: 2.5)
dotsCornerRadiusDot corner radius (defaults to half of dotsSize)
dotsElevationElevation applied to the dots
dotsClickableEnables changing page when a dot is tapped (default: true)

Kotlin

val dotsIndicator = findViewById<DotsIndicator>(R.id.dots_indicator)
val viewPager = findViewById<ViewPager2>(R.id.view_pager)
viewPager.adapter = ViewPagerAdapter()
dotsIndicator.attachTo(viewPager)

Per-dot colors (programmatic)

Give each dot its own selected and/or unselected color. Indices beyond the array length fall back to the global selectedDotColor / dotsColor. Empty arrays use the global colors, and assigning null restores them. Assigning an array refreshes existing dots; after changing an array element, reassign the array to repaint. In progressMode, passed dots retain their own selected color. This API applies only to the classic View-based DotsIndicator.

// Each dot lights up in a different accent color when selected
dotsIndicator.selectedDotColors = intArrayOf(
    Color.parseColor("#FF5252"), // dot 0
    Color.parseColor("#FF6D00"), // dot 1
    Color.parseColor("#FFD600"), // dot 2
    Color.parseColor("#69F0AE"), // dot 3
    Color.parseColor("#40C4FF"), // dot 4
)

// Optional: custom unselected colors per dot
dotsIndicator.dotColors = intArrayOf(
    Color.parseColor("#FFCDD2"),
    Color.parseColor("#FFE0B2"),
    Color.parseColor("#FFF9C4"),
    Color.parseColor("#CCFF90"),
    Color.parseColor("#B3E5FC"),
)

Java-friendly setters are also available:

dotsIndicator.setSelectedDotColors(new int[]{Color.RED, Color.GREEN, Color.BLUE});
dotsIndicator.setDotColors(new int[]{Color.LTGRAY, Color.LTGRAY, Color.LTGRAY});

SpringDotsIndicator

ezgif com-crop 4 ezgif com-crop 5

XML layout

<com.tbuonomo.viewpagerdotsindicator.SpringDotsIndicator
    android:id="@+id/spring_dots_indicator"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:dampingRatio="0.5"
    app:dotsColor="@color/material_white"
    app:dotsStrokeColor="@color/material_yellow"
    app:dotsCornerRadius="2dp"
    app:dotsSize="16dp"
    app:dotsSpacing="6dp"
    app:dotsStrokeWidth="2dp"
    app:stiffness="300"
    app:dotsClickable="true" />

Custom attributes

AttributeDescription
dotsColorColor of the moving indicator dot
dotsStrokeColorColor of the stroke dots (defaults to the indicator color)
dotsSizeSize of the dots (default: 16dp)
dotsSpacingSpace between dots (default: 4dp)
dotsCornerRadiusDot corner radius (defaults to half of dotsSize)
dotsStrokeWidthStroke width for the background dots (default: 2dp)
dampingRatioSpring force damping ratio (default: 0.5)
stiffnessSpring force stiffness (default: 300)
dotsClickableEnables changing page when a dot is tapped (default: true)

Kotlin

val springDotsIndicator = findViewById<SpringDotsIndicator>(R.id.spring_dots_indicator)
val viewPager = findViewById<ViewPager2>(R.id.view_pager)
viewPager.adapter = ViewPagerAdapter()
springDotsIndicator.attachTo(viewPager)

WormDotsIndicator

ezgif com-crop 6 ezgif com-crop 7

XML layout

<com.tbuonomo.viewpagerdotsindicator.WormDotsIndicator
    android:id="@+id/worm_dots_indicator"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:dotsColor="@color/material_blueA200"
    app:dotsStrokeColor="@color/material_yellow"
    app:dotsCornerRadius="8dp"
    app:dotsSize="16dp"
    app:dotsSpacing="4dp"
    app:dotsStrokeWidth="2dp"
    app:dotsClickable="true" />

Custom attributes

AttributeDescription
dotsColorColor of the moving indicator dot
dotsStrokeColorColor of the stroke dots (defaults to the indicator color)
dotsSizeSize of the dots (default: 16dp)
dotsSpacingSpace between dots (default: 4dp)
dotsCornerRadiusDot corner radius (defaults to half of dotsSize)
dotsStrokeWidthStroke width for the background dots (default: 2dp)
dotsClickableEnables changing page when a dot is tapped (default: true)

Kotlin

val wormDotsIndicator = findViewById<WormDotsIndicator>(R.id.worm_dots_indicator)
val viewPager = findViewById<ViewPager2>(R.id.view_pager)
viewPager.adapter = ViewPagerAdapter()
wormDotsIndicator.attachTo(viewPager)

ViewPager and ViewPager2

attachTo(...) accepts both androidx.viewpager.widget.ViewPager and androidx.viewpager2.widget.ViewPager2:

dotsIndicator.attachTo(viewPager)
dotsIndicator.attachTo(viewPager2)

Help Maintenance

If you could help me to continue maintain this repo, buying me a cup of coffee will make my life really happy and get much energy out of it.

Buy Me A Coffee

Changelog

5.1.2

  • Restore Java 17 compatibility for the published library while retaining JDK 21 for builds (#236)
  • Verify release AAR bytecode and compile a consumer with JDK 17 in CI

5.1.1

  • Fix RTL crashes and direction issues across classic and Compose indicators, including Worm and Spring behavior in RTL layouts
  • Fix indicator state regressions: RTL rotation now resets correctly when returning to LTR, and fast-scroll dot colors no longer get stuck out of sync
  • Add Compose regression coverage for Shift, Balloon, Spring, and Worm indicators with computation, behavior, and screenshot tests in both LTR and RTL
  • Fix #207, #209, and #211

5.1.0

  • Fix import issues
  • Upgrade AGP versions
  • Migrate repo to Maven Central Repository

5.0

  • Add Jetpack Compose support with 4 types: ShiftIndicatorType, SpringIndicatorType, WormIndicatorType, BalloonIndicatorType

4.3

4.2

Fix #115 The library is now on MavenCentral. The library name moves from com.tbuonomo.andrui:viewpagerdotsindicator to com.tbuonomo:dotsindicator

4.1.2

Fix #55 and #56

4.1.1

Fix crash

4.1

  • Support RTL (fix #32 and #51)

4.0

  • Support of ViewPager2 (fix #40)
  • Convert all the project to Kotlin
  • Migration to AndroidX
  • Fix #37: findViewById, causing missing adapter error

3.0.3

  • Fix #20: Dots indicator initialises with the wrong number of dots initially

3.0.2

  • Add attribute selectedDotColor and progressMode to DotsIndicator
  • Fix RTL issues and improve DotsIndicator globally

2.1.0

  • Add attribute dotsStrokeColor to SpringDotsIndicator and WormDotsIndicator

License

Copyright 2016 Tommy Buonomo

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
android
android-library
compose
dots
dots-indicator
indicator
kotlin
kotlin-android
material-design
viewpager
viewpager-indicator

Project health

Overall

3.7/10

Maintained

5/10

Code review

5/10
OpenSSF Scorecard

Latest release

5.1.2 · Oct 5, 2026

Releases this year

2

Significant stargazers

Zac Sweers

2,628 followers · starred Jun 2018

Fankes yooni

886 followers · starred Jan 2024

Shaishav Gandhi

205 followers · starred Jun 2018

Yogesh Ojha

1,162 followers · starred Mar 2019