flutter

Flutter ListView carga perezosa



(4)

¿Cómo puedo realizar la carga diferida de elementos para una vista de lista sin fin? Quiero cargar más elementos por red cuando el usuario se desplaza hasta el final de la vista de lista.


Gracias por el enfoque de Rémi Rousselet, pero no resuelve todo el problema. Especialmente cuando ListView se ha desplazado hacia abajo, todavía llama al scrollListener un par de veces. El mejor enfoque es combinar Notification Listener con el enfoque de Remi. Aquí está mi solución:

bool _handleScrollNotification(ScrollNotification notification) { if (notification is ScrollEndNotification) { if (_controller.position.extentAfter == 0) { loadMore(); } } return false; } @override Widget build(BuildContext context) { final Widget gridWithScrollNotification = NotificationListener< ScrollNotification>( onNotification: _handleScrollNotification, child: GridView.count( controller: _controller, padding: EdgeInsets.all(4.0), // Create a grid with 2 columns. If you change the scrollDirection to // horizontal, this would produce 2 rows. crossAxisCount: 2, crossAxisSpacing: 2.0, mainAxisSpacing: 2.0, // Generate 100 Widgets that display their index in the List children: _documents.map((doc) { return GridPhotoItem( doc: doc, ); }).toList())); return new Scaffold( key: _scaffoldKey, body: RefreshIndicator( onRefresh: _handleRefresh, child: gridWithScrollNotification)); }


La solución utiliza ScrollController y vi comentarios mencionados sobre la página.
Me gustaría compartir mi hallazgo sobre el paquete incrementally_loading_listview https://github.com/MaikuB/incrementally_loading_listview .
Como se indica en el paquete: Esto podría usarse para cargar datos paginados recibidos de solicitudes de API.

Básicamente, cuando ListView construye el último elemento y eso significa que el usuario se ha desplazado hacia abajo.
Espero que pueda ayudar a alguien que tenga preguntas similares.

Para fines de demostración, he cambiado el ejemplo para permitir que una página solo incluya un elemento y agregue un CircularProgressIndicator.

... bool _loadingMore; bool _hasMoreItems; int _maxItems = 30; int _numItemsPage = 1; ... _hasMoreItems = items.length < _maxItems; ... return IncrementallyLoadingListView( hasMore: () => _hasMoreItems, itemCount: () => items.length, loadMore: () async { // can shorten to "loadMore: _loadMoreItems" but this syntax is used to demonstrate that // functions with parameters can also be invoked if needed await _loadMoreItems(); }, onLoadMore: () { setState(() { _loadingMore = true; }); }, onLoadMoreFinished: () { setState(() { _loadingMore = false; }); }, loadMoreOffsetFromBottom: 0, itemBuilder: (context, index) { final item = items[index]; if ((_loadingMore ?? false) && index == items.length - 1) { return Column( children: <Widget>[ ItemCard(item: item), Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: <Widget>[ Row( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Container( width: 60.0, height: 60.0, color: Colors.grey, ), Padding( padding: const EdgeInsets.fromLTRB( 8.0, 0.0, 0.0, 0.0), child: Container( color: Colors.grey, child: Text( item.name, style: TextStyle( color: Colors.transparent), ), ), ) ], ), Padding( padding: const EdgeInsets.fromLTRB( 0.0, 8.0, 0.0, 0.0), child: Container( color: Colors.grey, child: Text( item.message, style: TextStyle( color: Colors.transparent), ), ), ) ], ), ), ), Center(child: CircularProgressIndicator()) ], ); } return ItemCard(item: item); }, );

ejemplo completo https://github.com/MaikuB/incrementally_loading_listview/blob/master/example/lib/main.dart

El paquete usa ListView index = last item y loadMoreOffsetFromBottom para detectar cuándo cargar más.

itemBuilder: (itemBuilderContext, index) { if (!_loadingMore && index == widget.itemCount() - widget.loadMoreOffsetFromBottom - 1 && widget.hasMore()) { _loadingMore = true; _loadingMoreSubject.add(true); }


Puedes escuchar un ScrollController .

ScrollController tiene información útil, como el desplazamiento de desplazamiento y una lista de ScrollPosition .

En su caso, la parte interesante está en controller.position que es la ScrollPosition actualmente visible. Que representa un segmento del desplazamiento.

ScrollPosition contiene información sobre su posición dentro del desplazamiento. Tales como extentBefore y extentAfter . O su tamaño, con extentInside .

Teniendo esto en cuenta, puede activar una llamada al servidor en función de la extentAfter que representa el espacio de desplazamiento restante disponible.

Aquí hay un ejemplo básico usando lo que dije.

class MyHome extends StatefulWidget { @override _MyHomeState createState() => new _MyHomeState(); } class _MyHomeState extends State<MyHome> { ScrollController controller; List<String> items = new List.generate(100, (index) => ''Hello $index''); @override void initState() { super.initState(); controller = new ScrollController()..addListener(_scrollListener); } @override void dispose() { controller.removeListener(_scrollListener); super.dispose(); } @override Widget build(BuildContext context) { return new Scaffold( body: new Scrollbar( child: new ListView.builder( controller: controller, itemBuilder: (context, index) { return new Text(items[index]); }, itemCount: items.length, ), ), ); } void _scrollListener() { print(controller.position.extentAfter); if (controller.position.extentAfter < 500) { setState(() { items.addAll(new List.generate(42, (index) => ''Inserted $index'')); }); } } }

Puede ver claramente que al llegar al final del desplazamiento, la barra de desplazamiento se gasta debido a que ha cargado más elementos.


Use el paquete lazy_load_scrollview: 1.0.0 que usa el mismo concepto detrás de escena que el mundo panda respondió aquí. El paquete hace que sea más fácil de implementar.