'Flutter: How to use RefreshIndicator with SliverAppBar

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,
      ),
    ),
  ],
);


Solution 1:[1]

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,
      ),
    ),
  );
}

Solution 2:[2]

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

Solution 3:[3]

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,
                ),
              ],
            ),
          ),
        ),
      ),
    ],
  );
}

Solution 4:[4]

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,
                );
              },
            ),
          ),
        ),
      ),
    ),
  ],
);

Solution 5:[5]

 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,
                ),
            ),
        ],
     ),
);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 tomwyr
Solution 2 Andrey Turkovsky
Solution 3 Sam Doggett
Solution 4 ming huang
Solution 5 Kibrom Hs