Reputation: 335
I have a horizontal scrolling ListView with an undetermined number of items inside.
How can I programatically scroll a specific item into the center of my screen?
Context: On the previous screen I have multiple items, and when I click on one, I need it to navigate to this screen and scroll the item selected on the previous screen to the center of the new screen.
My trouble is really just with the scrolling part.
Thanks in advance.
ListView:
final listViewController = ScrollController();
@override
Widget build(BuildContext context) {
return ListView.separated(
scrollDirection: Axis.horizontal,
physics: ClampingScrollPhysics(),
controller: listViewController,
padding: EdgeInsets.zero,
itemCount: testArray.length,
itemBuilder: (ctx, i) => Item(
testArray[i],
testArray[i] == 'item5' ? true : false,
() => {
// testing code for the scroll functionality
listViewController.animateTo(
i + MediaQuery.of(context).size.width / 2,
duration: Duration(seconds: 1),
curve: Curves.easeIn),
},
),
separatorBuilder: (ctx, i) => Padding(
padding: EdgeInsets.symmetric(horizontal: 6),
),
);
}
}
Item Widget:
class Item extends StatelessWidget {
final String itemName;
final bool selectedItem;
final VoidCallback navigationHandler;
Item(
this.itemName, this.selectedItem, this.navigationHandler);
@override
Widget build(BuildContext context) {
return Container(
height: double.infinity,
child: TextButton(
onPressed: navigationHandler,
child: Text(
itemName,
style: selectedItem
? Theme.of(context).textTheme.headline6?.copyWith(
fontSize: 22,
)
: Theme.of(context).textTheme.headline6?.copyWith(
color: Color(0xff707070),
),
),
),
);
}
}
Upvotes: 4
Views: 9275
Reputation: 3456
As mentioned in this StackOverflow thread the easiest way would be to set a GlobalKey
for each list item and use ensureVisible
to scroll, with alignment: 0.5
that item will be center at the scroll
Scrollable.ensureVisible(itemKey.currentContext, alignment: 0.5);
If you need to scroll after build then:
WidgetsBinding.instance.addPostFrameCallback((_) =>
Scrollable.ensureVisible(itemKey.currentContext, alignment: 0.5))
Upvotes: 2
Reputation: 5733
The best solution to this issue that I've found is to use the package scrollable_positioned_list which can scroll to items based on its index.
If you knew the extent of its children you could have used a FixedExtentScrollController
as the controller of your lisview and would not have needed to rely on a external dependency.
The gist of using the package is just to create a controller , this time an
ItemScrollController
and just replace your ListView.separated
to ScrollablePositionedList.separated
final ItemScrollController itemScrollController = ItemScrollController();
ScrollablePositionedList.separated(
itemScrollController: itemScrollController,
...
);
One then can scroll to a particular item with:
itemScrollController.scrollTo(
index: 150,
duration: Duration(seconds: 1),
curve: Curves.easeIn);
A complete example would be as follows
final testArray = [for (var i = 0; i < 100; i++) 'item$i'];
class _MyAppState extends State<MyApp> {
final itemScrollController = ItemScrollController();
@override
Widget build(BuildContext context) {
return MaterialApp(
title: MyApp._title,
home: Scaffold(
body: ScrollablePositionedList.separated(
itemCount: testArray.length,
scrollDirection: Axis.horizontal,
physics: ClampingScrollPhysics(),
padding: EdgeInsets.zero,
itemBuilder: (context, i) => Item(
testArray[i],
testArray[i] == 'item5' ? true : false,
() => {
// testing code for the scroll functionality
itemScrollController.scrollTo(
index: (i + 5) % testArray.length,
duration: Duration(seconds: 1),
curve: Curves.easeIn,
alignment: 0.5),/// Needed to center the item when scrolling
},
),
itemScrollController: itemScrollController,
separatorBuilder: (ctx, i) => Padding(padding: EdgeInsets.symmetric(horizontal: 6)),
)));
}
}
By the way be accustomed at whenever you're working with controllers create them in the State of a Stateful widget, so they are only created once, and dispose them if necessary. I'ts not the case with ItemScrollController
but ScrollController
would have needed to be disposed .
Upvotes: 4