Flutter: прокрутка до виджета в ListView
Как прокрутить до специального виджета в ListView
?
Например, я хочу автоматически перейти к какому-нибудь Container
в ListView
, если я нажму определенную кнопку.
ListView(children: <Widget>[
Container(...),
Container(...), #scroll for example to this container
Container(...)
]);
Ответы
Ответ 1
Безусловно, самым простым решением является использование Scrollable.ensureVisible(context)
. Так как он делает все для вас и работает с любым размером виджета. Извлечение контекста с помощью GlobalKey
.
Проблема в том, что ListView
не будет отображать невидимые элементы. Это означает, что ваша цель, скорее всего, не будет построена вообще. Это означает, что у вашей цели не будет context
; мешает вам использовать этот метод без дополнительной работы.
В конце концов, самым простым решением будет заменить ваш ListView
на SingleChilScrollView
и превратить ваших детей в Column
. Пример:
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"),
),
);
}
}
ПРИМЕЧАНИЕ: хотя это позволяет легко переходить к нужному элементу, рассмотрите этот метод только для небольших предварительно определенных списков. Что касается больших списков, вы получите проблемы с производительностью.
Но можно заставить Scrollable.ensureVisible
работать с ListView
; хотя это потребует больше работы.
Ответ 2
Вы можете просто указать ScrollController
для своего списка и вызвать метод animateTo
по нажатию кнопки.
Минимальный пример, демонстрирующий использование animateTo
:
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"),
),
],
),
);
}
}
Ответ 3
Вы можете использовать controller.jumpTo(100) после окончания загрузки
Ответ 4
Снимок экрана:
![enter image description here]()
Для ListView
вы можете попробовать это, следующий код будет анимирован до 10-го индекса.
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);
}