25

I'm trying to add RefreshIndicator to CustomScrollView. Is it possible to do? Here is the code sample:

CustomScrollView(
  slivers: <Widget>[
    SliverAppBar(
      title: Text('POSTAPP'),
      centerTitle: true,
      floating: true,
      pinned: false,
    ),
    SliverList(
        delegate: SliverChildBuilderDelegate(
          _createPostItem,
          childCount: postRepo.posts.length,
      ),
    ),
  ],
);
Muhammed Aydogan
  • 570
  • 1
  • 4
  • 22
Robert Apikyan
  • 1,972
  • 1
  • 20
  • 23

6 Answers6

27

Only like this:

RefreshIndicator(child: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
            title: Text('POSTAPP'),
            centerTitle: true,
            floating: true,
            pinned: false,
          ),
          SliverList(
              delegate: SliverChildBuilderDelegate(_createPostItem,
                  childCount: postRepo.posts.length))
        ],
      ), onRefresh: () => Future.value(true));

SliverList isn't scrollable, so you can't wrap it in RefreshIndicator

Andrii Turkovskyi
  • 27,554
  • 16
  • 95
  • 105
26

If you're not necessarily restricted to use CustomScrollView, you could achieve what you ask for with NestedScrollView:

@override
Widget build(BuildContext context) {
  return NestedScrollView(
    headerSliverBuilder: (context, innerBoxScrolled) => [
          SliverAppBar(
            title: Text('POSTAPP'),
            centerTitle: true,
            floating: true,
            pinned: false,
          ),
        ],
    body: RefreshIndicator(
      onRefresh: _loadMorePosts,
      child: ListView.builder(
        itemBuilder: _createPostItem,
        itemCount: _postsCount,
      ),
    ),
  );
}
tomwyr
  • 1,351
  • 2
  • 16
  • 21
6

For anyone circling back to this question, I have found a solution that works using the package pull_to_refresh.

final RefreshController _refreshController = RefreshController();

Future<void> _onRefresh() async {
  await Future<void>.delayed(const Duration(milliseconds: 1000));
  _refreshController.refreshCompleted();
}

@override
Widget build(BuildContext context) {
  return CustomScrollView(
    slivers: [
      SliverAppBar(
        title: Text('Title'),
        centerTitle: true,
        floating: true,
        pinned: false,
      ),
      SliverFillRemaining(
        child: SmartRefresher(
          controller: _refreshController,
          onRefresh: _onRefresh,
          // ListView, CustomScrollView, etc. here
          child: SingleChildScrollView(
            child: Column(
              children: [
                Container(
                  height: 200,
                  color: Colors.red,
                ),
                Container(
                  height: 200,
                  color: Colors.blue,
                ),
                Container(
                  height: 200,
                  color: Colors.yellow,
                ),
              ],
            ),
          ),
        ),
      ),
    ],
  );
}
Sam Doggett
  • 248
  • 5
  • 8
  • thank you for your solution. There is just on issue -> You have to use a singlechildscrollview, it doesn't work with a listview, which would be desirable for performance improvements – Gonçalo Gaspar May 19 '22 at 13:41
  • @GonçaloGaspar I haven't worked with `Flutter` for a while, but when I originally tested this, it worked with `ListView`. Thanks for update. – Sam Doggett May 19 '22 at 13:45
4

You can use CupertinoSliverRefreshControl inside the slivers list, you need to apply physics: BouncingScrollPhysics() to your CustomScrollView.

example:

    return Scaffold(
      body: CustomScrollView(
        physics: BouncingScrollPhysics(),
        slivers: [
          SliverAppBar(),
          CupertinoSliverRefreshControl(
            onRefresh: () => refreshList(),
          ),
          SliverList(delegate: delegate),
        ],
      ),
    );

in the example above, it will show RefreshIndicator in between the AppBar and the ListView.

Mohammad Ersan
  • 12,304
  • 8
  • 54
  • 77
3
 This is the answer

    RefreshIndicator(
    onRefresh: () async {
      return await Future.delayed(const Duration(seconds: 2));
    },
    edgeOffset: 100,   // ANY NUMBER SO THE REFRESH APPEARS ANYWHERE YOU WANT.
    child: CustomScrollView(
        slivers: <Widget>[
            SliverAppBar(
                title: Text('POSTAPP'),
                centerTitle: true,
                floating: true,
                pinned: false,
            ),
            SliverList(
                delegate: SliverChildBuilderDelegate(
                    _createPostItem,
                    childCount: postRepo.posts.length,
                ),
            ),
        ],
     ),
);
Kibrom Hs
  • 39
  • 3
1

I managed to do by create a custom widget that folk from flutter/lib/src/material/refresh_indicator.dart. and modify the following code (p.s i only added the variable initialDisplacement)

double initialDisplacement = 100.0;
return Stack(
  children: <Widget>[
    child,
    if (_mode != null) Positioned(
      top: _isIndicatorAtTop ? initialDisplacement : null,
      bottom: !_isIndicatorAtTop ? 0.0 : null,
      left: 0.0,
      right: 0.0,
      child: SizeTransition(
        axisAlignment: _isIndicatorAtTop ? 1.0 : -1.0,
        sizeFactor: _positionFactor, // this is what brings it down
        child: Container(
          padding: _isIndicatorAtTop
            ? EdgeInsets.only(top: widget.displacement)
            : EdgeInsets.only(bottom: widget.displacement),
          alignment: _isIndicatorAtTop
            ? Alignment.topCenter
            : Alignment.bottomCenter,
          child: ScaleTransition(
            scale: _scaleFactor,
            child: AnimatedBuilder(
              animation: _positionController,
              builder: (BuildContext context, Widget child) {
                return RefreshProgressIndicator(
                  semanticsLabel: widget.semanticsLabel ?? MaterialLocalizations.of(context).refreshIndicatorSemanticLabel,
                  semanticsValue: widget.semanticsValue,
                  value: showIndeterminateIndicator ? null : _value.value,
                  valueColor: _valueColor,
                  backgroundColor: widget.backgroundColor,
                  strokeWidth: widget.strokeWidth,
                );
              },
            ),
          ),
        ),
      ),
    ),
  ],
);
ming huang
  • 11
  • 1