web-dev-qa-db-fra.com

Flutter: ListView ne défile pas, ne rebondit pas

J'ai l'exemple suivant (testé sur un iPhone X, iOS 11):

import 'package:flutter/material.Dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return new ListView(
      children: <Widget>[
        new Container(
          height: 40.0,
          color: Colors.blue,
        ),
        new Container(
          height: 40.0,
          color: Colors.red,
        ),
        new Container(
          height: 40.0,
          color: Colors.green,
        ),
      ]
    );
  }

}

Dans ce cas, le ListView agit comme prévu. Je peux faire défiler au-delà de la fenêtre et le ListView rebondit à nouveau (comportement typique d'iOS). Mais lorsque j'ajoute un ScrollController pour suivre le décalage, le comportement du défilement change:

import 'package:flutter/material.Dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  ScrollController _controller = new ScrollController();

  @override
  Widget build(BuildContext context) {
    return new ListView(
      controller: _controller,
      children: <Widget>[
        new Container(
          height: 40.0,
          color: Colors.blue,
        ),
        new Container(
          height: 40.0,
          color: Colors.red,
        ),
        new Container(
          height: 40.0,
          color: Colors.green,
        ),
      ]
    );
  }
}

Dans ce cas, le défilement n'est plus possible. Pourquoi est-ce que lorsque j'ajoute un ScrollController, le défilement n'est plus possible? L'ajout de physics: new BouncingScrollPhysics(), à ListView n'aide pas non plus.

Merci pour toute aide :)

12
Renato Stauffer

Pour que le défilement soit toujours activé sur un ListView, vous pouvez encapsuler la phisique de défilement d'origine que vous souhaitez avec la classe AlwaysScrollableScrollPhysics. Plus de détails ici . Si vous le souhaitez, vous pouvez spécifier un parent ou vous fier à la valeur par défaut.

Voici votre exemple avec l'option ajoutée:

import 'package:flutter/material.Dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  ScrollController _controller = new ScrollController();

  @override
  Widget build(BuildContext context) {
    return new ListView(
        physics: const AlwaysScrollableScrollPhysics(), // new
        controller: _controller,
        children: <Widget>[
          new Container(
            height: 40.0,
            color: Colors.blue,
          ),
          new Container(
            height: 40.0,
            color: Colors.red,
          ),
          new Container(
            height: 40.0,
            color: Colors.green,
          ),
        ]
    );
  }
}
11
Fabio Veronese

Je pense que cette solution est meilleure sans CustomScrollView. Utilisez simplement NotificationListener pour envelopper le ListView.

  Widget noti = new NotificationListener(
    child:listView,
    onNotification: (ScrollNotification note){
      print(note.metrics.pixels.toInt());
    },
  );

Je l'ai testé, Bounce est efficace

0
ken

Ajoutez simplement AlwaysScrollableScrollPhysics

ListView(
        physics: const AlwaysScrollableScrollPhysics(),
        children :  [...]
}
0
Sanjayrajsinh

J'ai trouvé une solution pour suivre l'offset avec des listes qui ont une hauteur de contenu plus petite que la fenêtre. Utilisez un NotificationListener avec un CustomScrollView dans la méthode build() comme ceci:

import 'package:flutter/material.Dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  ScrollController _controller = new ScrollController();

  @override
  Widget build(BuildContext context) {
    return new NotificationListener(
      onNotification: _handleScrollPosition,
        child: new CustomScrollView(
            slivers: [
              new SliverList(
                  delegate: new SliverChildListDelegate([
                    new Container(
                      height: 40.0,
                      color: Colors.blue,
                    ),
                    new Container(
                      height: 40.0,
                      color: Colors.red,
                    ),
                    new Container(
                      height: 40.0,
                      color: Colors.green,
                    ),
                  ])
              )
            ]
        )
    );
  }

  bool _handleScrollPosition(ScrollNotification notification) {
    print(notification.metrics.pixels);
    return true;
  }
}

Tant qu'il n'y aura pas de solution avec une solution ScrollController uniquement (ou une solution "meilleure" (plus élégante)), j'accepterai cela comme la réponse.

0
Renato Stauffer