Flutter - Using ListWheelScrollView Widget Examples

This tutorial is about how to use ListWheelScrollView widget in Flutter.

Flutter's ListWheelScrollView is a widget that puts its children in a scrollable wheel. That results in a 3D effect as if the children are rotating in a wheel. The current selected item is the one in the center of the wheel. Below I will show you how to create a ListWheelScrollView including how to customize the look and how to handle when the selected item changes.

Creating ListWheelScrollView

The default constructor ListWheelScrollView() can be used to create a new ListWheelScrollView. There are two required parameters: children (List<Widget>) and itemExtent (double). The children which are the list item are passed to a delegate and lazily built during layout. The itemExtent property is used to set the size of each item in the main axis.

For example, there is a collection of ListTile that will be set as children.

  List<Widget> items = [
    ListTile(
      leading: Icon(Icons.local_activity, size: 50),
      title: Text('Activity'),
      subtitle: Text('Description here'),
    ),
    ListTile(
      leading: Icon(Icons.local_airport, size: 50),
      title: Text('Airport'),
      subtitle: Text('Description here'),
    ),
    ListTile(
      leading: Icon(Icons.local_atm, size: 50),
      title: Text('ATM'),
      subtitle: Text('Description here'),
    ),
    ListTile(
      leading: Icon(Icons.local_bar, size: 50),
      title: Text('Bar'),
      subtitle: Text('Description here'),
    ),
    ListTile(
      leading: Icon(Icons.local_cafe, size: 50),
      title: Text('Cafe'),
      subtitle: Text('Description here'),
    ),
    ListTile(
      leading: Icon(Icons.local_car_wash, size: 50),
      title: Text('Car Wash'),
      subtitle: Text('Description here'),
    ),
    ListTile(
      leading: Icon(Icons.local_convenience_store, size: 50),
      title: Text('Heart Shaker'),
      subtitle: Text('Description here'),
    ),
    ListTile(
      leading: Icon(Icons.local_dining, size: 50),
      title: Text('Dining'),
      subtitle: Text('Description here'),
    ),
    ListTile(
      leading: Icon(Icons.local_drink, size: 50),
      title: Text('Drink'),
      subtitle: Text('Description here'),
    ),
    ListTile(
      leading: Icon(Icons.local_florist, size: 50),
      title: Text('Florist'),
      subtitle: Text('Description here'),
    ),
    ListTile(
      leading: Icon(Icons.local_gas_station, size: 50),
      title: Text('Gas Station'),
      subtitle: Text('Description here'),
    ),
    ListTile(
      leading: Icon(Icons.local_grocery_store, size: 50),
      title: Text('Grocery Store'),
      subtitle: Text('Description here'),
    ),
  ];

The basic usage is very simple. You only need to pass the required parameters to the constructor.

  ListWheelScrollView(
    itemExtent: 75,
    children: items,
  )

Output:

Flutter - ListWheelScrollView - Basic

 

There is another way to create a ListWheelScrollView by using .useDelegate() named constructor. Instead of passing children, you need to pass a ListWheelChildDelegate to build the children.

Assuming the data is stored on a list of object named items, here's the example

  ListWheelScrollView.useDelegate(
    itemExtent: 75,
    childDelegate: ListWheelChildBuilderDelegate(
      builder: (BuildContext context, int index) {
        if (index < 0 || index > 10) {
          return null;
        }
        return ListTile(
          leading: Icon(items[index].icon, size: 50),
          title: items[index].text,
          subtitle: Text('Description here'),
        );
      }
    ),
  )

Using Magnifier

You can have magnifier for the center item by setting useMagnifier property to true. To set the zoom-in rate, use magnification property which defaults to 1.0. If the value is greater than 1.0, it will look bigger. Setting the value to be lower than 1.0 makes the magnified item looks smaller.

  ListWheelScrollView(
    itemExtent: 75,
    children: items,
    useMagnifier: true,
    magnification: 1.5,
  )

Output:

Flutter - ListWheelScrollView - Magnifier

 

Setting Physics

