web-dev-qa-db-fra.com

Défilement par programme jusqu'à la fin d'un ListView

J'ai une variable ListView où le nombre d'éléments peut changer dynamiquement. Chaque fois qu'un nouvel élément est ajouté à la fin de la liste, j'aimerais faire défiler par programme la ListView jusqu'à la fin. (par exemple, quelque chose comme une liste de messages de discussion où de nouveaux messages peuvent être ajoutés à la fin)

Je suppose que je devrais créer une variable ScrollController dans mon objet State et la transmettre manuellement au constructeur ListView afin de pouvoir ultérieurement appeler la méthode animateTo()/jumpTo() sur le contrôleur. Cependant, étant donné que je ne parviens pas à déterminer facilement le décalage de défilement maximum, il semble impossible de réaliser simplement une opération de type scrollToEnd() (alors que je peux facilement passer 0.0 pour le faire défiler jusqu'à la position initiale).

Y a-t-il un moyen facile d'y parvenir?

Utiliser reverse: true n’est pas une solution parfaite pour moi, car j’aimerais que les éléments soient alignés en haut lorsque le nombre d’éléments pouvant être insérés dans la fenêtre ListView est petit.

23
yyoon

Si vous utilisez une ListView rétrécie avec reverse: true, le faire défiler jusqu'à 0,0 fera ce que vous voulez.

import 'Dart:collection';

import 'package:flutter/material.Dart';

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Example',
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<Widget> _messages = <Widget>[new Text('hello'), new Text('world')];
  ScrollController _scrollController = new ScrollController();

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Center(
        child: new Container(
          decoration: new BoxDecoration(backgroundColor: Colors.blueGrey.shade100),
          width: 100.0,
          height: 100.0,
          child: new Column(
            children: [
              new Flexible(
                child: new ListView(
                  controller: _scrollController,
                  reverse: true,
                  shrinkWrap: true,
                  children: new UnmodifiableListView(_messages),
                ),
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        child: new Icon(Icons.add),
        onPressed: () {
          setState(() {
            _messages.insert(0, new Text("message ${_messages.length}"));
          });
          _scrollController.animateTo(
            0.0,
            curve: Curves.easeOut,
            duration: const Duration(milliseconds: 300),
          );
        }
      ),
    );
  }
}
28
Collin Jackson

Utilisez la méthode ScrollController.jumpTo() ou ScrollController.animateTo() pour y parvenir. 

Voici l'extrait de code (après 1 seconde, la ListView défilera vers le bas)

class _HomePageState extends State<HomePage> {
  ScrollController _controller = ScrollController();

  @override
  Widget build(BuildContext context) {
    Timer(Duration(milliseconds: 1000), () => _controller.jumpTo(_controller.position.maxScrollExtent));

    return ListView.builder(
      controller: _controller,
        itemCount: 50,
        itemBuilder: (context, index) => ListTile(title: Text("ListTile"),));
  }
}
8
CopsOnRoad
Timer(Duration(milliseconds: 300),(){
          _scrollController.animateTo(_scrollController.position.maxScrollExtent,curve: Curves.ease,
        duration: const Duration(milliseconds: 300), );
      });

Ce code fonctionne sans reverse:true, donc la liste ressemble à un nouvel élément du dernier index.

0
Benjith Kizhisseri

J'ai tellement de difficulté à utiliser le contrôleur de défilement pour aller au bas de la liste que j'utilise une autre approche.

Au lieu de créer un événement pour envoyer la liste au bas de la liste, je change de logique pour utiliser une liste inversée.

Donc, chaque fois que j'ai un nouvel article, je le fais simplement à insérer en haut de la liste. 

// add new message at the begin of the list 
list.insert(0, message);
// ...

// pull items from the database
list = await bean.getAllReversed(); // basically a method that applies a descendent order

// I remove the scroll controller
new Flexible(
  child: new ListView.builder(
    reverse: true, 
    key: new Key(model.count().toString()),
    itemCount: model.count(),
    itemBuilder: (context, i) => ChatItem.displayMessage(model.getItem(i))
  ),
),
0
winter