EdsonBueno / infinite_scroll_pagination

Flutter package to help you lazily load and display pages of items as the user scrolls down your screen.
https://pub.dev/packages/infinite_scroll_pagination
MIT License
605 stars 201 forks source link

Issue while Loading (firstPageProgressIndicatorBuilder) using ListView #310

Closed nandakista closed 5 months ago

nandakista commented 5 months ago

Hi I tried to using Shimmer loading when load the page at the first time. So I create the loading view with 2 different ways:

  1. I put the Shimmer component using ListView, but the error appears.
  2. But if I used SingleChildScrollView --> Column --> for loop my shimmer item its work perfectly.

Can anyone help or explain about this issue ?

Here the code for reproduce:


import 'package:flutter/material.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final PagingController<int, int> pagingController =
      PagingController(firstPageKey: 1);

  @override
  void initState() {
    pagingController.addPageRequestListener((page) => _fetchPage(page));
    super.initState();
  }

  Future<void> _fetchPage(int page) async {
    try {
      await Future.delayed(const Duration(seconds: 1));
      final data = List.generate(10, (index) => Random().nextInt(100));
      pagingController.appendPage(data, page++);
    } catch (error) {
      pagingController.error = error;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sample 1'),
      ),
      body: RefreshIndicator(
        onRefresh: () async => pagingController.refresh(),
        child: PagedListView<int, int>(
          pagingController: pagingController,
          builderDelegate: PagedChildBuilderDelegate<int>(
            firstPageProgressIndicatorBuilder: (context) {
              /// It's not work
              return ListView.builder(
                itemCount: 20,
                itemBuilder: (context, index) {
                  return Text('[Sample] Loading shimmer item-$index');
                },
              );

              // It is work perfectly
              // return SingleChildScrollView(
              //   child: Column(
              //     children: [
              //       for (var i = 0; i < 20; i++) Text('[Sample] Loading shimmer item-$i'),
              //     ],
              //   ),
              // );
            },
            itemBuilder: (context, item, index) => Container(
              height: 120,
              margin: const EdgeInsets.all(12),
              color: Colors.amber,
              child: Text('Index $index'),
            ),
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    pagingController.dispose();
    super.dispose();
  }
}

This the error

Screenshot 2024-01-19 at 18 31 18
caiombueno commented 5 months ago

hi @nandakista

I think this happens because a ListView doesn't provide its complete dimensions all at once. Instead, it only renders the elements that are currently visible.

On the other hand, when using SingleChildScrollView with Column, the entire item list is mounted and painted, even if only a few items are visible. The widget has defined dimensions, and that appears to be what the parameter wants.

If you want to know more about it, take a look at this StackOverflow discussion.

nandakista commented 5 months ago

Hi @caiombueno Thank you for the response and the explanation.

I tried building using a ListView without a builder like the code below

/// Still not work
...
firstPageProgressIndicatorBuilder: (context) {
    return ListView(
      children: [
        for (var i = 0; i < 20; i++)
          Text('[Sample] Loading shimmer item-$i'),
      ],
    );
}
...

But having the same error, isn't it true that if use a ListView without a builder it will also build all the widgets not just the visible ones (constructor ListView #1) ?

So for this problem, am I required to use SingleChildScrollView for the shimmer list? That's a kinda weird

caiombueno commented 5 months ago

As far as I understand, the default ListView doesn't build and render the whole list at once; instead, it creates the entire list in memory at once. I took this information from Ala Amarneh's answer in this discussion.

If you really want to use the ListView, you could wrap it in a SizedBox.

mllrr96 commented 5 months ago

Had same issue, had to use column like that to mimic ListView.separated Column( children: List.generate( 20, (index) => index.isEven ? shimmerWidget : const SizedBox(height: 8.0)))

EArminjon commented 3 months ago

Elegant solution : wrap your listview inside firstPageProgressIndicatorBuilder with a Sizedbox.shrink.