The physics property which is of type ScrollPhysics is used to define how the scroll view should respond to user input. By default the scroll can stop anywhere within the scroll extent. You can set it to a FixedExtentScrollPhysics, which is a snapping physics that makes the scroll always resulting in exactly one item is located right in the center area. To achieve that condition, after the user finishes a scroll, it automatically scrolls up or down to adjust the position.

  ListWheelScrollView(
    itemExtent: 75,
    children: items,
    physics: FixedExtentScrollPhysics(),
  )

Output:

Flutter-ListWheelScrollView-FixedExtentScrollPhysics

 

Setting Diameter Ratio

The diameter of the cylinder can be set using diameterRatio property value. The default value is 2. The below examples show you how decreasing and increasing the value affect the output.

  ListWheelScrollView(
    itemExtent: 75,
    children: items,
    diameterRatio: 1,
  )

Output:

Flutter - ListWheelScrollView - diameterRatio 1

 

  ListWheelScrollView(
    itemExtent: 75,
    children: items,
    diameterRatio: 4,
  )

Output:

Flutter - ListWheelScrollView - diameterRatio 4

 

Setting Perspective

To set the perspective of the cylindrical projection, you need to set the perspective property. The default value is 0.003. The valid value must be between 0 (exclusive) and 0.01 (inclusive). Giving invalid value will result in the following error.

  The following assertion was thrown building ListWheelApp(dirty, state: _ListWheelAppState#a8bf3):
  A perspective too high will be clipped in the z-axis and therefore not renderable. Value must be
  between 0 and 0.01.

Setting the value with a very small number makes the list looks almost flat, while setting the value with the maximum allowed value makes the list looks more rounded.

  ListWheelScrollView(
    itemExtent: 75,
    children: items,
    perspective: 0.0000000001,
)

Output:

Flutter - ListWheelScrollView -  perspective 0.0000000001

 

  ListWheelScrollView(
    itemExtent: 75,
    children: items,
    perspective: 0.01,
  )

Output:

Flutter - ListWheelScrollView -  perspective 0.01

 

Setting Squeeze

The squeeze property is used to set the compactness of each item. The default value is 1.0. Decreasing the value makes it less compact, while increasing the value makes it more compact.

  ListWheelScrollView(
    itemExtent: 75,
    children: items,
    squeeze: 0.5,
  )

Output:

Flutter - ListWheelScrollView -  squeeze 0.5

 

  ListWheelScrollView(
    itemExtent: 75,
    children: items,
    squeeze: 2,
  )

Output:

Flutter - ListWheelScrollView -  squeeze 2

 

Handling Selected Item Changed

The selected item is the one currently in the center of the wheel. It's changed everytime the user scrolls up/down. To get the index of the selected item, you can pass a callback function as onSelectedItemChanged property. It's of type ValueChanged<int>, which accepts an integer parameter.

  onSelectedItemChanged: (index) => {
    print(index)
  },

 

Setting Controller

The controller used is typically a FixedExtentScrollController. It will be used if you don't set the controller property. Be careful if you change it to ScrollController as it will cause onSelectedItemChanged not working as it doesn't provide FixedExtentMetrics.

  ListWheelScrollView(
    itemExtent: 75,
    children: items,
  )

 

Full Code

Below is a full code example that uses ListWheelScrollView widget where the magnifier is turned on, FixedExtentScrollPhysics set as the physics, custom diameterRatio and squeeze, as well as an onSelectedItemChanged callback that updates a state variable whenever the selected item changes.

  import 'package:flutter/material.dart';
  
  void main() => runApp(MyApp());
  
  class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Flutter Tutorial by Woolha.com',
        home: ListWheelScrollViewApp(),
      );
    }
  }
  
  class ListWheelScrollViewApp extends StatefulWidget {
    @override
    _ListWheelScrollViewAppState createState() {
      return _ListWheelScrollViewAppState();
    }
  }
  
  class _ListWheelScrollViewAppState extends State<ListWheelScrollViewApp> {
  
    int _selectedItemIndex = 0;
  
    @override
    Widget build(BuildContext context) {
      List<Widget> items = [
        ListTile(
          leading: Icon(Icons.local_activity, size: 50),
          title: Text('Activity'),
          subtitle: Text('Description here'),
        ),
        ListTile(
          leading: Icon(Icons.local_airport, size: 50),
          title: Text('Airport'),
          subtitle: Text('Description here'),
        ),
        ListTile(
          leading: Icon(Icons.local_atm, size: 50),
          title: Text('ATM'),
          subtitle: Text('Description here'),
        ),
        ListTile(
          leading: Icon(Icons.local_bar, size: 50),
          title: Text('Bar'),
          subtitle: Text('Description here'),
        ),
        ListTile(
          leading: Icon(Icons.local_cafe, size: 50),
          title: Text('Cafe'),
          subtitle: Text('Description here'),
        ),
        ListTile(
          leading: Icon(Icons.local_car_wash, size: 50),
          title: Text('Car Wash'),
          subtitle: Text('Description here'),
        ),
        ListTile(
          leading: Icon(Icons.local_convenience_store, size: 50),
          title: Text('Convenience Store'),
          subtitle: Text('Description here'),
        ),
        ListTile(
          leading: Icon(Icons.local_dining, size: 50),
          title: Text('Dining'),
          subtitle: Text('Description here'),
        ),
        ListTile(
          leading: Icon(Icons.local_drink, size: 50),
          title: Text('Drink'),
          subtitle: Text('Description here'),
        ),
        ListTile(
          leading: Icon(Icons.local_florist, size: 50),
          title: Text('Florist'),
          subtitle: Text('Description here'),
        ),
        ListTile(
          leading: Icon(Icons.local_gas_station, size: 50),
          title: Text('Gas Station'),
          subtitle: Text('Description here'),
        ),
        ListTile(
          leading: Icon(Icons.local_grocery_store, size: 50),
          title: Text('Grocery Store'),
          subtitle: Text('Description here'),
        ),
      ];
  
      return Scaffold(
        appBar: AppBar(
          title: Text('Woolha.com Flutter Tutorial'),
        ),
        body: Center(
            child: ListWheelScrollView(
              itemExtent: 75,
              children: items,
              magnification: 1.5,
              useMagnifier: true,
              physics: FixedExtentScrollPhysics(),
              diameterRatio: 1.5,
              squeeze: 0.8,
              onSelectedItemChanged: (index) => {
                setState(() {
                _selectedItemIndex = index;
                })
              },
            )
        )
      );
    }
  }
  

