Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make RefreshIndicator work on whole screen (ListView)

While looking for solutions, I found some related discussions, but none of the solutions worked in my case.

I made a "RefreshableScrollFrame" widget in my app, which offers a generic frame for pages that should show a StatusBar (loading indicator, etc.) and which should provide a pull-down to refresh.

RefreshableScrollFrame

return Center(
      child: Column(
        children: [
          SizedBox(
            height: 4,
            child: const MyStatusbarWidget(),
          ),
          RefreshIndicator(
            onRefresh: onRefresh,
            child: SingleChildScrollView(
              physics: const AlwaysScrollableScrollPhysics(),
              child: child,
            ),
          )
        ],
      ),
    );

Child Option - ListView:

ListView.builder(
            itemCount: items.length,
            physics: const NeverScrollableScrollPhysics(),
            shrinkWrap: true,
            itemBuilder: (context, i) => Card(
              child: ListTile(
                 title: Text(items[i].name),
              ),
            ),
          );

Child Option - Column with the ListView and static items:

Column(
          children: [
            ListView.builder(
                itemCount: items.length,
                physics: const NeverScrollableScrollPhysics(),
                shrinkWrap: true,
                itemBuilder: (context, i) => Card(
                  child: ListTile(
                    title: Text(items[i].name),
                  ),
                ),
              ),
            ...someStaticWidgets,
          ],
        );

So far so good, everything works fine, except for one important detail. The RefreshIndicator is only triggered in areas, which are covered by the child widget. If the content is small and covers just the top of the page, the gesture is detected only in that area. If the content is just an empty ListView, I won't even be able to trigger a refresh.

I tried several suggested solutions in order to "expand" the child to the whole screen, but I always ran into exceptions, when trying to get rid of shrinkWrap, when adding Expanded(), and so on.

Any hints would be appreciated.

like image 722
redarflap Avatar asked Nov 18 '25 15:11

redarflap


1 Answers

I found a solution that seems to work for me, using a LayoutBuilder + ConstrainedBox to wrap the contents of the SingleChildScrollView. This way, the SingleChildScrollView will always span the whole remaining space.

return Column(
  children: [
    const SizedBox(
      height: 4,
      child: MyStatusbarWidget(),
    ),
    Expanded(
      child: LayoutBuilder(builder: (context, constraints) {
        return RefreshIndicator(
          onRefresh: onRefresh,
          child: SingleChildScrollView(
            physics: const AlwaysScrollableScrollPhysics(),
            child: ConstrainedBox(
                constraints: BoxConstraints(minHeight: constraints.maxHeight),
                child: child,
              ),
            ),
          ),
        );
      }),
    ),
  ],
);
like image 89
redarflap Avatar answered Nov 21 '25 07:11

redarflap



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!