> ## Documentation Index
> Fetch the complete documentation index at: https://algolia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Filter Numeric Range

> Lets users refine results between two numeric values using a slider.

```kotlin Signature theme={"system"}
FilterRangeConnector(
    filterState: FilterState,
    attribute: String,
    bounds: ClosedRange<T>?,
    range: ClosedRange<T>?
)
```

<Card title="Explore example code" icon="github" href="https://github.com/algolia/instantsearch-android/tree/master/examples/android">
  Browse the Filter Numeric Range example code on GitHub.
</Card>

## About this widget

Filter Numeric Range is a filtering view made to filter between two numeric values.
The most common interface for this is a slider.

The [Android View](https://github.com/algolia/instantsearch-android/tree/master/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/compose/filter/range) and [Compose UI](https://github.com/algolia/instantsearch-android/tree/master/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/compose/filter/range) code samples (part of the [Android widget showcase repository on GitHub](https://github.com/algolia/instantsearch-android/tree/master/examples)) illustrate the use of sliders.

## Examples

```kotlin Kotlin icon=code theme={"system"}
class MyActivity : AppCompatActivity() {
    val searcher = HitsSearcher(
        applicationID = "YourApplicationID",
        apiKey = "YourSearchOnlyAPIKey",
        indexName = "YourIndexName"
    )
    val price = "price"
    val groupID = FilterGroupID(price)
    val primaryBounds = 0..15
    val initialRange = 0..15
    val filters = filters {
        group(groupID) {
            range(price, initialRange)
        }
    }
    val filterState = FilterState(filters)
    val range = FilterRangeConnector(
        filterState = filterState,
        attribute = price,
        range = initialRange,
        bounds = primaryBounds
    )
    val connection = ConnectionHandler(range, searcher.connectFilterState(filterState))

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val view: NumberRangeView<Int> = MyNumberRangeView(textLabel) // your `NumberRangeView<T>` implementation
        connection += range.connectView(view)
        searcher.searchAsync()
    }

    override fun onDestroy() {
        super.onDestroy()
        connection.disconnect()
        searcher.cancel()
    }
}
```

### Low-level API

If you want to fully control the Filter Numeric Range components and connect them manually, use the following components:

* `Searcher`. The [`Searcher`](/doc/api-reference/widgets/instantsearch/android) that handles your searches.
* `FilterState`. The current state of the filters.
* `FilterRangeViewModel`. The logic applied to the numeric ranges.
* `NumberRangeView`. The view that renders the numeric range filter.

```kotlin Kotlin icon=code theme={"system"}
class MyActivity : AppCompatActivity() {

    val searcher = HitsSearcher(
        applicationID = "YourApplicationID",
        apiKey = "YourSearchOnlyAPIKey",
        indexName = "YourIndexName"
    )
    val filterState = FilterState()
    val attribute = "price"
    val viewModel = FilterRangeViewModel<Int>()
    val connection = ConnectionHandler()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val view: NumberRangeView<Int> = MyNumberRangeView(textLabel) // your `NumberRangeView<T>` implementation
        connection += searcher.connectFilterState(filterState)
        connection += viewModel.connectFilterState(filterState, attribute)
        connection += viewModel.connectView(view)

        searcher.searchAsync()
    }

    override fun onDestroy() {
        super.onDestroy()
        connection.disconnect()
        searcher.cancel()
    }
}
```

### Compose UI

InstantSearch provides the `NumberRangeState` as a state model,
which is an implementation of the `NumberRangeView` interface.
You need to connect `NumberRangeState` to the `FilterRangeConnector` or `FilterRangeViewModel` like any other `NumberRangeView` implementation.

```kotlin Kotlin icon=code theme={"system"}
class MyActivity : AppCompatActivity() {
    val searcher = HitsSearcher(
        applicationID = "YourApplicationID",
        apiKey = "YourSearchOnlyAPIKey",
        indexName = "YourIndexName"
    )
    val price = "price"
    val groupID = FilterGroupID(price)
    val primaryBounds = 0..15
    val initialRange = 0..15
    val filters = filters {
        group(groupID) {
            range(price, initialRange)
        }
    }
    val filterState = FilterState(filters)
    val sliderState = NumberRangeState<Int>()
    val range = FilterRangeConnector(
        filterState = filterState,
        attribute = price,
        range = initialRange,
        bounds = primaryBounds
    )
    val connections = ConnectionHandler(range)

    init {
        connections += searcher.connectFilterState(filterState)
        connections += range.connectView(sliderState)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyRangeSlider(sliderState) // your own UI composable to display range and bounds as `Range<T>`
        }
        searcher.searchAsync()
    }

    fun Range<Int>.toClosedFloatRange(): ClosedFloatingPointRange<Float> {
        return min.toFloat()..max.toFloat()
    }

    override fun onDestroy() {
        super.onDestroy()
        connections.disconnect()
        searcher.cancel()
    }
}
```

## Parameters

<ParamField body="filterState" type="FilterState" required>
  The [`FilterState`](/doc/api-reference/widgets/filter-state/android) that will hold your filters.
</ParamField>

<ParamField body="attribute" type="String" required>
  The attribute to filter.
</ParamField>

<ParamField body="bounds" type="ClosedRange<T>?">
  The limits of the acceptable range within which values are coerced.
</ParamField>

<ParamField body="range" type="ClosedRange<T>?">
  The range of values within the bounds.
</ParamField>

## View

<ParamField body="view" type="NumberRangeView" required>
  The view that renders the numeric range filter.

  ```kotlin Kotlin icon=code theme={"system"}
  val view = MyNumberRangeView(textLabel)
  range.connectView(view)

  // Example of `NumberRangeView<T>` implementation
  class MyNumberRangeView(val view: TextView) : NumberRangeView<Int> {
      override var onRangeChanged: Callback<Range<Int>>? = null
      private var bounds: Range<Int>? = null

      override fun setBounds(bounds: Range<Int>?) {
          this.bounds = bounds
          view.text = bounds?.let {
              "Bounds: ${it.min} to ${it.max}"
          } ?: "No bounds"
      }

      override fun setRange(range: Range<Int>?) = Unit
  }
  ```
</ParamField>