Output:

Flutter - ListWheelScrollView -  Full Example

 

ListWheelScrollView Properties

Below is the list of available properties you can pass as the constructor parameters.

  • Key key: The widget key, used to control if it's should be replaced.
  • ScrollController controller: Used to control the current item, typically a FixedExtentScrollController, which will be used if none is provided. Changing it to ScrollController will cause onSelectedItemChanged not working as it doesn't provide FixedExtentMetrics.
  • ScrollPhysics physics: How the scroll view should respond to user input. By default it uses platform conventions.
  • diameterRatio: Ratio of the wheel diameter compared to the size of the viewport.
  • double perspective: Perspective of the cylindrical projection.
  • double offAxisFraction: How much the wheel is horizontally off-center, as a fraction of its width. Defaults to 0.0
  • bool useMagnifier: Whether to use magnifier for the current item. Defaults to false.
  • double magnification: The zoom-in rate if magnifier is used.
  • double itemExtent*: Size of each child in the main axis.
  • double squeeze: The angular compactness of the items in the wheel.
  • onSelectedItemChanged: Listener that will be called whenever the center item changes.
  • clipToSize: Whether to clip painted children to the inside of this viewport.
  • renderChildrenOutsideViewport: Whether to paint children inside the viewport only. Defaults to false.
  • List<Widget> (only for default constructor): List of Widgets to be set as the items of ListWheelScrollView..
  • ListWheelChildDelegate childDelegate* (only for .useDelegate): A delegate that helps lazily instantiating child.

*: Required.