---
title: "Leaderboards"
canonical_url: "https://apidocs.sportradar.com/resources/virtual-stadium/docs/sdk/centralHub/leaderboard"
markdown_url: "https://apidocs.sportradar.com/resources/virtual-stadium/docs/sdk/centralHub/leaderboard.md"
last_updated: "2026-04-16T10:19:11Z"
---

# Leaderboards

Browse Central Hub leaderboard rankings, track the signed-in user's rank, and paginate through additional results.

## Loading a Leaderboard

Use `CentralHubLeaderboardProvider` to load rankings for a specific category and timeframe.

The provider state includes:

- loaded `entries`,
- the current user's `userRank`,
- `loadingStatus` for the initial request,
- `previousPageLoadingStatus` for pagination, and
- paging metadata such as `allDataLoaded` and `currentPage`.

**Android**

```kotlin
class LeaderboardViewModel : ViewModel(), KoinComponent {
    private val leaderboardProvider: CentralHubLeaderboardProvider = get()
    val state = leaderboardProvider.state

    fun loadFollowersWeeklyLeaderboard() {
        viewModelScope.launch {
            leaderboardProvider.loadLeaderboard(
                category = LeaderboardCategoryType.FOLLOWERS,
                timeframe = LeaderboardTimeframeType.WEEKLY,
            )
        }
    }

    override fun onCleared() {
        super.onCleared()
        leaderboardProvider.clear()
    }
}
```

```kotlin
@Composable
fun LeaderboardScreen(
    vm: LeaderboardViewModel = viewModel(),
) {
    val state by vm.state.collectAsStateWithLifecycle()

    LaunchedEffect(Unit) {
        vm.loadFollowersWeeklyLeaderboard()
    }

    when (state.loadingStatus) {
        LoadingStatus.LOADING,
        LoadingStatus.INITIAL,
        -> CircularProgressIndicator()

        LoadingStatus.ERROR -> Text("Failed to load leaderboard")
        LoadingStatus.IDLE -> LazyColumn {
            items(state.entries) { entry ->
                Text(text = "#${entry.rank} ${entry.user.displayName} · ${entry.count}")
            }
        }
    }
}
```

**iOS**

```swift
import Foundation
import VirtualStadiumDataSDK

class LeaderboardViewModel: ObservableObject {
    private let leaderboardProvider = KoinHelper().getLeaderboardProvider()
    private var stateDisposable: (any Kotlinx_coroutines_coreDisposableHandle)?

    @Published var state: LeaderboardState?

    init() {
        stateDisposable = leaderboardProvider.state.subscribe { [weak self] (state: LeaderboardState?) in
            guard let state else { return }
            DispatchQueue.main.async {
                self?.state = state
            }
        }
    }

    func loadFollowersWeeklyLeaderboard() {
        Task {
            try? await leaderboardProvider.loadLeaderboard(
                category: .followers,
                timeframe: .weekly,
                pageSize: 20
            )
        }
    }

    deinit {
        stateDisposable?.dispose()
        leaderboardProvider.clear()
    }
}
```

```swift
import SwiftUI

struct LeaderboardScreen: View {
    @StateObject private var vm = LeaderboardViewModel()

    var body: some View {
        Group {
            if let state = vm.state {
                switch state.loadingStatus {
                case .initial, .loading:
                    ProgressView()
                case .error:
                    Text("Failed to load leaderboard")
                default:
                    List(state.entries, id: \.rank) { entry in
                        Text("#\(entry.rank) \(entry.user.displayName) · \(entry.count)")
                    }
                }
            } else {
                ProgressView()
            }
        }
        .task {
            vm.loadFollowersWeeklyLeaderboard()
        }
    }
}
```

## Loading More Results

Call `loadMoreLeaderboard()` to fetch the next page for the most recently loaded category and timeframe.

Use these fields to drive your pagination UI:

- `previousPageLoadingStatus`
- `allDataLoaded`
- `currentPage`

> **Tip**
>
> Disable your load-more UI while `previousPageLoadingStatus` is `LOADING` or when `allDataLoaded` is `true`.

**Android**

```kotlin
fun loadNextPage() {
    viewModelScope.launch {
        leaderboardProvider.loadMoreLeaderboard()
    }
}
```

```kotlin
val canLoadMore =
    state.previousPageLoadingStatus != LoadingStatus.LOADING &&
        !state.allDataLoaded

Button(
    onClick = vm::loadNextPage,
    enabled = canLoadMore,
) {
    Text(
        when {
            state.previousPageLoadingStatus == LoadingStatus.LOADING -> "Loading..."
            state.allDataLoaded -> "All data loaded"
            else -> "Load more"
        }
    )
}
```

**iOS**

```swift
func loadNextPage() {
    guard let state, state.previousPageLoadingStatus != .loading, !state.allDataLoaded else {
        return
    }

    Task {
        try? await leaderboardProvider.loadMoreLeaderboard(pageSize: 20)
    }
}
```

```swift
Button {
    vm.loadNextPage()
} label: {
    Text(vm.state?.allDataLoaded == true ? "All data loaded" : "Load more")
}
.disabled(
    vm.state?.previousPageLoadingStatus == .loading ||
    vm.state?.allDataLoaded == true
)
```

## State Behavior

During the first request:

- `loadingStatus` moves from `INITIAL` to `LOADING`.
- `entries` are replaced with the new first page.
- `currentPage` resets to `0`.
- `userRank` is populated when that request succeeds.

During pagination:

- `previousPageLoadingStatus` moves to `LOADING`.
- new entries are appended to the existing list.
- `currentPage` advances after success.
- `allDataLoaded` becomes `true` when there are no more items to fetch.

> **Info**
>
> If you keep the provider alive across multiple screens, call `clear()` when you want a fresh leaderboard session.

## Choosing Categories and Timeframes

### Categories

- `FOLLOWERS`
- `POPULAR_BETS`
- `BET_COMMENTS`

### Timeframes

- `DAILY`
- `WEEKLY`
- `MONTHLY`
- `ALL_TIME`

## Data Models

### LeaderboardState

```kotlin
data class LeaderboardState(
    val entries: List<LeaderboardEntry>,
    val userRank: UserLeaderboardRank?,
    val loadingStatus: LoadingStatus,
    val previousPageLoadingStatus: LoadingStatus,
    val allDataLoaded: Boolean,
    val currentPage: Int,
)
```

### LeaderboardEntry

```kotlin
data class LeaderboardEntry(
    val rank: Int,
    val user: User,
    val count: Int,
)
```

### UserLeaderboardRank

```kotlin
data class UserLeaderboardRank(
    val rank: Int,
    val score: Int,
    val totalUsers: Int,
)
```

## Related Topics

- [Central Hub Overview](https://apidocs.sportradar.com/resources/virtual-stadium/docs/sdk/centralHub/overview.md) - Main Central Hub feature documentation.
- [Onboarding](https://apidocs.sportradar.com/resources/virtual-stadium/docs/sdk/centralHub/onboarding.md) - Show onboarding only to first-time users.
- [User Profiles](https://apidocs.sportradar.com/resources/virtual-stadium/docs/sdk/centralHub/profiles.md) - Load user profile data for ranked users.
- [Social Features](https://apidocs.sportradar.com/resources/virtual-stadium/docs/sdk/centralHub/social.md) - Work with follow relationships and social metrics.
