问题
How can I scroll to a special widget in a ListView
?
For instance I want to scroll automatically to some Container
in the ListView
if I press a specific button.
ListView(children: <Widget>[
Container(...),
Container(...), #scroll for example to this container
Container(...)
]);
回答1:
By far, the easiest solution is to use Scrollable.ensureVisible(context)
. As it does everything for you and work with any widget size. Fetching the context using GlobalKey
.
The problem is that ListView
won't render non-visible items. Meaning that your target most likely will not be built at all. Which means your target will have no context
; preventing you from using that method without some more work.
In the end, the easiest solution will be to replace your ListView
by a SingleChilScrollView
and wrap your children into a Column
. Example :
class ScrollView extends StatelessWidget {
final dataKey = new GlobalKey();
@override
Widget build(BuildContext context) {
return new Scaffold(
primary: true,
appBar: new AppBar(
title: const Text('Home'),
),
body: new SingleChildScrollView(
child: new Column(
children: <Widget>[
new SizedBox(height: 160.0, width: double.infinity, child: new Card()),
new SizedBox(height: 160.0, width: double.infinity, child: new Card()),
new SizedBox(height: 160.0, width: double.infinity, child: new Card()),
// destination
new Card(
key: dataKey,
child: new Text("data\n\n\n\n\n\ndata"),
)
],
),
),
bottomNavigationBar: new RaisedButton(
onPressed: () => Scrollable.ensureVisible(dataKey.currentContext),
child: new Text("Scroll to data"),
),
);
}
}
NOTE : While this allows to scroll to the desired item easily, consider this method only for small predefined lists. As for bigger lists you'll get performance problems.
But it's possible to make Scrollable.ensureVisible
work with ListView
; although it will require more work.
回答2:
Unfortunately, ListView has no built-in approach to a scrollToIndex() function. You’ll have to develop your own way to measure to that element’s offset for animateTo()
or jumpTo()
, or you can search through these suggested solutions/plugins or from other posts like flutter ListView scroll to index not available
(the general scrollToIndex issue is discussed at flutter/issues/12319 since 2017, but still with no current plans)
But there is a different kind of ListView that does support scrollToIndex:
- ScrollablePositionedList
- scrollable_positioned_list
- dependency: flutter_widgets
You set it up exactly like ListView and works the same, except you now have access to a ItemScrollController that does:
jumpTo({index, alignment})
scrollTo({index, alignment, duration, curve})
Simplified example:
ItemScrollController _scrollController = ItemScrollController();
ScrollablePositionedList.builder(
itemScrollController: _scrollController,
itemCount: _myList.length,
itemBuilder: (context, index) {
return _myList[index];
},
)
_scrollController.scrollTo(index: 150, duration: Duration(seconds: 1));
(note that this library is developed by Google but not by the core Flutter team.)
回答3:
You can just specify a ScrollController to your listview and call the animateTo method on button click.
A mininmal example to demonstrate animateTo
usage :
class Example extends StatefulWidget {
@override
_ExampleState createState() => new _ExampleState();
}
class _ExampleState extends State<Example> {
ScrollController _controller = new ScrollController();
void _goToElement(int index){
_controller.animateTo((100.0 * index), // 100 is the height of container and index of 6th element is 5
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut);
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(),
body: new Column(
children: <Widget>[
new Expanded(
child: new ListView(
controller: _controller,
children: Colors.primaries.map((Color c) {
return new Container(
alignment: Alignment.center,
height: 100.0,
color: c,
child: new Text((Colors.primaries.indexOf(c)+1).toString()),
);
}).toList(),
),
),
new FlatButton(
// on press animate to 6 th element
onPressed: () => _goToElement(6),
child: new Text("Scroll to 6th element"),
),
],
),
);
}
}
回答4:
You can use the controller.jumpTo(100) after the loading finish
回答5:
Screenshot:
For ListView
, you can try this, the following code will animate to 10th index.
class HomePage extends StatelessWidget {
final _controller = ScrollController();
final _height = 100.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
onPressed: () => _animateToIndex(10),
child: Icon(Icons.arrow_downward),
),
body: ListView.builder(
controller: _controller,
itemCount: 100,
itemBuilder: (_, i) => Container(
height: _height,
child: Card(child: Center(child: Text("Item $i"))),
),
),
);
}
_animateToIndex(i) => _controller.animateTo(_height * i, duration: Duration(seconds: 2), curve: Curves.fastOutSlowIn);
}
来源:https://stackoverflow.com/questions/49153087/flutter-scrolling-to-a-widget-in-listview