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);
}