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:
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:
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:
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:
ListWheelScrollView(
itemExtent: 75,
children: items,
diameterRatio: 4,
)
Output:
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:
ListWheelScrollView(
itemExtent: 75,
children: items,
perspective: 0.01,
)
Output:
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:
ListWheelScrollView(
itemExtent: 75,
children: items,
squeeze: 2,
)
Output:
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:
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 aFixedExtentScrollController
, which will be used if none is provided. Changing it toScrollController
will causeonSelectedItemChanged
not working as it doesn't provideFixedExtentMetrics
.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.0bool useMagnifier
: Whether to use magnifier for the current item. Defaults tofalse
.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 tofalse
.List<Widget>
(only for default constructor): List ofWidget
s to be set as the items ofListWheelScrollView
..ListWheelChildDelegate childDelegate
* (only for.useDelegate
): A delegate that helps lazily instantiating child.
*: Required.